// SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2019 MediaTek Inc. */ #include #include #include #include #include #include #include #include #include "ktz8866.h" /***************************************************************************** * GLobal Variable *****************************************************************************/ struct ktz8866 { struct device *dev; struct i2c_client *ktz8866_i2c; bool pwm_en; bool bias_en; }; static struct ktz8866 *ktz8866_left; static struct ktz8866 *ktz8866_right; static DEFINE_MUTEX(read_lock); /***************************************************************************** * Function Prototype *****************************************************************************/ /***************************************************************************** * Extern Area *****************************************************************************/ static int lcd_bl_write_byte(struct i2c_client *i2c, unsigned char addr, unsigned char value) { int ret = 0; unsigned char write_data[2] = {0}; write_data[0] = addr; write_data[1] = value; ret = i2c_master_send(i2c, write_data, 2); if (ret < 0) pr_info("%s i2c write data fail !!\n", __func__); return ret; } static int lcd_bl_read_byte(struct i2c_client *i2c, u8 regnum) { u8 buffer[1], reg_value[1]; int res = 0; mutex_lock(&read_lock); buffer[0] = regnum; res = i2c_master_send(i2c, buffer, 0x1); if (res <= 0) { mutex_unlock(&read_lock); pr_info("read reg send res = %d\n", res); return res; } res = i2c_master_recv(i2c, reg_value, 0x1); if (res <= 0) { mutex_unlock(&read_lock); pr_info("read reg send res = %d\n", res); return res; } pr_info("ktz8866 read left=0x%x\n",reg_value[0]); mutex_unlock(&read_lock); return reg_value[0]; } int lcd_bl_set_led_brightness(int value) { pr_info("%s:hyper bl = %d\n", __func__, value); if (value < 0) { pr_info("%s: invalid value=%d\n", __func__, value); return 0; } if (ktz8866_left && ktz8866_left->pwm_en) { if (value > 0) { pr_info("%s:left bl = %d\n", __func__, value); lcd_bl_write_byte(ktz8866_left->ktz8866_i2c, 0x02, 0xDA); lcd_bl_write_byte(ktz8866_left->ktz8866_i2c, 0x11, 0x37); lcd_bl_write_byte(ktz8866_left->ktz8866_i2c, 0x15, 0xA0); lcd_bl_write_byte(ktz8866_left->ktz8866_i2c, 0x08, 0x4F); lcd_bl_write_byte(ktz8866_left->ktz8866_i2c, 0x04, value & 0x07); lcd_bl_write_byte(ktz8866_left->ktz8866_i2c, 0x05, (value >> 3) & 0xFF); } else { pr_info("%s:left bl = %d\n", __func__, value); lcd_bl_write_byte(ktz8866_left->ktz8866_i2c, 0x04, 0x00); lcd_bl_write_byte(ktz8866_left->ktz8866_i2c, 0x05, 0x00); } } if (ktz8866_right && ktz8866_right->pwm_en) { if (value > 0) { pr_info("%s:right bl = %d\n", __func__, value); lcd_bl_write_byte(ktz8866_right->ktz8866_i2c, 0x02, 0xDA); lcd_bl_write_byte(ktz8866_right->ktz8866_i2c, 0x11, 0x37); lcd_bl_write_byte(ktz8866_right->ktz8866_i2c, 0x15, 0xA0); lcd_bl_write_byte(ktz8866_right->ktz8866_i2c, 0x08, 0x4F); lcd_bl_write_byte(ktz8866_right->ktz8866_i2c, 0x04, value & 0x07); lcd_bl_write_byte(ktz8866_right->ktz8866_i2c, 0x05, (value >> 3) & 0xFF); } else { pr_info("%s:right bl = %d\n", __func__, value); lcd_bl_write_byte(ktz8866_right->ktz8866_i2c, 0x04, 0x00); lcd_bl_write_byte(ktz8866_right->ktz8866_i2c, 0x05, 0x00); } } return 0; } EXPORT_SYMBOL(lcd_bl_set_led_brightness); int lcd_set_bias(int enable) { pr_info("%s,value = %d", __func__, enable); if (ktz8866_left && ktz8866_left->bias_en) { if (enable) { pr_info("%s:left en = %d\n", __func__, enable); lcd_bl_write_byte(ktz8866_left->ktz8866_i2c, 0x0C, 0x2E); lcd_bl_write_byte(ktz8866_left->ktz8866_i2c, 0x0D, 0x26); lcd_bl_write_byte(ktz8866_left->ktz8866_i2c, 0x0E, 0x26); lcd_bl_write_byte(ktz8866_left->ktz8866_i2c, 0x09, 0x9C); mdelay(5); lcd_bl_write_byte(ktz8866_left->ktz8866_i2c, 0x09, 0x9E); } else { pr_info("%s:left en = %d\n", __func__, enable); lcd_bl_write_byte(ktz8866_left->ktz8866_i2c, 0x09, 0x9C); mdelay(5); lcd_bl_write_byte(ktz8866_left->ktz8866_i2c, 0x09, 0x98); } } if (ktz8866_right && ktz8866_right->bias_en) { if (enable) { pr_info("%s:right en = %d\n", __func__, enable); lcd_bl_write_byte(ktz8866_right->ktz8866_i2c, 0x0C, 0x2E); lcd_bl_write_byte(ktz8866_right->ktz8866_i2c, 0x0D, 0x26); lcd_bl_write_byte(ktz8866_right->ktz8866_i2c, 0x0E, 0x26); lcd_bl_write_byte(ktz8866_right->ktz8866_i2c, 0x09, 0x9C); mdelay(5); /* delay 5ms */ lcd_bl_write_byte(ktz8866_right->ktz8866_i2c, 0x09, 0x9E); } else { pr_info("%s:right en = %d\n", __func__, enable); lcd_bl_write_byte(ktz8866_right->ktz8866_i2c, 0x09, 0x9C); mdelay(5); lcd_bl_write_byte(ktz8866_right->ktz8866_i2c, 0x09, 0x98); } } return 0; } EXPORT_SYMBOL(lcd_set_bias); static int ktz8866_probe(struct i2c_client *client, const struct i2c_device_id *id) { int ret = 0; struct device *dev = &client->dev; struct ktz8866 *ktz8866_client; if (!client) { pr_info("%s i2c_client is NULL\n", __func__); return -EINVAL; } pr_info("%s, i2c address: %0x\n", __func__, client->addr); ktz8866_client = devm_kzalloc(dev, sizeof(*ktz8866_client), GFP_KERNEL); if (!ktz8866_client) return -ENOMEM; ktz8866_client->ktz8866_i2c = client; ktz8866_client->bias_en = of_property_read_bool(dev->of_node, "ktz8866,bias-en"); ktz8866_client->pwm_en = of_property_read_bool(dev->of_node, "ktz8866,pwm-en"); if (!ktz8866_left) { pr_info("probe for left\n"); ktz8866_left = ktz8866_client; } else if (!ktz8866_right) { pr_info("probe for right\n"); ktz8866_right = ktz8866_client; } #ifdef CONFIG_MTK_DISP_NO_LK //8866 is initial in lk when have lk, we connot touch it in probe if (ktz8866_client->bias_en) { //write vsp/vsn reg pr_info("%s:bias en\n", __func__); ret = lcd_bl_write_byte(ktz8866_client->ktz8866_i2c, 0x0C, 0x2E); ret = lcd_bl_write_byte(ktz8866_client->ktz8866_i2c, 0x0D, 0x26); ret = lcd_bl_write_byte(ktz8866_client->ktz8866_i2c, 0x0E, 0x26); ret = lcd_bl_write_byte(ktz8866_client->ktz8866_i2c, 0x09, 0x9C); mdelay(5); /* delay 5ms */ ret = lcd_bl_write_byte(ktz8866_client->ktz8866_i2c, 0x09, 0x9E); } //write backlight reg if (ktz8866_client->pwm_en) { pr_info("%s:pwm en\n", __func__); ret = lcd_bl_write_byte(ktz8866_client->ktz8866_i2c, 0x02, 0xDA); ret = lcd_bl_write_byte(ktz8866_client->ktz8866_i2c, 0x11, 0x37); ret = lcd_bl_write_byte(ktz8866_client->ktz8866_i2c, 0x15, 0xA0); ret = lcd_bl_write_byte(ktz8866_client->ktz8866_i2c, 0x08, 0x4F); } #endif if (ret < 0) pr_info("[%s]:I2C write reg is fail!", __func__); else pr_info("[%s]:I2C write reg is success!", __func__); return ret; } static int ktz8866_remove(struct i2c_client *client) { ktz8866_left = NULL; ktz8866_right = NULL; i2c_unregister_device(client); return 0; } static const struct i2c_device_id ktz8866_i2c_table[] = { {"ktz8866", 0}, {}, }; MODULE_DEVICE_TABLE(i2c, ktz8866_i2c_table); static const struct of_device_id ktz8866_match[] = { { .compatible = "ktz,ktz8866" }, {}, }; MODULE_DEVICE_TABLE(of, it6151_match); static struct i2c_driver mtz8866_driver = { .id_table = ktz8866_i2c_table, .probe = ktz8866_probe, .remove = ktz8866_remove, .driver = { .name = "ktz,ktz8866", .of_match_table = ktz8866_match, }, }; module_i2c_driver(mtz8866_driver); MODULE_AUTHOR("henry tu "); MODULE_DESCRIPTION("Mediatek ktz8866 Driver"); MODULE_LICENSE("GPL");