18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * upd6408x - NEC Electronics 3-Dimensional Y/C separation driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * 2003 by T.Adachi (tadachi@tadachi-net.com) 68c2ecf20Sopenharmony_ci * 2003 by Takeru KOMORIYA <komoriya@paken.org> 78c2ecf20Sopenharmony_ci * 2006 by Hans Verkuil <hverkuil@xs4all.nl> 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/module.h> 118c2ecf20Sopenharmony_ci#include <linux/kernel.h> 128c2ecf20Sopenharmony_ci#include <linux/i2c.h> 138c2ecf20Sopenharmony_ci#include <linux/videodev2.h> 148c2ecf20Sopenharmony_ci#include <linux/slab.h> 158c2ecf20Sopenharmony_ci#include <media/v4l2-device.h> 168c2ecf20Sopenharmony_ci#include <media/i2c/upd64083.h> 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("uPD64083 driver"); 198c2ecf20Sopenharmony_ciMODULE_AUTHOR("T. Adachi, Takeru KOMORIYA, Hans Verkuil"); 208c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_cistatic bool debug; 238c2ecf20Sopenharmony_cimodule_param(debug, bool, 0644); 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ciMODULE_PARM_DESC(debug, "Debug level (0-1)"); 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_cienum { 298c2ecf20Sopenharmony_ci R00 = 0, R01, R02, R03, R04, 308c2ecf20Sopenharmony_ci R05, R06, R07, R08, R09, 318c2ecf20Sopenharmony_ci R0A, R0B, R0C, R0D, R0E, R0F, 328c2ecf20Sopenharmony_ci R10, R11, R12, R13, R14, 338c2ecf20Sopenharmony_ci R15, R16, 348c2ecf20Sopenharmony_ci TOT_REGS 358c2ecf20Sopenharmony_ci}; 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_cistruct upd64083_state { 388c2ecf20Sopenharmony_ci struct v4l2_subdev sd; 398c2ecf20Sopenharmony_ci u8 mode; 408c2ecf20Sopenharmony_ci u8 ext_y_adc; 418c2ecf20Sopenharmony_ci u8 regs[TOT_REGS]; 428c2ecf20Sopenharmony_ci}; 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_cistatic inline struct upd64083_state *to_state(struct v4l2_subdev *sd) 458c2ecf20Sopenharmony_ci{ 468c2ecf20Sopenharmony_ci return container_of(sd, struct upd64083_state, sd); 478c2ecf20Sopenharmony_ci} 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci/* Initial values when used in combination with the 508c2ecf20Sopenharmony_ci NEC upd64031a ghost reduction chip. */ 518c2ecf20Sopenharmony_cistatic u8 upd64083_init[] = { 528c2ecf20Sopenharmony_ci 0x1f, 0x01, 0xa0, 0x2d, 0x29, /* we use EXCSS=0 */ 538c2ecf20Sopenharmony_ci 0x36, 0xdd, 0x05, 0x56, 0x48, 548c2ecf20Sopenharmony_ci 0x00, 0x3a, 0xa0, 0x05, 0x08, 558c2ecf20Sopenharmony_ci 0x44, 0x60, 0x08, 0x52, 0xf8, 568c2ecf20Sopenharmony_ci 0x53, 0x60, 0x10 578c2ecf20Sopenharmony_ci}; 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci/* ------------------------------------------------------------------------ */ 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_cistatic void upd64083_write(struct v4l2_subdev *sd, u8 reg, u8 val) 628c2ecf20Sopenharmony_ci{ 638c2ecf20Sopenharmony_ci struct i2c_client *client = v4l2_get_subdevdata(sd); 648c2ecf20Sopenharmony_ci u8 buf[2]; 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci buf[0] = reg; 678c2ecf20Sopenharmony_ci buf[1] = val; 688c2ecf20Sopenharmony_ci v4l2_dbg(1, debug, sd, "write reg: %02x val: %02x\n", reg, val); 698c2ecf20Sopenharmony_ci if (i2c_master_send(client, buf, 2) != 2) 708c2ecf20Sopenharmony_ci v4l2_err(sd, "I/O error write 0x%02x/0x%02x\n", reg, val); 718c2ecf20Sopenharmony_ci} 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci/* ------------------------------------------------------------------------ */ 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci#ifdef CONFIG_VIDEO_ADV_DEBUG 768c2ecf20Sopenharmony_cistatic u8 upd64083_read(struct v4l2_subdev *sd, u8 reg) 778c2ecf20Sopenharmony_ci{ 788c2ecf20Sopenharmony_ci struct i2c_client *client = v4l2_get_subdevdata(sd); 798c2ecf20Sopenharmony_ci u8 buf[7]; 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci if (reg >= sizeof(buf)) 828c2ecf20Sopenharmony_ci return 0xff; 838c2ecf20Sopenharmony_ci i2c_master_recv(client, buf, sizeof(buf)); 848c2ecf20Sopenharmony_ci return buf[reg]; 858c2ecf20Sopenharmony_ci} 868c2ecf20Sopenharmony_ci#endif 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci/* ------------------------------------------------------------------------ */ 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_cistatic int upd64083_s_routing(struct v4l2_subdev *sd, 918c2ecf20Sopenharmony_ci u32 input, u32 output, u32 config) 928c2ecf20Sopenharmony_ci{ 938c2ecf20Sopenharmony_ci struct upd64083_state *state = to_state(sd); 948c2ecf20Sopenharmony_ci u8 r00, r02; 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci if (input > 7 || (input & 6) == 6) 978c2ecf20Sopenharmony_ci return -EINVAL; 988c2ecf20Sopenharmony_ci state->mode = (input & 3) << 6; 998c2ecf20Sopenharmony_ci state->ext_y_adc = (input & UPD64083_EXT_Y_ADC) << 3; 1008c2ecf20Sopenharmony_ci r00 = (state->regs[R00] & ~(3 << 6)) | state->mode; 1018c2ecf20Sopenharmony_ci r02 = (state->regs[R02] & ~(1 << 5)) | state->ext_y_adc; 1028c2ecf20Sopenharmony_ci upd64083_write(sd, R00, r00); 1038c2ecf20Sopenharmony_ci upd64083_write(sd, R02, r02); 1048c2ecf20Sopenharmony_ci return 0; 1058c2ecf20Sopenharmony_ci} 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci#ifdef CONFIG_VIDEO_ADV_DEBUG 1088c2ecf20Sopenharmony_cistatic int upd64083_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) 1098c2ecf20Sopenharmony_ci{ 1108c2ecf20Sopenharmony_ci reg->val = upd64083_read(sd, reg->reg & 0xff); 1118c2ecf20Sopenharmony_ci reg->size = 1; 1128c2ecf20Sopenharmony_ci return 0; 1138c2ecf20Sopenharmony_ci} 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_cistatic int upd64083_s_register(struct v4l2_subdev *sd, const struct v4l2_dbg_register *reg) 1168c2ecf20Sopenharmony_ci{ 1178c2ecf20Sopenharmony_ci upd64083_write(sd, reg->reg & 0xff, reg->val & 0xff); 1188c2ecf20Sopenharmony_ci return 0; 1198c2ecf20Sopenharmony_ci} 1208c2ecf20Sopenharmony_ci#endif 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_cistatic int upd64083_log_status(struct v4l2_subdev *sd) 1238c2ecf20Sopenharmony_ci{ 1248c2ecf20Sopenharmony_ci struct i2c_client *client = v4l2_get_subdevdata(sd); 1258c2ecf20Sopenharmony_ci u8 buf[7]; 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci i2c_master_recv(client, buf, 7); 1288c2ecf20Sopenharmony_ci v4l2_info(sd, "Status: SA00=%02x SA01=%02x SA02=%02x SA03=%02x " 1298c2ecf20Sopenharmony_ci "SA04=%02x SA05=%02x SA06=%02x\n", 1308c2ecf20Sopenharmony_ci buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6]); 1318c2ecf20Sopenharmony_ci return 0; 1328c2ecf20Sopenharmony_ci} 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci/* ----------------------------------------------------------------------- */ 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_core_ops upd64083_core_ops = { 1378c2ecf20Sopenharmony_ci .log_status = upd64083_log_status, 1388c2ecf20Sopenharmony_ci#ifdef CONFIG_VIDEO_ADV_DEBUG 1398c2ecf20Sopenharmony_ci .g_register = upd64083_g_register, 1408c2ecf20Sopenharmony_ci .s_register = upd64083_s_register, 1418c2ecf20Sopenharmony_ci#endif 1428c2ecf20Sopenharmony_ci}; 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_video_ops upd64083_video_ops = { 1458c2ecf20Sopenharmony_ci .s_routing = upd64083_s_routing, 1468c2ecf20Sopenharmony_ci}; 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_ops upd64083_ops = { 1498c2ecf20Sopenharmony_ci .core = &upd64083_core_ops, 1508c2ecf20Sopenharmony_ci .video = &upd64083_video_ops, 1518c2ecf20Sopenharmony_ci}; 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci/* ------------------------------------------------------------------------ */ 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci/* i2c implementation */ 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_cistatic int upd64083_probe(struct i2c_client *client, 1588c2ecf20Sopenharmony_ci const struct i2c_device_id *id) 1598c2ecf20Sopenharmony_ci{ 1608c2ecf20Sopenharmony_ci struct upd64083_state *state; 1618c2ecf20Sopenharmony_ci struct v4l2_subdev *sd; 1628c2ecf20Sopenharmony_ci int i; 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) 1658c2ecf20Sopenharmony_ci return -EIO; 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci v4l_info(client, "chip found @ 0x%x (%s)\n", 1688c2ecf20Sopenharmony_ci client->addr << 1, client->adapter->name); 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci state = devm_kzalloc(&client->dev, sizeof(*state), GFP_KERNEL); 1718c2ecf20Sopenharmony_ci if (state == NULL) 1728c2ecf20Sopenharmony_ci return -ENOMEM; 1738c2ecf20Sopenharmony_ci sd = &state->sd; 1748c2ecf20Sopenharmony_ci v4l2_i2c_subdev_init(sd, client, &upd64083_ops); 1758c2ecf20Sopenharmony_ci /* Initially assume that a ghost reduction chip is present */ 1768c2ecf20Sopenharmony_ci state->mode = 0; /* YCS mode */ 1778c2ecf20Sopenharmony_ci state->ext_y_adc = (1 << 5); 1788c2ecf20Sopenharmony_ci memcpy(state->regs, upd64083_init, TOT_REGS); 1798c2ecf20Sopenharmony_ci for (i = 0; i < TOT_REGS; i++) 1808c2ecf20Sopenharmony_ci upd64083_write(sd, i, state->regs[i]); 1818c2ecf20Sopenharmony_ci return 0; 1828c2ecf20Sopenharmony_ci} 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_cistatic int upd64083_remove(struct i2c_client *client) 1858c2ecf20Sopenharmony_ci{ 1868c2ecf20Sopenharmony_ci struct v4l2_subdev *sd = i2c_get_clientdata(client); 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci v4l2_device_unregister_subdev(sd); 1898c2ecf20Sopenharmony_ci return 0; 1908c2ecf20Sopenharmony_ci} 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci/* ----------------------------------------------------------------------- */ 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_cistatic const struct i2c_device_id upd64083_id[] = { 1958c2ecf20Sopenharmony_ci { "upd64083", 0 }, 1968c2ecf20Sopenharmony_ci { } 1978c2ecf20Sopenharmony_ci}; 1988c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, upd64083_id); 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_cistatic struct i2c_driver upd64083_driver = { 2018c2ecf20Sopenharmony_ci .driver = { 2028c2ecf20Sopenharmony_ci .name = "upd64083", 2038c2ecf20Sopenharmony_ci }, 2048c2ecf20Sopenharmony_ci .probe = upd64083_probe, 2058c2ecf20Sopenharmony_ci .remove = upd64083_remove, 2068c2ecf20Sopenharmony_ci .id_table = upd64083_id, 2078c2ecf20Sopenharmony_ci}; 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_cimodule_i2c_driver(upd64083_driver); 210