18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci// tm6000-i2c.c - driver for TM5600/TM6000/TM6010 USB video capture devices 38c2ecf20Sopenharmony_ci// 48c2ecf20Sopenharmony_ci// Copyright (c) 2006-2007 Mauro Carvalho Chehab <mchehab@kernel.org> 58c2ecf20Sopenharmony_ci// 68c2ecf20Sopenharmony_ci// Copyright (c) 2007 Michel Ludwig <michel.ludwig@gmail.com> 78c2ecf20Sopenharmony_ci// - Fix SMBus Read Byte command 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/module.h> 108c2ecf20Sopenharmony_ci#include <linux/kernel.h> 118c2ecf20Sopenharmony_ci#include <linux/usb.h> 128c2ecf20Sopenharmony_ci#include <linux/i2c.h> 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include "tm6000.h" 158c2ecf20Sopenharmony_ci#include "tm6000-regs.h" 168c2ecf20Sopenharmony_ci#include <media/v4l2-common.h> 178c2ecf20Sopenharmony_ci#include <media/tuner.h> 188c2ecf20Sopenharmony_ci#include "tuner-xc2028.h" 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci/* ----------------------------------------------------------- */ 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_cistatic unsigned int i2c_debug; 248c2ecf20Sopenharmony_cimodule_param(i2c_debug, int, 0644); 258c2ecf20Sopenharmony_ciMODULE_PARM_DESC(i2c_debug, "enable debug messages [i2c]"); 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci#define i2c_dprintk(lvl, fmt, args...) if (i2c_debug >= lvl) do { \ 288c2ecf20Sopenharmony_ci printk(KERN_DEBUG "%s at %s: " fmt, \ 298c2ecf20Sopenharmony_ci dev->name, __func__, ##args); } while (0) 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_cistatic int tm6000_i2c_send_regs(struct tm6000_core *dev, unsigned char addr, 328c2ecf20Sopenharmony_ci __u8 reg, char *buf, int len) 338c2ecf20Sopenharmony_ci{ 348c2ecf20Sopenharmony_ci int rc; 358c2ecf20Sopenharmony_ci unsigned int i2c_packet_limit = 16; 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci if (dev->dev_type == TM6010) 388c2ecf20Sopenharmony_ci i2c_packet_limit = 80; 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci if (!buf) 418c2ecf20Sopenharmony_ci return -1; 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci if (len < 1 || len > i2c_packet_limit) { 448c2ecf20Sopenharmony_ci printk(KERN_ERR "Incorrect length of i2c packet = %d, limit set to %d\n", 458c2ecf20Sopenharmony_ci len, i2c_packet_limit); 468c2ecf20Sopenharmony_ci return -1; 478c2ecf20Sopenharmony_ci } 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci /* capture mutex */ 508c2ecf20Sopenharmony_ci rc = tm6000_read_write_usb(dev, USB_DIR_OUT | USB_TYPE_VENDOR | 518c2ecf20Sopenharmony_ci USB_RECIP_DEVICE, REQ_16_SET_GET_I2C_WR1_RDN, 528c2ecf20Sopenharmony_ci addr | reg << 8, 0, buf, len); 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci if (rc < 0) { 558c2ecf20Sopenharmony_ci /* release mutex */ 568c2ecf20Sopenharmony_ci return rc; 578c2ecf20Sopenharmony_ci } 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci /* release mutex */ 608c2ecf20Sopenharmony_ci return rc; 618c2ecf20Sopenharmony_ci} 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci/* Generic read - doesn't work fine with 16bit registers */ 648c2ecf20Sopenharmony_cistatic int tm6000_i2c_recv_regs(struct tm6000_core *dev, unsigned char addr, 658c2ecf20Sopenharmony_ci __u8 reg, char *buf, int len) 668c2ecf20Sopenharmony_ci{ 678c2ecf20Sopenharmony_ci int rc; 688c2ecf20Sopenharmony_ci u8 b[2]; 698c2ecf20Sopenharmony_ci unsigned int i2c_packet_limit = 16; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci if (dev->dev_type == TM6010) 728c2ecf20Sopenharmony_ci i2c_packet_limit = 64; 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci if (!buf) 758c2ecf20Sopenharmony_ci return -1; 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci if (len < 1 || len > i2c_packet_limit) { 788c2ecf20Sopenharmony_ci printk(KERN_ERR "Incorrect length of i2c packet = %d, limit set to %d\n", 798c2ecf20Sopenharmony_ci len, i2c_packet_limit); 808c2ecf20Sopenharmony_ci return -1; 818c2ecf20Sopenharmony_ci } 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci /* capture mutex */ 848c2ecf20Sopenharmony_ci if ((dev->caps.has_zl10353) && (dev->demod_addr << 1 == addr) && (reg % 2 == 0)) { 858c2ecf20Sopenharmony_ci /* 868c2ecf20Sopenharmony_ci * Workaround an I2C bug when reading from zl10353 878c2ecf20Sopenharmony_ci */ 888c2ecf20Sopenharmony_ci reg -= 1; 898c2ecf20Sopenharmony_ci len += 1; 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci rc = tm6000_read_write_usb(dev, USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, 928c2ecf20Sopenharmony_ci REQ_16_SET_GET_I2C_WR1_RDN, addr | reg << 8, 0, b, len); 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci *buf = b[1]; 958c2ecf20Sopenharmony_ci } else { 968c2ecf20Sopenharmony_ci rc = tm6000_read_write_usb(dev, USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, 978c2ecf20Sopenharmony_ci REQ_16_SET_GET_I2C_WR1_RDN, addr | reg << 8, 0, buf, len); 988c2ecf20Sopenharmony_ci } 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci /* release mutex */ 1018c2ecf20Sopenharmony_ci return rc; 1028c2ecf20Sopenharmony_ci} 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci/* 1058c2ecf20Sopenharmony_ci * read from a 16bit register 1068c2ecf20Sopenharmony_ci * for example xc2028, xc3028 or xc3028L 1078c2ecf20Sopenharmony_ci */ 1088c2ecf20Sopenharmony_cistatic int tm6000_i2c_recv_regs16(struct tm6000_core *dev, unsigned char addr, 1098c2ecf20Sopenharmony_ci __u16 reg, char *buf, int len) 1108c2ecf20Sopenharmony_ci{ 1118c2ecf20Sopenharmony_ci int rc; 1128c2ecf20Sopenharmony_ci unsigned char ureg; 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci if (!buf || len != 2) 1158c2ecf20Sopenharmony_ci return -1; 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci /* capture mutex */ 1188c2ecf20Sopenharmony_ci if (dev->dev_type == TM6010) { 1198c2ecf20Sopenharmony_ci ureg = reg & 0xFF; 1208c2ecf20Sopenharmony_ci rc = tm6000_read_write_usb(dev, USB_DIR_OUT | USB_TYPE_VENDOR | 1218c2ecf20Sopenharmony_ci USB_RECIP_DEVICE, REQ_16_SET_GET_I2C_WR1_RDN, 1228c2ecf20Sopenharmony_ci addr | (reg & 0xFF00), 0, &ureg, 1); 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci if (rc < 0) { 1258c2ecf20Sopenharmony_ci /* release mutex */ 1268c2ecf20Sopenharmony_ci return rc; 1278c2ecf20Sopenharmony_ci } 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci rc = tm6000_read_write_usb(dev, USB_DIR_IN | USB_TYPE_VENDOR | 1308c2ecf20Sopenharmony_ci USB_RECIP_DEVICE, REQ_35_AFTEK_TUNER_READ, 1318c2ecf20Sopenharmony_ci reg, 0, buf, len); 1328c2ecf20Sopenharmony_ci } else { 1338c2ecf20Sopenharmony_ci rc = tm6000_read_write_usb(dev, USB_DIR_IN | USB_TYPE_VENDOR | 1348c2ecf20Sopenharmony_ci USB_RECIP_DEVICE, REQ_14_SET_GET_I2C_WR2_RDN, 1358c2ecf20Sopenharmony_ci addr, reg, buf, len); 1368c2ecf20Sopenharmony_ci } 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci /* release mutex */ 1398c2ecf20Sopenharmony_ci return rc; 1408c2ecf20Sopenharmony_ci} 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_cistatic int tm6000_i2c_xfer(struct i2c_adapter *i2c_adap, 1438c2ecf20Sopenharmony_ci struct i2c_msg msgs[], int num) 1448c2ecf20Sopenharmony_ci{ 1458c2ecf20Sopenharmony_ci struct tm6000_core *dev = i2c_adap->algo_data; 1468c2ecf20Sopenharmony_ci int addr, rc, i, byte; 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci for (i = 0; i < num; i++) { 1498c2ecf20Sopenharmony_ci addr = (msgs[i].addr << 1) & 0xff; 1508c2ecf20Sopenharmony_ci i2c_dprintk(2, "%s %s addr=0x%x len=%d:", 1518c2ecf20Sopenharmony_ci (msgs[i].flags & I2C_M_RD) ? "read" : "write", 1528c2ecf20Sopenharmony_ci i == num - 1 ? "stop" : "nonstop", addr, msgs[i].len); 1538c2ecf20Sopenharmony_ci if (msgs[i].flags & I2C_M_RD) { 1548c2ecf20Sopenharmony_ci /* read request without preceding register selection */ 1558c2ecf20Sopenharmony_ci /* 1568c2ecf20Sopenharmony_ci * The TM6000 only supports a read transaction 1578c2ecf20Sopenharmony_ci * immediately after a 1 or 2 byte write to select 1588c2ecf20Sopenharmony_ci * a register. We cannot fulfill this request. 1598c2ecf20Sopenharmony_ci */ 1608c2ecf20Sopenharmony_ci i2c_dprintk(2, " read without preceding write not supported"); 1618c2ecf20Sopenharmony_ci rc = -EOPNOTSUPP; 1628c2ecf20Sopenharmony_ci goto err; 1638c2ecf20Sopenharmony_ci } else if (i + 1 < num && msgs[i].len <= 2 && 1648c2ecf20Sopenharmony_ci (msgs[i + 1].flags & I2C_M_RD) && 1658c2ecf20Sopenharmony_ci msgs[i].addr == msgs[i + 1].addr) { 1668c2ecf20Sopenharmony_ci /* 1 or 2 byte write followed by a read */ 1678c2ecf20Sopenharmony_ci if (i2c_debug >= 2) 1688c2ecf20Sopenharmony_ci for (byte = 0; byte < msgs[i].len; byte++) 1698c2ecf20Sopenharmony_ci printk(KERN_CONT " %02x", msgs[i].buf[byte]); 1708c2ecf20Sopenharmony_ci i2c_dprintk(2, "; joined to read %s len=%d:", 1718c2ecf20Sopenharmony_ci i == num - 2 ? "stop" : "nonstop", 1728c2ecf20Sopenharmony_ci msgs[i + 1].len); 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci if (msgs[i].len == 2) { 1758c2ecf20Sopenharmony_ci rc = tm6000_i2c_recv_regs16(dev, addr, 1768c2ecf20Sopenharmony_ci msgs[i].buf[0] << 8 | msgs[i].buf[1], 1778c2ecf20Sopenharmony_ci msgs[i + 1].buf, msgs[i + 1].len); 1788c2ecf20Sopenharmony_ci } else { 1798c2ecf20Sopenharmony_ci rc = tm6000_i2c_recv_regs(dev, addr, msgs[i].buf[0], 1808c2ecf20Sopenharmony_ci msgs[i + 1].buf, msgs[i + 1].len); 1818c2ecf20Sopenharmony_ci } 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci i++; 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci if (addr == dev->tuner_addr << 1) { 1868c2ecf20Sopenharmony_ci tm6000_set_reg(dev, REQ_50_SET_START, 0, 0); 1878c2ecf20Sopenharmony_ci tm6000_set_reg(dev, REQ_51_SET_STOP, 0, 0); 1888c2ecf20Sopenharmony_ci } 1898c2ecf20Sopenharmony_ci if (i2c_debug >= 2) 1908c2ecf20Sopenharmony_ci for (byte = 0; byte < msgs[i].len; byte++) 1918c2ecf20Sopenharmony_ci printk(KERN_CONT " %02x", msgs[i].buf[byte]); 1928c2ecf20Sopenharmony_ci } else { 1938c2ecf20Sopenharmony_ci /* write bytes */ 1948c2ecf20Sopenharmony_ci if (i2c_debug >= 2) 1958c2ecf20Sopenharmony_ci for (byte = 0; byte < msgs[i].len; byte++) 1968c2ecf20Sopenharmony_ci printk(KERN_CONT " %02x", msgs[i].buf[byte]); 1978c2ecf20Sopenharmony_ci rc = tm6000_i2c_send_regs(dev, addr, msgs[i].buf[0], 1988c2ecf20Sopenharmony_ci msgs[i].buf + 1, msgs[i].len - 1); 1998c2ecf20Sopenharmony_ci } 2008c2ecf20Sopenharmony_ci if (i2c_debug >= 2) 2018c2ecf20Sopenharmony_ci printk(KERN_CONT "\n"); 2028c2ecf20Sopenharmony_ci if (rc < 0) 2038c2ecf20Sopenharmony_ci goto err; 2048c2ecf20Sopenharmony_ci } 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci return num; 2078c2ecf20Sopenharmony_cierr: 2088c2ecf20Sopenharmony_ci i2c_dprintk(2, " ERROR: %i\n", rc); 2098c2ecf20Sopenharmony_ci return rc; 2108c2ecf20Sopenharmony_ci} 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_cistatic int tm6000_i2c_eeprom(struct tm6000_core *dev) 2138c2ecf20Sopenharmony_ci{ 2148c2ecf20Sopenharmony_ci int i, rc; 2158c2ecf20Sopenharmony_ci unsigned char *p = dev->eedata; 2168c2ecf20Sopenharmony_ci unsigned char bytes[17]; 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci dev->i2c_client.addr = 0xa0 >> 1; 2198c2ecf20Sopenharmony_ci dev->eedata_size = 0; 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci bytes[16] = '\0'; 2228c2ecf20Sopenharmony_ci for (i = 0; i < sizeof(dev->eedata); ) { 2238c2ecf20Sopenharmony_ci *p = i; 2248c2ecf20Sopenharmony_ci rc = tm6000_i2c_recv_regs(dev, 0xa0, i, p, 1); 2258c2ecf20Sopenharmony_ci if (rc < 1) { 2268c2ecf20Sopenharmony_ci if (p == dev->eedata) 2278c2ecf20Sopenharmony_ci goto noeeprom; 2288c2ecf20Sopenharmony_ci else { 2298c2ecf20Sopenharmony_ci printk(KERN_WARNING 2308c2ecf20Sopenharmony_ci "%s: i2c eeprom read error (err=%d)\n", 2318c2ecf20Sopenharmony_ci dev->name, rc); 2328c2ecf20Sopenharmony_ci } 2338c2ecf20Sopenharmony_ci return -EINVAL; 2348c2ecf20Sopenharmony_ci } 2358c2ecf20Sopenharmony_ci dev->eedata_size++; 2368c2ecf20Sopenharmony_ci p++; 2378c2ecf20Sopenharmony_ci if (0 == (i % 16)) 2388c2ecf20Sopenharmony_ci printk(KERN_INFO "%s: i2c eeprom %02x:", dev->name, i); 2398c2ecf20Sopenharmony_ci printk(KERN_CONT " %02x", dev->eedata[i]); 2408c2ecf20Sopenharmony_ci if ((dev->eedata[i] >= ' ') && (dev->eedata[i] <= 'z')) 2418c2ecf20Sopenharmony_ci bytes[i%16] = dev->eedata[i]; 2428c2ecf20Sopenharmony_ci else 2438c2ecf20Sopenharmony_ci bytes[i%16] = '.'; 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci i++; 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci if (0 == (i % 16)) { 2488c2ecf20Sopenharmony_ci bytes[16] = '\0'; 2498c2ecf20Sopenharmony_ci printk(KERN_CONT " %s\n", bytes); 2508c2ecf20Sopenharmony_ci } 2518c2ecf20Sopenharmony_ci } 2528c2ecf20Sopenharmony_ci if (0 != (i%16)) { 2538c2ecf20Sopenharmony_ci bytes[i%16] = '\0'; 2548c2ecf20Sopenharmony_ci for (i %= 16; i < 16; i++) 2558c2ecf20Sopenharmony_ci printk(KERN_CONT " "); 2568c2ecf20Sopenharmony_ci printk(KERN_CONT " %s\n", bytes); 2578c2ecf20Sopenharmony_ci } 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci return 0; 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_cinoeeprom: 2628c2ecf20Sopenharmony_ci printk(KERN_INFO "%s: Huh, no eeprom present (err=%d)?\n", 2638c2ecf20Sopenharmony_ci dev->name, rc); 2648c2ecf20Sopenharmony_ci return -EINVAL; 2658c2ecf20Sopenharmony_ci} 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci/* ----------------------------------------------------------- */ 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci/* 2708c2ecf20Sopenharmony_ci * functionality() 2718c2ecf20Sopenharmony_ci */ 2728c2ecf20Sopenharmony_cistatic u32 functionality(struct i2c_adapter *adap) 2738c2ecf20Sopenharmony_ci{ 2748c2ecf20Sopenharmony_ci return I2C_FUNC_SMBUS_EMUL; 2758c2ecf20Sopenharmony_ci} 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_cistatic const struct i2c_algorithm tm6000_algo = { 2788c2ecf20Sopenharmony_ci .master_xfer = tm6000_i2c_xfer, 2798c2ecf20Sopenharmony_ci .functionality = functionality, 2808c2ecf20Sopenharmony_ci}; 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci/* ----------------------------------------------------------- */ 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci/* 2858c2ecf20Sopenharmony_ci * tm6000_i2c_register() 2868c2ecf20Sopenharmony_ci * register i2c bus 2878c2ecf20Sopenharmony_ci */ 2888c2ecf20Sopenharmony_ciint tm6000_i2c_register(struct tm6000_core *dev) 2898c2ecf20Sopenharmony_ci{ 2908c2ecf20Sopenharmony_ci int rc; 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci dev->i2c_adap.owner = THIS_MODULE; 2938c2ecf20Sopenharmony_ci dev->i2c_adap.algo = &tm6000_algo; 2948c2ecf20Sopenharmony_ci dev->i2c_adap.dev.parent = &dev->udev->dev; 2958c2ecf20Sopenharmony_ci strscpy(dev->i2c_adap.name, dev->name, sizeof(dev->i2c_adap.name)); 2968c2ecf20Sopenharmony_ci dev->i2c_adap.algo_data = dev; 2978c2ecf20Sopenharmony_ci i2c_set_adapdata(&dev->i2c_adap, &dev->v4l2_dev); 2988c2ecf20Sopenharmony_ci rc = i2c_add_adapter(&dev->i2c_adap); 2998c2ecf20Sopenharmony_ci if (rc) 3008c2ecf20Sopenharmony_ci return rc; 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci dev->i2c_client.adapter = &dev->i2c_adap; 3038c2ecf20Sopenharmony_ci strscpy(dev->i2c_client.name, "tm6000 internal", I2C_NAME_SIZE); 3048c2ecf20Sopenharmony_ci tm6000_i2c_eeprom(dev); 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci return 0; 3078c2ecf20Sopenharmony_ci} 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci/* 3108c2ecf20Sopenharmony_ci * tm6000_i2c_unregister() 3118c2ecf20Sopenharmony_ci * unregister i2c_bus 3128c2ecf20Sopenharmony_ci */ 3138c2ecf20Sopenharmony_ciint tm6000_i2c_unregister(struct tm6000_core *dev) 3148c2ecf20Sopenharmony_ci{ 3158c2ecf20Sopenharmony_ci i2c_del_adapter(&dev->i2c_adap); 3168c2ecf20Sopenharmony_ci return 0; 3178c2ecf20Sopenharmony_ci} 318