diff --git a/drivers/mfd/display-serdes/core.h b/drivers/mfd/display-serdes/core.h index 941f448adcac..e74f4436beef 100644 --- a/drivers/mfd/display-serdes/core.h +++ b/drivers/mfd/display-serdes/core.h @@ -28,6 +28,7 @@ #include #include #include +#include #include #include @@ -351,6 +352,7 @@ struct serdes { struct mutex io_lock; struct mutex irq_lock; struct mutex wq_lock; + struct mutex reg_check_lock; struct device *dev; enum serdes_type type; struct regmap *regmap; @@ -377,6 +379,10 @@ struct serdes { bool route_enable; bool use_delay_work; + struct kthread_worker *kworker; + struct kthread_delayed_work reg_check_work; + bool use_reg_check_work; + bool split_mode_enable; unsigned int reg_hw; unsigned int reg_use; @@ -391,6 +397,7 @@ struct serdes { struct pinctrl_state *pins_sleep; struct serdes_init_seq *serdes_init_seq; + struct serdes_init_seq *serdes_backup_seq; struct serdes_bridge *serdes_bridge; struct serdes_bridge_split *serdes_bridge_split; struct serdes_panel *serdes_panel; diff --git a/drivers/mfd/display-serdes/serdes-i2c.c b/drivers/mfd/display-serdes/serdes-i2c.c index 951d40d3db50..b02a64b04291 100644 --- a/drivers/mfd/display-serdes/serdes-i2c.c +++ b/drivers/mfd/display-serdes/serdes-i2c.c @@ -14,7 +14,7 @@ static struct serdes *g_serdes_ser_split[MAX_NUM_SERDES_SPLIT]; int serdes_i2c_set_sequence(struct serdes *serdes) { struct device *dev = serdes->dev; - int i, ret = 0; + int i, num = 0, ret = 0; unsigned int def = 0; for (i = 0; i < serdes->serdes_init_seq->reg_seq_cnt; i++) { @@ -30,9 +30,9 @@ int serdes_i2c_set_sequence(struct serdes *serdes) serdes->serdes_init_seq->reg_sequence[i].def); if (ret < 0) { - dev_err(serdes->dev, - "failed to write register %04x, ret %d, write again now\n", - serdes->serdes_init_seq->reg_sequence[i].reg, ret); + SERDES_DBG_MFD("%s failed to write reg %04x, ret %d, again now\n", + serdes->dev, + serdes->serdes_init_seq->reg_sequence[i].reg, ret); ret = serdes_reg_write(serdes, serdes->serdes_init_seq->reg_sequence[i].reg, serdes->serdes_init_seq->reg_sequence[i].def); @@ -40,9 +40,10 @@ int serdes_i2c_set_sequence(struct serdes *serdes) serdes_reg_read(serdes, serdes->serdes_init_seq->reg_sequence[i].reg, &def); if ((def != serdes->serdes_init_seq->reg_sequence[i].def) || (ret < 0)) { /* if read value != write value then write again */ - dev_err(dev, "read %04x %04x != %04x\n", - serdes->serdes_init_seq->reg_sequence[i].reg, - def, serdes->serdes_init_seq->reg_sequence[i].def); + if (num++ < 1) + dev_err(dev, "read %04x %04x != %04x\n", + serdes->serdes_init_seq->reg_sequence[i].reg, + def, serdes->serdes_init_seq->reg_sequence[i].def); serdes_reg_write(serdes, serdes->serdes_init_seq->reg_sequence[i].reg, serdes->serdes_init_seq->reg_sequence[i].def); @@ -91,6 +92,133 @@ static int serdes_set_i2c_address(struct serdes *serdes, u32 reg_hw, u32 reg_use return ret; } +static int serdes_i2c_set_sequence_backup(struct serdes *serdes) +{ + struct device *dev = serdes->dev; + int i, num = 0, ret = 0; + unsigned int def = 0; + + for (i = 0; i < serdes->serdes_backup_seq->reg_seq_cnt; i++) { + if (serdes->serdes_backup_seq->reg_sequence[i].reg == 0xffff) { + SERDES_DBG_MFD("%s: delay 0x%04x us\n", __func__, + serdes->serdes_backup_seq->reg_sequence[i].def); + udelay(serdes->serdes_backup_seq->reg_sequence[i].def); + continue; + } + + ret = serdes_reg_write(serdes, + serdes->serdes_backup_seq->reg_sequence[i].reg, + serdes->serdes_backup_seq->reg_sequence[i].def); + if (ret < 0) { + SERDES_DBG_MFD("%s failed to write reg %04x, ret %d, again now\n", + serdes->dev, + serdes->serdes_backup_seq->reg_sequence[i].reg, ret); + ret = serdes_reg_write(serdes, + serdes->serdes_backup_seq->reg_sequence[i].reg, + serdes->serdes_backup_seq->reg_sequence[i].def); + } + serdes_reg_read(serdes, serdes->serdes_backup_seq->reg_sequence[i].reg, &def); + if ((def != serdes->serdes_backup_seq->reg_sequence[i].def) || (ret < 0)) { + /* if read value != write value then write again */ + if (num++ < 1) + dev_err(dev, "%s read %04x %04x != %04x\n", __func__, + serdes->serdes_backup_seq->reg_sequence[i].reg, + def, serdes->serdes_backup_seq->reg_sequence[i].def); + serdes_reg_write(serdes, + serdes->serdes_backup_seq->reg_sequence[i].reg, + serdes->serdes_backup_seq->reg_sequence[i].def); + } + } + + return ret; +} + +static int serdes_i2c_backup_register(struct serdes *serdes) +{ + int i, ret = 0; + + for (i = 0; i < serdes->serdes_backup_seq->reg_seq_cnt; i++) { + if (serdes->serdes_backup_seq->reg_sequence[i].reg == 0xffff) + continue; + serdes_reg_read(serdes, serdes->serdes_backup_seq->reg_sequence[i].reg, + &serdes->serdes_backup_seq->reg_sequence[i].def); + } + + return ret; +} + +static int serdes_i2c_check_register(struct serdes *serdes, int *flag) +{ + struct device *dev = serdes->dev; + int ret = 0; + unsigned int def = 0; + unsigned int num = 0; + + get_random_bytes(&num, 1); + if (num > serdes->serdes_backup_seq->reg_seq_cnt - 1) + num = 0; + + if (serdes->serdes_backup_seq->reg_sequence[num].reg == 0xffff) + return 0; + + ret = serdes_reg_read(serdes, serdes->serdes_backup_seq->reg_sequence[num].reg, &def); + if ((def != serdes->serdes_backup_seq->reg_sequence[num].def) || (ret < 0)) { + /* if read value != write value then write again */ + dev_err(dev, "%s read %04x %04x != %04x\n", __func__, + serdes->serdes_backup_seq->reg_sequence[num].reg, + def, serdes->serdes_backup_seq->reg_sequence[num].def); + *flag = 1; + return ret; + } + + return ret; +} + +static void serdes_reg_check_work(struct kthread_work *work) +{ + int flag = 0; + struct serdes *serdes = container_of(work, struct serdes, + reg_check_work.work); + + if (atomic_read(&serdes->flag_ser_init)) { + serdes_i2c_backup_register(serdes); + atomic_set(&serdes->flag_ser_init, 0); + } + + serdes_i2c_check_register(serdes, &flag); + if (flag) { + if (serdes->chip_data->chip_init) + serdes->chip_data->chip_init(serdes); + serdes_i2c_set_sequence_backup(serdes); + msleep(500); + SERDES_DBG_MFD("%s %s\n", __func__, serdes->chip_data->name); + } + kthread_queue_delayed_work(serdes->kworker, &serdes->reg_check_work, + msecs_to_jiffies(2000)); +} + +static int serdes_reg_check_work_setup(struct serdes *serdes) +{ + kthread_init_delayed_work(&serdes->reg_check_work, + serdes_reg_check_work); + + serdes->kworker = kthread_create_worker(0, "%s", dev_name(serdes->dev)); + if (IS_ERR(serdes->kworker)) + return PTR_ERR(serdes->kworker); + mutex_init(&serdes->reg_check_lock); + atomic_set(&serdes->flag_ser_init, 1); + kthread_queue_delayed_work(serdes->kworker, &serdes->reg_check_work, + msecs_to_jiffies(20000)); + + return 0; +} + +static void serdes_reg_check_work_free(struct serdes *serdes) +{ + kthread_cancel_delayed_work_sync(&serdes->reg_check_work); + kthread_destroy_worker(serdes->kworker); +} + static void serdes_mfd_work(struct work_struct *work) { struct serdes *serdes = container_of(work, struct serdes, mfd_delay_work.work); @@ -161,6 +289,17 @@ static int serdes_get_init_seq(struct serdes *serdes) return err; } + serdes->serdes_backup_seq = devm_kzalloc(dev, sizeof(*serdes->serdes_backup_seq), + GFP_KERNEL); + if (!serdes->serdes_backup_seq) + return -ENOMEM; + + err = serdes_parse_init_seq(dev, data, len, serdes->serdes_backup_seq); + if (err) { + dev_err(dev, "failed to parse serdes-init-sequence\n"); + return err; + } + /* init ser register(not des register) more early if uboot logo disabled */ serdes->route_enable = of_property_read_bool(dev->of_node, "route-enable"); if ((!serdes->route_enable) && (serdes->chip_data->serdes_type == TYPE_SER)) { @@ -284,6 +423,13 @@ static int serdes_i2c_probe(struct i2c_client *client, SERDES_DBG_MFD("%s: use_delay_work=%d\n", __func__, serdes->use_delay_work); } + serdes->use_reg_check_work = of_property_read_bool(dev->of_node, "use-reg-check-work"); + if (serdes->use_reg_check_work) { + serdes_reg_check_work_setup(serdes); + + SERDES_DBG_MFD("%s: use_reg_check_work=%d\n", __func__, serdes->use_reg_check_work); + } + dev_info(dev, "serdes %s serdes_i2c_probe successful version %s\n", serdes->chip_data->name, MFD_SERDES_DISPLAY_VERSION); @@ -298,6 +444,20 @@ static void serdes_i2c_shutdown(struct i2c_client *client) serdes_device_shutdown(serdes); } +static void serdes_i2c_remove(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct serdes *serdes = dev_get_drvdata(dev); + + if (serdes->use_reg_check_work) + serdes_reg_check_work_free(serdes); + + if (serdes->use_delay_work) { + cancel_delayed_work_sync(&serdes->mfd_delay_work); + destroy_workqueue(serdes->mfd_wq); + } +} + static int serdes_i2c_prepare(struct device *dev) { return 0; @@ -394,6 +554,7 @@ static struct i2c_driver serdes_i2c_driver = { }, .probe = serdes_i2c_probe, .shutdown = serdes_i2c_shutdown, + .remove = (void *)serdes_i2c_remove, }; static int __init serdes_i2c_init(void)