18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * CIMaX SP2/SP2HF (Atmel T90FJR) CI driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2014 Olli Salonen <olli.salonen@iki.fi> 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Heavily based on CIMax2(R) SP2 driver in conjunction with NetUp Dual 88c2ecf20Sopenharmony_ci * DVB-S2 CI card (cimax2) with following copyrights: 98c2ecf20Sopenharmony_ci * 108c2ecf20Sopenharmony_ci * Copyright (C) 2009 NetUP Inc. 118c2ecf20Sopenharmony_ci * Copyright (C) 2009 Igor M. Liplianin <liplianin@netup.ru> 128c2ecf20Sopenharmony_ci * Copyright (C) 2009 Abylay Ospan <aospan@netup.ru> 138c2ecf20Sopenharmony_ci */ 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci#include "sp2_priv.h" 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_cistatic int sp2_read_i2c(struct sp2 *s, u8 reg, u8 *buf, int len) 188c2ecf20Sopenharmony_ci{ 198c2ecf20Sopenharmony_ci int ret; 208c2ecf20Sopenharmony_ci struct i2c_client *client = s->client; 218c2ecf20Sopenharmony_ci struct i2c_adapter *adap = client->adapter; 228c2ecf20Sopenharmony_ci struct i2c_msg msg[] = { 238c2ecf20Sopenharmony_ci { 248c2ecf20Sopenharmony_ci .addr = client->addr, 258c2ecf20Sopenharmony_ci .flags = 0, 268c2ecf20Sopenharmony_ci .buf = ®, 278c2ecf20Sopenharmony_ci .len = 1 288c2ecf20Sopenharmony_ci }, { 298c2ecf20Sopenharmony_ci .addr = client->addr, 308c2ecf20Sopenharmony_ci .flags = I2C_M_RD, 318c2ecf20Sopenharmony_ci .buf = buf, 328c2ecf20Sopenharmony_ci .len = len 338c2ecf20Sopenharmony_ci } 348c2ecf20Sopenharmony_ci }; 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci ret = i2c_transfer(adap, msg, 2); 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci if (ret != 2) { 398c2ecf20Sopenharmony_ci dev_err(&client->dev, "i2c read error, reg = 0x%02x, status = %d\n", 408c2ecf20Sopenharmony_ci reg, ret); 418c2ecf20Sopenharmony_ci if (ret < 0) 428c2ecf20Sopenharmony_ci return ret; 438c2ecf20Sopenharmony_ci else 448c2ecf20Sopenharmony_ci return -EIO; 458c2ecf20Sopenharmony_ci } 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci dev_dbg(&s->client->dev, "addr=0x%04x, reg = 0x%02x, data = %02x\n", 488c2ecf20Sopenharmony_ci client->addr, reg, buf[0]); 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci return 0; 518c2ecf20Sopenharmony_ci} 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_cistatic int sp2_write_i2c(struct sp2 *s, u8 reg, u8 *buf, int len) 548c2ecf20Sopenharmony_ci{ 558c2ecf20Sopenharmony_ci int ret; 568c2ecf20Sopenharmony_ci u8 buffer[35]; 578c2ecf20Sopenharmony_ci struct i2c_client *client = s->client; 588c2ecf20Sopenharmony_ci struct i2c_adapter *adap = client->adapter; 598c2ecf20Sopenharmony_ci struct i2c_msg msg = { 608c2ecf20Sopenharmony_ci .addr = client->addr, 618c2ecf20Sopenharmony_ci .flags = 0, 628c2ecf20Sopenharmony_ci .buf = &buffer[0], 638c2ecf20Sopenharmony_ci .len = len + 1 648c2ecf20Sopenharmony_ci }; 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci if ((len + 1) > sizeof(buffer)) { 678c2ecf20Sopenharmony_ci dev_err(&client->dev, "i2c wr reg=%02x: len=%d is too big!\n", 688c2ecf20Sopenharmony_ci reg, len); 698c2ecf20Sopenharmony_ci return -EINVAL; 708c2ecf20Sopenharmony_ci } 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci buffer[0] = reg; 738c2ecf20Sopenharmony_ci memcpy(&buffer[1], buf, len); 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci ret = i2c_transfer(adap, &msg, 1); 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci if (ret != 1) { 788c2ecf20Sopenharmony_ci dev_err(&client->dev, "i2c write error, reg = 0x%02x, status = %d\n", 798c2ecf20Sopenharmony_ci reg, ret); 808c2ecf20Sopenharmony_ci if (ret < 0) 818c2ecf20Sopenharmony_ci return ret; 828c2ecf20Sopenharmony_ci else 838c2ecf20Sopenharmony_ci return -EIO; 848c2ecf20Sopenharmony_ci } 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci dev_dbg(&s->client->dev, "addr=0x%04x, reg = 0x%02x, data = %*ph\n", 878c2ecf20Sopenharmony_ci client->addr, reg, len, buf); 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci return 0; 908c2ecf20Sopenharmony_ci} 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_cistatic int sp2_ci_op_cam(struct dvb_ca_en50221 *en50221, int slot, u8 acs, 938c2ecf20Sopenharmony_ci u8 read, int addr, u8 data) 948c2ecf20Sopenharmony_ci{ 958c2ecf20Sopenharmony_ci struct sp2 *s = en50221->data; 968c2ecf20Sopenharmony_ci u8 store; 978c2ecf20Sopenharmony_ci int mem, ret; 988c2ecf20Sopenharmony_ci int (*ci_op_cam)(void*, u8, int, u8, int*) = s->ci_control; 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci if (slot != 0) 1018c2ecf20Sopenharmony_ci return -EINVAL; 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci /* 1048c2ecf20Sopenharmony_ci * change module access type between IO space and attribute memory 1058c2ecf20Sopenharmony_ci * when needed 1068c2ecf20Sopenharmony_ci */ 1078c2ecf20Sopenharmony_ci if (s->module_access_type != acs) { 1088c2ecf20Sopenharmony_ci ret = sp2_read_i2c(s, 0x00, &store, 1); 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci if (ret) 1118c2ecf20Sopenharmony_ci return ret; 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci store &= ~(SP2_MOD_CTL_ACS1 | SP2_MOD_CTL_ACS0); 1148c2ecf20Sopenharmony_ci store |= acs; 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci ret = sp2_write_i2c(s, 0x00, &store, 1); 1178c2ecf20Sopenharmony_ci if (ret) 1188c2ecf20Sopenharmony_ci return ret; 1198c2ecf20Sopenharmony_ci } 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci s->module_access_type = acs; 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci /* implementation of ci_op_cam is device specific */ 1248c2ecf20Sopenharmony_ci if (ci_op_cam) { 1258c2ecf20Sopenharmony_ci ret = ci_op_cam(s->priv, read, addr, data, &mem); 1268c2ecf20Sopenharmony_ci } else { 1278c2ecf20Sopenharmony_ci dev_err(&s->client->dev, "callback not defined"); 1288c2ecf20Sopenharmony_ci return -EINVAL; 1298c2ecf20Sopenharmony_ci } 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci if (ret) 1328c2ecf20Sopenharmony_ci return ret; 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci dev_dbg(&s->client->dev, "%s: slot=%d, addr=0x%04x, %s, data=%x", 1358c2ecf20Sopenharmony_ci (read) ? "read" : "write", slot, addr, 1368c2ecf20Sopenharmony_ci (acs == SP2_CI_ATTR_ACS) ? "attr" : "io", 1378c2ecf20Sopenharmony_ci (read) ? mem : data); 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci if (read) 1408c2ecf20Sopenharmony_ci return mem; 1418c2ecf20Sopenharmony_ci else 1428c2ecf20Sopenharmony_ci return 0; 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci} 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ciint sp2_ci_read_attribute_mem(struct dvb_ca_en50221 *en50221, 1478c2ecf20Sopenharmony_ci int slot, int addr) 1488c2ecf20Sopenharmony_ci{ 1498c2ecf20Sopenharmony_ci return sp2_ci_op_cam(en50221, slot, SP2_CI_ATTR_ACS, 1508c2ecf20Sopenharmony_ci SP2_CI_RD, addr, 0); 1518c2ecf20Sopenharmony_ci} 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ciint sp2_ci_write_attribute_mem(struct dvb_ca_en50221 *en50221, 1548c2ecf20Sopenharmony_ci int slot, int addr, u8 data) 1558c2ecf20Sopenharmony_ci{ 1568c2ecf20Sopenharmony_ci return sp2_ci_op_cam(en50221, slot, SP2_CI_ATTR_ACS, 1578c2ecf20Sopenharmony_ci SP2_CI_WR, addr, data); 1588c2ecf20Sopenharmony_ci} 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ciint sp2_ci_read_cam_control(struct dvb_ca_en50221 *en50221, 1618c2ecf20Sopenharmony_ci int slot, u8 addr) 1628c2ecf20Sopenharmony_ci{ 1638c2ecf20Sopenharmony_ci return sp2_ci_op_cam(en50221, slot, SP2_CI_IO_ACS, 1648c2ecf20Sopenharmony_ci SP2_CI_RD, addr, 0); 1658c2ecf20Sopenharmony_ci} 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ciint sp2_ci_write_cam_control(struct dvb_ca_en50221 *en50221, 1688c2ecf20Sopenharmony_ci int slot, u8 addr, u8 data) 1698c2ecf20Sopenharmony_ci{ 1708c2ecf20Sopenharmony_ci return sp2_ci_op_cam(en50221, slot, SP2_CI_IO_ACS, 1718c2ecf20Sopenharmony_ci SP2_CI_WR, addr, data); 1728c2ecf20Sopenharmony_ci} 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ciint sp2_ci_slot_reset(struct dvb_ca_en50221 *en50221, int slot) 1758c2ecf20Sopenharmony_ci{ 1768c2ecf20Sopenharmony_ci struct sp2 *s = en50221->data; 1778c2ecf20Sopenharmony_ci u8 buf; 1788c2ecf20Sopenharmony_ci int ret; 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci dev_dbg(&s->client->dev, "slot: %d\n", slot); 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci if (slot != 0) 1838c2ecf20Sopenharmony_ci return -EINVAL; 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci /* RST on */ 1868c2ecf20Sopenharmony_ci buf = SP2_MOD_CTL_RST; 1878c2ecf20Sopenharmony_ci ret = sp2_write_i2c(s, 0x00, &buf, 1); 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci if (ret) 1908c2ecf20Sopenharmony_ci return ret; 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci usleep_range(500, 600); 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci /* RST off */ 1958c2ecf20Sopenharmony_ci buf = 0x00; 1968c2ecf20Sopenharmony_ci ret = sp2_write_i2c(s, 0x00, &buf, 1); 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci if (ret) 1998c2ecf20Sopenharmony_ci return ret; 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci msleep(1000); 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci return 0; 2048c2ecf20Sopenharmony_ci} 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ciint sp2_ci_slot_shutdown(struct dvb_ca_en50221 *en50221, int slot) 2078c2ecf20Sopenharmony_ci{ 2088c2ecf20Sopenharmony_ci struct sp2 *s = en50221->data; 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci dev_dbg(&s->client->dev, "slot:%d\n", slot); 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci /* not implemented */ 2138c2ecf20Sopenharmony_ci return 0; 2148c2ecf20Sopenharmony_ci} 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ciint sp2_ci_slot_ts_enable(struct dvb_ca_en50221 *en50221, int slot) 2178c2ecf20Sopenharmony_ci{ 2188c2ecf20Sopenharmony_ci struct sp2 *s = en50221->data; 2198c2ecf20Sopenharmony_ci u8 buf; 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci dev_dbg(&s->client->dev, "slot:%d\n", slot); 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci if (slot != 0) 2248c2ecf20Sopenharmony_ci return -EINVAL; 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci sp2_read_i2c(s, 0x00, &buf, 1); 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci /* disable bypass and enable TS */ 2298c2ecf20Sopenharmony_ci buf |= (SP2_MOD_CTL_TSOEN | SP2_MOD_CTL_TSIEN); 2308c2ecf20Sopenharmony_ci return sp2_write_i2c(s, 0, &buf, 1); 2318c2ecf20Sopenharmony_ci} 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ciint sp2_ci_poll_slot_status(struct dvb_ca_en50221 *en50221, 2348c2ecf20Sopenharmony_ci int slot, int open) 2358c2ecf20Sopenharmony_ci{ 2368c2ecf20Sopenharmony_ci struct sp2 *s = en50221->data; 2378c2ecf20Sopenharmony_ci u8 buf[2]; 2388c2ecf20Sopenharmony_ci int ret; 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci dev_dbg(&s->client->dev, "slot:%d open:%d\n", slot, open); 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci /* 2438c2ecf20Sopenharmony_ci * CAM module INSERT/REMOVE processing. Slow operation because of i2c 2448c2ecf20Sopenharmony_ci * transfers. Throttle read to one per sec. 2458c2ecf20Sopenharmony_ci */ 2468c2ecf20Sopenharmony_ci if (time_after(jiffies, s->next_status_checked_time)) { 2478c2ecf20Sopenharmony_ci ret = sp2_read_i2c(s, 0x00, buf, 1); 2488c2ecf20Sopenharmony_ci s->next_status_checked_time = jiffies + msecs_to_jiffies(1000); 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci if (ret) 2518c2ecf20Sopenharmony_ci return 0; 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci if (buf[0] & SP2_MOD_CTL_DET) 2548c2ecf20Sopenharmony_ci s->status = DVB_CA_EN50221_POLL_CAM_PRESENT | 2558c2ecf20Sopenharmony_ci DVB_CA_EN50221_POLL_CAM_READY; 2568c2ecf20Sopenharmony_ci else 2578c2ecf20Sopenharmony_ci s->status = 0; 2588c2ecf20Sopenharmony_ci } 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci return s->status; 2618c2ecf20Sopenharmony_ci} 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_cistatic int sp2_init(struct sp2 *s) 2648c2ecf20Sopenharmony_ci{ 2658c2ecf20Sopenharmony_ci int ret = 0; 2668c2ecf20Sopenharmony_ci u8 buf; 2678c2ecf20Sopenharmony_ci u8 cimax_init[34] = { 2688c2ecf20Sopenharmony_ci 0x00, /* module A control*/ 2698c2ecf20Sopenharmony_ci 0x00, /* auto select mask high A */ 2708c2ecf20Sopenharmony_ci 0x00, /* auto select mask low A */ 2718c2ecf20Sopenharmony_ci 0x00, /* auto select pattern high A */ 2728c2ecf20Sopenharmony_ci 0x00, /* auto select pattern low A */ 2738c2ecf20Sopenharmony_ci 0x44, /* memory access time A, 600 ns */ 2748c2ecf20Sopenharmony_ci 0x00, /* invert input A */ 2758c2ecf20Sopenharmony_ci 0x00, /* RFU */ 2768c2ecf20Sopenharmony_ci 0x00, /* RFU */ 2778c2ecf20Sopenharmony_ci 0x00, /* module B control*/ 2788c2ecf20Sopenharmony_ci 0x00, /* auto select mask high B */ 2798c2ecf20Sopenharmony_ci 0x00, /* auto select mask low B */ 2808c2ecf20Sopenharmony_ci 0x00, /* auto select pattern high B */ 2818c2ecf20Sopenharmony_ci 0x00, /* auto select pattern low B */ 2828c2ecf20Sopenharmony_ci 0x44, /* memory access time B, 600 ns */ 2838c2ecf20Sopenharmony_ci 0x00, /* invert input B */ 2848c2ecf20Sopenharmony_ci 0x00, /* RFU */ 2858c2ecf20Sopenharmony_ci 0x00, /* RFU */ 2868c2ecf20Sopenharmony_ci 0x00, /* auto select mask high Ext */ 2878c2ecf20Sopenharmony_ci 0x00, /* auto select mask low Ext */ 2888c2ecf20Sopenharmony_ci 0x00, /* auto select pattern high Ext */ 2898c2ecf20Sopenharmony_ci 0x00, /* auto select pattern low Ext */ 2908c2ecf20Sopenharmony_ci 0x00, /* RFU */ 2918c2ecf20Sopenharmony_ci 0x02, /* destination - module A */ 2928c2ecf20Sopenharmony_ci 0x01, /* power control reg, VCC power on */ 2938c2ecf20Sopenharmony_ci 0x00, /* RFU */ 2948c2ecf20Sopenharmony_ci 0x00, /* int status read only */ 2958c2ecf20Sopenharmony_ci 0x00, /* Interrupt Mask Register */ 2968c2ecf20Sopenharmony_ci 0x05, /* EXTINT=active-high, INT=push-pull */ 2978c2ecf20Sopenharmony_ci 0x00, /* USCG1 */ 2988c2ecf20Sopenharmony_ci 0x04, /* ack active low */ 2998c2ecf20Sopenharmony_ci 0x00, /* LOCK = 0 */ 3008c2ecf20Sopenharmony_ci 0x22, /* unknown */ 3018c2ecf20Sopenharmony_ci 0x00, /* synchronization? */ 3028c2ecf20Sopenharmony_ci }; 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci dev_dbg(&s->client->dev, "\n"); 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci s->ca.owner = THIS_MODULE; 3078c2ecf20Sopenharmony_ci s->ca.read_attribute_mem = sp2_ci_read_attribute_mem; 3088c2ecf20Sopenharmony_ci s->ca.write_attribute_mem = sp2_ci_write_attribute_mem; 3098c2ecf20Sopenharmony_ci s->ca.read_cam_control = sp2_ci_read_cam_control; 3108c2ecf20Sopenharmony_ci s->ca.write_cam_control = sp2_ci_write_cam_control; 3118c2ecf20Sopenharmony_ci s->ca.slot_reset = sp2_ci_slot_reset; 3128c2ecf20Sopenharmony_ci s->ca.slot_shutdown = sp2_ci_slot_shutdown; 3138c2ecf20Sopenharmony_ci s->ca.slot_ts_enable = sp2_ci_slot_ts_enable; 3148c2ecf20Sopenharmony_ci s->ca.poll_slot_status = sp2_ci_poll_slot_status; 3158c2ecf20Sopenharmony_ci s->ca.data = s; 3168c2ecf20Sopenharmony_ci s->module_access_type = 0; 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci /* initialize all regs */ 3198c2ecf20Sopenharmony_ci ret = sp2_write_i2c(s, 0x00, &cimax_init[0], 34); 3208c2ecf20Sopenharmony_ci if (ret) 3218c2ecf20Sopenharmony_ci goto err; 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci /* lock registers */ 3248c2ecf20Sopenharmony_ci buf = 1; 3258c2ecf20Sopenharmony_ci ret = sp2_write_i2c(s, 0x1f, &buf, 1); 3268c2ecf20Sopenharmony_ci if (ret) 3278c2ecf20Sopenharmony_ci goto err; 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci /* power on slots */ 3308c2ecf20Sopenharmony_ci ret = sp2_write_i2c(s, 0x18, &buf, 1); 3318c2ecf20Sopenharmony_ci if (ret) 3328c2ecf20Sopenharmony_ci goto err; 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci ret = dvb_ca_en50221_init(s->dvb_adap, &s->ca, 0, 1); 3358c2ecf20Sopenharmony_ci if (ret) 3368c2ecf20Sopenharmony_ci goto err; 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci return 0; 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_cierr: 3418c2ecf20Sopenharmony_ci dev_dbg(&s->client->dev, "init failed=%d\n", ret); 3428c2ecf20Sopenharmony_ci return ret; 3438c2ecf20Sopenharmony_ci} 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_cistatic int sp2_exit(struct i2c_client *client) 3468c2ecf20Sopenharmony_ci{ 3478c2ecf20Sopenharmony_ci struct sp2 *s; 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci dev_dbg(&client->dev, "\n"); 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci if (!client) 3528c2ecf20Sopenharmony_ci return 0; 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci s = i2c_get_clientdata(client); 3558c2ecf20Sopenharmony_ci if (!s) 3568c2ecf20Sopenharmony_ci return 0; 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci if (!s->ca.data) 3598c2ecf20Sopenharmony_ci return 0; 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci dvb_ca_en50221_release(&s->ca); 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci return 0; 3648c2ecf20Sopenharmony_ci} 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_cistatic int sp2_probe(struct i2c_client *client, 3678c2ecf20Sopenharmony_ci const struct i2c_device_id *id) 3688c2ecf20Sopenharmony_ci{ 3698c2ecf20Sopenharmony_ci struct sp2_config *cfg = client->dev.platform_data; 3708c2ecf20Sopenharmony_ci struct sp2 *s; 3718c2ecf20Sopenharmony_ci int ret; 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci dev_dbg(&client->dev, "\n"); 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ci s = kzalloc(sizeof(*s), GFP_KERNEL); 3768c2ecf20Sopenharmony_ci if (!s) { 3778c2ecf20Sopenharmony_ci ret = -ENOMEM; 3788c2ecf20Sopenharmony_ci goto err; 3798c2ecf20Sopenharmony_ci } 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci s->client = client; 3828c2ecf20Sopenharmony_ci s->dvb_adap = cfg->dvb_adap; 3838c2ecf20Sopenharmony_ci s->priv = cfg->priv; 3848c2ecf20Sopenharmony_ci s->ci_control = cfg->ci_control; 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci i2c_set_clientdata(client, s); 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci ret = sp2_init(s); 3898c2ecf20Sopenharmony_ci if (ret) 3908c2ecf20Sopenharmony_ci goto err; 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci dev_info(&s->client->dev, "CIMaX SP2 successfully attached\n"); 3938c2ecf20Sopenharmony_ci return 0; 3948c2ecf20Sopenharmony_cierr: 3958c2ecf20Sopenharmony_ci dev_dbg(&client->dev, "init failed=%d\n", ret); 3968c2ecf20Sopenharmony_ci kfree(s); 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci return ret; 3998c2ecf20Sopenharmony_ci} 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_cistatic int sp2_remove(struct i2c_client *client) 4028c2ecf20Sopenharmony_ci{ 4038c2ecf20Sopenharmony_ci struct sp2 *s = i2c_get_clientdata(client); 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci dev_dbg(&client->dev, "\n"); 4068c2ecf20Sopenharmony_ci sp2_exit(client); 4078c2ecf20Sopenharmony_ci kfree(s); 4088c2ecf20Sopenharmony_ci return 0; 4098c2ecf20Sopenharmony_ci} 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_cistatic const struct i2c_device_id sp2_id[] = { 4128c2ecf20Sopenharmony_ci {"sp2", 0}, 4138c2ecf20Sopenharmony_ci {} 4148c2ecf20Sopenharmony_ci}; 4158c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, sp2_id); 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_cistatic struct i2c_driver sp2_driver = { 4188c2ecf20Sopenharmony_ci .driver = { 4198c2ecf20Sopenharmony_ci .name = "sp2", 4208c2ecf20Sopenharmony_ci }, 4218c2ecf20Sopenharmony_ci .probe = sp2_probe, 4228c2ecf20Sopenharmony_ci .remove = sp2_remove, 4238c2ecf20Sopenharmony_ci .id_table = sp2_id, 4248c2ecf20Sopenharmony_ci}; 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_cimodule_i2c_driver(sp2_driver); 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("CIMaX SP2/HF CI driver"); 4298c2ecf20Sopenharmony_ciMODULE_AUTHOR("Olli Salonen <olli.salonen@iki.fi>"); 4308c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 431