/* * Driver for rockchip rk1000 core controller * Copyright (C) 2017 Fuzhou Rockchip Electronics Co.Ltd * Author: * Algea Cao * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2 of the License, or (at your * option) any later version. */ #include #include #include #include #include #include #include #include #include #include #define CTRL_ADC 0x00 #define ADC_OFF 0x88 #define CTRL_CODEC 0x01 #define CODEC_OFF 0x0d #define CTRL_I2C 0x02 #define I2C_TIMEOUT_PERIOD 0x22 #define CTRL_TVE 0x03 #define TVE_OFF 0x00 struct rk1000 { struct i2c_client *client; struct device *dev; struct regmap *regmap; struct gpio_desc *io_reset; struct clk *mclk; }; static const struct mfd_cell rk1000_devs[] = { { .name = "rk1000-tve", .of_compatible = "rockchip,rk1000-tve", }, }; static const struct regmap_range rk1000_ctl_volatile_ranges[] = { { .range_min = 0x00, .range_max = 0x05 }, }; static const struct regmap_access_table rk1000_ctl_reg_table = { .yes_ranges = rk1000_ctl_volatile_ranges, .n_yes_ranges = ARRAY_SIZE(rk1000_ctl_volatile_ranges), }; struct regmap_config rk1000_regmap_config = { .reg_bits = 8, .val_bits = 8, .volatile_table = &rk1000_ctl_reg_table, }; static int rk1000_probe(struct i2c_client *client, const struct i2c_device_id *id) { bool uboot_logo; int ret, val = 0; struct rk1000 *rk1000 = devm_kzalloc(&client->dev, sizeof(*rk1000), GFP_KERNEL); if (!rk1000) return -ENOMEM; rk1000->client = client; rk1000->dev = &client->dev; rk1000->regmap = devm_regmap_init_i2c(client, &rk1000_regmap_config); if (IS_ERR(rk1000->regmap)) return PTR_ERR(rk1000->regmap); ret = regmap_read(rk1000->regmap, CTRL_TVE, &val); /* * If rk1000's registers can be read and rk1000 cvbs output is * enabled, we think uboot logo is on. */ if (!ret && val & BIT(6)) uboot_logo = true; else uboot_logo = false; if (!uboot_logo) { /********Get reset pin***********/ rk1000->io_reset = devm_gpiod_get_optional(rk1000->dev, "reset", GPIOD_OUT_LOW); if (IS_ERR(rk1000->io_reset)) { dev_err(rk1000->dev, "can't get rk1000 reset gpio\n"); return PTR_ERR(rk1000->io_reset); } gpiod_set_value(rk1000->io_reset, 0); usleep_range(500, 1000); gpiod_set_value(rk1000->io_reset, 1); usleep_range(500, 1000); gpiod_set_value(rk1000->io_reset, 0); } rk1000->mclk = devm_clk_get(rk1000->dev, "mclk"); if (IS_ERR(rk1000->mclk)) { dev_err(rk1000->dev, "get mclk err\n"); return PTR_ERR(rk1000->mclk); } ret = clk_prepare_enable(rk1000->mclk); if (ret < 0) { dev_err(rk1000->dev, "prepare mclk err\n"); goto clk_err; } regmap_write(rk1000->regmap, CTRL_ADC, ADC_OFF); regmap_write(rk1000->regmap, CTRL_CODEC, CODEC_OFF); regmap_write(rk1000->regmap, CTRL_I2C, I2C_TIMEOUT_PERIOD); if (!uboot_logo) regmap_write(rk1000->regmap, CTRL_TVE, TVE_OFF); ret = mfd_add_devices(rk1000->dev, -1, rk1000_devs, ARRAY_SIZE(rk1000_devs), NULL, 0, NULL); if (ret) { dev_err(rk1000->dev, "rk1000 mfd_add_devices failed\n"); goto mfd_add_err; } i2c_set_clientdata(client, rk1000); dev_dbg(rk1000->dev, "rk1000 probe ok!\n"); return 0; mfd_add_err: mfd_remove_devices(rk1000->dev); clk_err: clk_disable_unprepare(rk1000->mclk); return ret; } static int rk1000_remove(struct i2c_client *client) { struct rk1000 *rk1000 = i2c_get_clientdata(client); clk_disable_unprepare(rk1000->mclk); mfd_remove_devices(rk1000->dev); return 0; } static const struct i2c_device_id rk1000_id[] = { { "rk1000-ctl", 0 }, { } }; MODULE_DEVICE_TABLE(i2c, rk1000_id); static const struct of_device_id rk1000_ctl_dt_ids[] = { { .compatible = "rockchip,rk1000-ctl" }, { } }; MODULE_DEVICE_TABLE(of, rk1000_ctl_dt_ids); static struct i2c_driver rk1000_driver = { .driver = { .name = "rk1000-ctl", }, .probe = rk1000_probe, .remove = rk1000_remove, .id_table = rk1000_id, }; module_i2c_driver(rk1000_driver); MODULE_DESCRIPTION("RK1000 core control driver"); MODULE_AUTHOR("Algea Cao "); MODULE_LICENSE("GPL");