18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Driver for the ST STV6111 tuner 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2014 Digital Devices GmbH 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * This program is free software; you can redistribute it and/or 88c2ecf20Sopenharmony_ci * modify it under the terms of the GNU General Public License 98c2ecf20Sopenharmony_ci * version 2 only, as published by the Free Software Foundation. 108c2ecf20Sopenharmony_ci * 118c2ecf20Sopenharmony_ci * This program is distributed in the hope that it will be useful, 128c2ecf20Sopenharmony_ci * but WITHOUT ANY WARRANTY; without even the implied warranty of 138c2ecf20Sopenharmony_ci * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 148c2ecf20Sopenharmony_ci * GNU General Public License for more details. 158c2ecf20Sopenharmony_ci */ 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#include <linux/kernel.h> 188c2ecf20Sopenharmony_ci#include <linux/module.h> 198c2ecf20Sopenharmony_ci#include <linux/moduleparam.h> 208c2ecf20Sopenharmony_ci#include <linux/init.h> 218c2ecf20Sopenharmony_ci#include <linux/delay.h> 228c2ecf20Sopenharmony_ci#include <linux/firmware.h> 238c2ecf20Sopenharmony_ci#include <linux/i2c.h> 248c2ecf20Sopenharmony_ci#include <asm/div64.h> 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci#include "stv6111.h" 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci#include <media/dvb_frontend.h> 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_cistruct stv { 318c2ecf20Sopenharmony_ci struct i2c_adapter *i2c; 328c2ecf20Sopenharmony_ci u8 adr; 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci u8 reg[11]; 358c2ecf20Sopenharmony_ci u32 ref_freq; 368c2ecf20Sopenharmony_ci u32 frequency; 378c2ecf20Sopenharmony_ci}; 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_cistruct slookup { 408c2ecf20Sopenharmony_ci s16 value; 418c2ecf20Sopenharmony_ci u16 reg_value; 428c2ecf20Sopenharmony_ci}; 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_cistatic const struct slookup lnagain_nf_lookup[] = { 458c2ecf20Sopenharmony_ci /* Gain *100dB // Reg */ 468c2ecf20Sopenharmony_ci { 2572, 0 }, 478c2ecf20Sopenharmony_ci { 2575, 1 }, 488c2ecf20Sopenharmony_ci { 2580, 2 }, 498c2ecf20Sopenharmony_ci { 2588, 3 }, 508c2ecf20Sopenharmony_ci { 2596, 4 }, 518c2ecf20Sopenharmony_ci { 2611, 5 }, 528c2ecf20Sopenharmony_ci { 2633, 6 }, 538c2ecf20Sopenharmony_ci { 2664, 7 }, 548c2ecf20Sopenharmony_ci { 2701, 8 }, 558c2ecf20Sopenharmony_ci { 2753, 9 }, 568c2ecf20Sopenharmony_ci { 2816, 10 }, 578c2ecf20Sopenharmony_ci { 2902, 11 }, 588c2ecf20Sopenharmony_ci { 2995, 12 }, 598c2ecf20Sopenharmony_ci { 3104, 13 }, 608c2ecf20Sopenharmony_ci { 3215, 14 }, 618c2ecf20Sopenharmony_ci { 3337, 15 }, 628c2ecf20Sopenharmony_ci { 3492, 16 }, 638c2ecf20Sopenharmony_ci { 3614, 17 }, 648c2ecf20Sopenharmony_ci { 3731, 18 }, 658c2ecf20Sopenharmony_ci { 3861, 19 }, 668c2ecf20Sopenharmony_ci { 3988, 20 }, 678c2ecf20Sopenharmony_ci { 4124, 21 }, 688c2ecf20Sopenharmony_ci { 4253, 22 }, 698c2ecf20Sopenharmony_ci { 4386, 23 }, 708c2ecf20Sopenharmony_ci { 4505, 24 }, 718c2ecf20Sopenharmony_ci { 4623, 25 }, 728c2ecf20Sopenharmony_ci { 4726, 26 }, 738c2ecf20Sopenharmony_ci { 4821, 27 }, 748c2ecf20Sopenharmony_ci { 4903, 28 }, 758c2ecf20Sopenharmony_ci { 4979, 29 }, 768c2ecf20Sopenharmony_ci { 5045, 30 }, 778c2ecf20Sopenharmony_ci { 5102, 31 } 788c2ecf20Sopenharmony_ci}; 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_cistatic const struct slookup lnagain_iip3_lookup[] = { 818c2ecf20Sopenharmony_ci /* Gain *100dB // reg */ 828c2ecf20Sopenharmony_ci { 1548, 0 }, 838c2ecf20Sopenharmony_ci { 1552, 1 }, 848c2ecf20Sopenharmony_ci { 1569, 2 }, 858c2ecf20Sopenharmony_ci { 1565, 3 }, 868c2ecf20Sopenharmony_ci { 1577, 4 }, 878c2ecf20Sopenharmony_ci { 1594, 5 }, 888c2ecf20Sopenharmony_ci { 1627, 6 }, 898c2ecf20Sopenharmony_ci { 1656, 7 }, 908c2ecf20Sopenharmony_ci { 1700, 8 }, 918c2ecf20Sopenharmony_ci { 1748, 9 }, 928c2ecf20Sopenharmony_ci { 1805, 10 }, 938c2ecf20Sopenharmony_ci { 1896, 11 }, 948c2ecf20Sopenharmony_ci { 1995, 12 }, 958c2ecf20Sopenharmony_ci { 2113, 13 }, 968c2ecf20Sopenharmony_ci { 2233, 14 }, 978c2ecf20Sopenharmony_ci { 2366, 15 }, 988c2ecf20Sopenharmony_ci { 2543, 16 }, 998c2ecf20Sopenharmony_ci { 2687, 17 }, 1008c2ecf20Sopenharmony_ci { 2842, 18 }, 1018c2ecf20Sopenharmony_ci { 2999, 19 }, 1028c2ecf20Sopenharmony_ci { 3167, 20 }, 1038c2ecf20Sopenharmony_ci { 3342, 21 }, 1048c2ecf20Sopenharmony_ci { 3507, 22 }, 1058c2ecf20Sopenharmony_ci { 3679, 23 }, 1068c2ecf20Sopenharmony_ci { 3827, 24 }, 1078c2ecf20Sopenharmony_ci { 3970, 25 }, 1088c2ecf20Sopenharmony_ci { 4094, 26 }, 1098c2ecf20Sopenharmony_ci { 4210, 27 }, 1108c2ecf20Sopenharmony_ci { 4308, 28 }, 1118c2ecf20Sopenharmony_ci { 4396, 29 }, 1128c2ecf20Sopenharmony_ci { 4468, 30 }, 1138c2ecf20Sopenharmony_ci { 4535, 31 } 1148c2ecf20Sopenharmony_ci}; 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_cistatic const struct slookup gain_rfagc_lookup[] = { 1178c2ecf20Sopenharmony_ci /* Gain *100dB // reg */ 1188c2ecf20Sopenharmony_ci { 4870, 0x3000 }, 1198c2ecf20Sopenharmony_ci { 4850, 0x3C00 }, 1208c2ecf20Sopenharmony_ci { 4800, 0x4500 }, 1218c2ecf20Sopenharmony_ci { 4750, 0x4800 }, 1228c2ecf20Sopenharmony_ci { 4700, 0x4B00 }, 1238c2ecf20Sopenharmony_ci { 4650, 0x4D00 }, 1248c2ecf20Sopenharmony_ci { 4600, 0x4F00 }, 1258c2ecf20Sopenharmony_ci { 4550, 0x5100 }, 1268c2ecf20Sopenharmony_ci { 4500, 0x5200 }, 1278c2ecf20Sopenharmony_ci { 4420, 0x5500 }, 1288c2ecf20Sopenharmony_ci { 4316, 0x5800 }, 1298c2ecf20Sopenharmony_ci { 4200, 0x5B00 }, 1308c2ecf20Sopenharmony_ci { 4119, 0x5D00 }, 1318c2ecf20Sopenharmony_ci { 3999, 0x6000 }, 1328c2ecf20Sopenharmony_ci { 3950, 0x6100 }, 1338c2ecf20Sopenharmony_ci { 3876, 0x6300 }, 1348c2ecf20Sopenharmony_ci { 3755, 0x6600 }, 1358c2ecf20Sopenharmony_ci { 3641, 0x6900 }, 1368c2ecf20Sopenharmony_ci { 3567, 0x6B00 }, 1378c2ecf20Sopenharmony_ci { 3425, 0x6F00 }, 1388c2ecf20Sopenharmony_ci { 3350, 0x7100 }, 1398c2ecf20Sopenharmony_ci { 3236, 0x7400 }, 1408c2ecf20Sopenharmony_ci { 3118, 0x7700 }, 1418c2ecf20Sopenharmony_ci { 3004, 0x7A00 }, 1428c2ecf20Sopenharmony_ci { 2917, 0x7C00 }, 1438c2ecf20Sopenharmony_ci { 2776, 0x7F00 }, 1448c2ecf20Sopenharmony_ci { 2635, 0x8200 }, 1458c2ecf20Sopenharmony_ci { 2516, 0x8500 }, 1468c2ecf20Sopenharmony_ci { 2406, 0x8800 }, 1478c2ecf20Sopenharmony_ci { 2290, 0x8B00 }, 1488c2ecf20Sopenharmony_ci { 2170, 0x8E00 }, 1498c2ecf20Sopenharmony_ci { 2073, 0x9100 }, 1508c2ecf20Sopenharmony_ci { 1949, 0x9400 }, 1518c2ecf20Sopenharmony_ci { 1836, 0x9700 }, 1528c2ecf20Sopenharmony_ci { 1712, 0x9A00 }, 1538c2ecf20Sopenharmony_ci { 1631, 0x9C00 }, 1548c2ecf20Sopenharmony_ci { 1515, 0x9F00 }, 1558c2ecf20Sopenharmony_ci { 1400, 0xA200 }, 1568c2ecf20Sopenharmony_ci { 1323, 0xA400 }, 1578c2ecf20Sopenharmony_ci { 1203, 0xA700 }, 1588c2ecf20Sopenharmony_ci { 1091, 0xAA00 }, 1598c2ecf20Sopenharmony_ci { 1011, 0xAC00 }, 1608c2ecf20Sopenharmony_ci { 904, 0xAF00 }, 1618c2ecf20Sopenharmony_ci { 787, 0xB200 }, 1628c2ecf20Sopenharmony_ci { 685, 0xB500 }, 1638c2ecf20Sopenharmony_ci { 571, 0xB800 }, 1648c2ecf20Sopenharmony_ci { 464, 0xBB00 }, 1658c2ecf20Sopenharmony_ci { 374, 0xBE00 }, 1668c2ecf20Sopenharmony_ci { 275, 0xC200 }, 1678c2ecf20Sopenharmony_ci { 181, 0xC600 }, 1688c2ecf20Sopenharmony_ci { 102, 0xCC00 }, 1698c2ecf20Sopenharmony_ci { 49, 0xD900 } 1708c2ecf20Sopenharmony_ci}; 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci/* 1738c2ecf20Sopenharmony_ci * This table is 6 dB too low comapred to the others (probably created with 1748c2ecf20Sopenharmony_ci * a different BB_MAG setting) 1758c2ecf20Sopenharmony_ci */ 1768c2ecf20Sopenharmony_cistatic const struct slookup gain_channel_agc_nf_lookup[] = { 1778c2ecf20Sopenharmony_ci /* Gain *100dB // reg */ 1788c2ecf20Sopenharmony_ci { 7082, 0x3000 }, 1798c2ecf20Sopenharmony_ci { 7052, 0x4000 }, 1808c2ecf20Sopenharmony_ci { 7007, 0x4600 }, 1818c2ecf20Sopenharmony_ci { 6954, 0x4A00 }, 1828c2ecf20Sopenharmony_ci { 6909, 0x4D00 }, 1838c2ecf20Sopenharmony_ci { 6833, 0x5100 }, 1848c2ecf20Sopenharmony_ci { 6753, 0x5400 }, 1858c2ecf20Sopenharmony_ci { 6659, 0x5700 }, 1868c2ecf20Sopenharmony_ci { 6561, 0x5A00 }, 1878c2ecf20Sopenharmony_ci { 6472, 0x5C00 }, 1888c2ecf20Sopenharmony_ci { 6366, 0x5F00 }, 1898c2ecf20Sopenharmony_ci { 6259, 0x6100 }, 1908c2ecf20Sopenharmony_ci { 6151, 0x6400 }, 1918c2ecf20Sopenharmony_ci { 6026, 0x6700 }, 1928c2ecf20Sopenharmony_ci { 5920, 0x6900 }, 1938c2ecf20Sopenharmony_ci { 5835, 0x6B00 }, 1948c2ecf20Sopenharmony_ci { 5770, 0x6C00 }, 1958c2ecf20Sopenharmony_ci { 5681, 0x6E00 }, 1968c2ecf20Sopenharmony_ci { 5596, 0x7000 }, 1978c2ecf20Sopenharmony_ci { 5503, 0x7200 }, 1988c2ecf20Sopenharmony_ci { 5429, 0x7300 }, 1998c2ecf20Sopenharmony_ci { 5319, 0x7500 }, 2008c2ecf20Sopenharmony_ci { 5220, 0x7700 }, 2018c2ecf20Sopenharmony_ci { 5111, 0x7900 }, 2028c2ecf20Sopenharmony_ci { 4983, 0x7B00 }, 2038c2ecf20Sopenharmony_ci { 4876, 0x7D00 }, 2048c2ecf20Sopenharmony_ci { 4755, 0x7F00 }, 2058c2ecf20Sopenharmony_ci { 4635, 0x8100 }, 2068c2ecf20Sopenharmony_ci { 4499, 0x8300 }, 2078c2ecf20Sopenharmony_ci { 4405, 0x8500 }, 2088c2ecf20Sopenharmony_ci { 4323, 0x8600 }, 2098c2ecf20Sopenharmony_ci { 4233, 0x8800 }, 2108c2ecf20Sopenharmony_ci { 4156, 0x8A00 }, 2118c2ecf20Sopenharmony_ci { 4038, 0x8C00 }, 2128c2ecf20Sopenharmony_ci { 3935, 0x8E00 }, 2138c2ecf20Sopenharmony_ci { 3823, 0x9000 }, 2148c2ecf20Sopenharmony_ci { 3712, 0x9200 }, 2158c2ecf20Sopenharmony_ci { 3601, 0x9500 }, 2168c2ecf20Sopenharmony_ci { 3511, 0x9700 }, 2178c2ecf20Sopenharmony_ci { 3413, 0x9900 }, 2188c2ecf20Sopenharmony_ci { 3309, 0x9B00 }, 2198c2ecf20Sopenharmony_ci { 3213, 0x9D00 }, 2208c2ecf20Sopenharmony_ci { 3088, 0x9F00 }, 2218c2ecf20Sopenharmony_ci { 2992, 0xA100 }, 2228c2ecf20Sopenharmony_ci { 2878, 0xA400 }, 2238c2ecf20Sopenharmony_ci { 2769, 0xA700 }, 2248c2ecf20Sopenharmony_ci { 2645, 0xAA00 }, 2258c2ecf20Sopenharmony_ci { 2538, 0xAD00 }, 2268c2ecf20Sopenharmony_ci { 2441, 0xB000 }, 2278c2ecf20Sopenharmony_ci { 2350, 0xB600 }, 2288c2ecf20Sopenharmony_ci { 2237, 0xBA00 }, 2298c2ecf20Sopenharmony_ci { 2137, 0xBF00 }, 2308c2ecf20Sopenharmony_ci { 2039, 0xC500 }, 2318c2ecf20Sopenharmony_ci { 1938, 0xDF00 }, 2328c2ecf20Sopenharmony_ci { 1927, 0xFF00 } 2338c2ecf20Sopenharmony_ci}; 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_cistatic const struct slookup gain_channel_agc_iip3_lookup[] = { 2368c2ecf20Sopenharmony_ci /* Gain *100dB // reg */ 2378c2ecf20Sopenharmony_ci { 7070, 0x3000 }, 2388c2ecf20Sopenharmony_ci { 7028, 0x4000 }, 2398c2ecf20Sopenharmony_ci { 7019, 0x4600 }, 2408c2ecf20Sopenharmony_ci { 6900, 0x4A00 }, 2418c2ecf20Sopenharmony_ci { 6811, 0x4D00 }, 2428c2ecf20Sopenharmony_ci { 6763, 0x5100 }, 2438c2ecf20Sopenharmony_ci { 6690, 0x5400 }, 2448c2ecf20Sopenharmony_ci { 6644, 0x5700 }, 2458c2ecf20Sopenharmony_ci { 6617, 0x5A00 }, 2468c2ecf20Sopenharmony_ci { 6598, 0x5C00 }, 2478c2ecf20Sopenharmony_ci { 6462, 0x5F00 }, 2488c2ecf20Sopenharmony_ci { 6348, 0x6100 }, 2498c2ecf20Sopenharmony_ci { 6197, 0x6400 }, 2508c2ecf20Sopenharmony_ci { 6154, 0x6700 }, 2518c2ecf20Sopenharmony_ci { 6098, 0x6900 }, 2528c2ecf20Sopenharmony_ci { 5893, 0x6B00 }, 2538c2ecf20Sopenharmony_ci { 5812, 0x6C00 }, 2548c2ecf20Sopenharmony_ci { 5773, 0x6E00 }, 2558c2ecf20Sopenharmony_ci { 5723, 0x7000 }, 2568c2ecf20Sopenharmony_ci { 5661, 0x7200 }, 2578c2ecf20Sopenharmony_ci { 5579, 0x7300 }, 2588c2ecf20Sopenharmony_ci { 5460, 0x7500 }, 2598c2ecf20Sopenharmony_ci { 5308, 0x7700 }, 2608c2ecf20Sopenharmony_ci { 5099, 0x7900 }, 2618c2ecf20Sopenharmony_ci { 4910, 0x7B00 }, 2628c2ecf20Sopenharmony_ci { 4800, 0x7D00 }, 2638c2ecf20Sopenharmony_ci { 4785, 0x7F00 }, 2648c2ecf20Sopenharmony_ci { 4635, 0x8100 }, 2658c2ecf20Sopenharmony_ci { 4466, 0x8300 }, 2668c2ecf20Sopenharmony_ci { 4314, 0x8500 }, 2678c2ecf20Sopenharmony_ci { 4295, 0x8600 }, 2688c2ecf20Sopenharmony_ci { 4144, 0x8800 }, 2698c2ecf20Sopenharmony_ci { 3920, 0x8A00 }, 2708c2ecf20Sopenharmony_ci { 3889, 0x8C00 }, 2718c2ecf20Sopenharmony_ci { 3771, 0x8E00 }, 2728c2ecf20Sopenharmony_ci { 3655, 0x9000 }, 2738c2ecf20Sopenharmony_ci { 3446, 0x9200 }, 2748c2ecf20Sopenharmony_ci { 3298, 0x9500 }, 2758c2ecf20Sopenharmony_ci { 3083, 0x9700 }, 2768c2ecf20Sopenharmony_ci { 3015, 0x9900 }, 2778c2ecf20Sopenharmony_ci { 2833, 0x9B00 }, 2788c2ecf20Sopenharmony_ci { 2746, 0x9D00 }, 2798c2ecf20Sopenharmony_ci { 2632, 0x9F00 }, 2808c2ecf20Sopenharmony_ci { 2598, 0xA100 }, 2818c2ecf20Sopenharmony_ci { 2480, 0xA400 }, 2828c2ecf20Sopenharmony_ci { 2236, 0xA700 }, 2838c2ecf20Sopenharmony_ci { 2171, 0xAA00 }, 2848c2ecf20Sopenharmony_ci { 2060, 0xAD00 }, 2858c2ecf20Sopenharmony_ci { 1999, 0xB000 }, 2868c2ecf20Sopenharmony_ci { 1974, 0xB600 }, 2878c2ecf20Sopenharmony_ci { 1820, 0xBA00 }, 2888c2ecf20Sopenharmony_ci { 1741, 0xBF00 }, 2898c2ecf20Sopenharmony_ci { 1655, 0xC500 }, 2908c2ecf20Sopenharmony_ci { 1444, 0xDF00 }, 2918c2ecf20Sopenharmony_ci { 1325, 0xFF00 }, 2928c2ecf20Sopenharmony_ci}; 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_cistatic inline u32 muldiv32(u32 a, u32 b, u32 c) 2958c2ecf20Sopenharmony_ci{ 2968c2ecf20Sopenharmony_ci u64 tmp64; 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci tmp64 = (u64)a * (u64)b; 2998c2ecf20Sopenharmony_ci do_div(tmp64, c); 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci return (u32)tmp64; 3028c2ecf20Sopenharmony_ci} 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_cistatic int i2c_read(struct i2c_adapter *adap, 3058c2ecf20Sopenharmony_ci u8 adr, u8 *msg, int len, u8 *answ, int alen) 3068c2ecf20Sopenharmony_ci{ 3078c2ecf20Sopenharmony_ci struct i2c_msg msgs[2] = { { .addr = adr, .flags = 0, 3088c2ecf20Sopenharmony_ci .buf = msg, .len = len}, 3098c2ecf20Sopenharmony_ci { .addr = adr, .flags = I2C_M_RD, 3108c2ecf20Sopenharmony_ci .buf = answ, .len = alen } }; 3118c2ecf20Sopenharmony_ci if (i2c_transfer(adap, msgs, 2) != 2) { 3128c2ecf20Sopenharmony_ci dev_err(&adap->dev, "i2c read error\n"); 3138c2ecf20Sopenharmony_ci return -EIO; 3148c2ecf20Sopenharmony_ci } 3158c2ecf20Sopenharmony_ci return 0; 3168c2ecf20Sopenharmony_ci} 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_cistatic int i2c_write(struct i2c_adapter *adap, u8 adr, u8 *data, int len) 3198c2ecf20Sopenharmony_ci{ 3208c2ecf20Sopenharmony_ci struct i2c_msg msg = {.addr = adr, .flags = 0, 3218c2ecf20Sopenharmony_ci .buf = data, .len = len}; 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci if (i2c_transfer(adap, &msg, 1) != 1) { 3248c2ecf20Sopenharmony_ci dev_err(&adap->dev, "i2c write error\n"); 3258c2ecf20Sopenharmony_ci return -EIO; 3268c2ecf20Sopenharmony_ci } 3278c2ecf20Sopenharmony_ci return 0; 3288c2ecf20Sopenharmony_ci} 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_cistatic int write_regs(struct stv *state, int reg, int len) 3318c2ecf20Sopenharmony_ci{ 3328c2ecf20Sopenharmony_ci u8 d[12]; 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci memcpy(&d[1], &state->reg[reg], len); 3358c2ecf20Sopenharmony_ci d[0] = reg; 3368c2ecf20Sopenharmony_ci return i2c_write(state->i2c, state->adr, d, len + 1); 3378c2ecf20Sopenharmony_ci} 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_cistatic int write_reg(struct stv *state, u8 reg, u8 val) 3408c2ecf20Sopenharmony_ci{ 3418c2ecf20Sopenharmony_ci u8 d[2] = {reg, val}; 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci return i2c_write(state->i2c, state->adr, d, 2); 3448c2ecf20Sopenharmony_ci} 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_cistatic int read_reg(struct stv *state, u8 reg, u8 *val) 3478c2ecf20Sopenharmony_ci{ 3488c2ecf20Sopenharmony_ci return i2c_read(state->i2c, state->adr, ®, 1, val, 1); 3498c2ecf20Sopenharmony_ci} 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_cistatic int wait_for_call_done(struct stv *state, u8 mask) 3528c2ecf20Sopenharmony_ci{ 3538c2ecf20Sopenharmony_ci int status = 0; 3548c2ecf20Sopenharmony_ci u32 lock_retry_count = 10; 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci while (lock_retry_count > 0) { 3578c2ecf20Sopenharmony_ci u8 regval; 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci status = read_reg(state, 9, ®val); 3608c2ecf20Sopenharmony_ci if (status < 0) 3618c2ecf20Sopenharmony_ci return status; 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci if ((regval & mask) == 0) 3648c2ecf20Sopenharmony_ci break; 3658c2ecf20Sopenharmony_ci usleep_range(4000, 6000); 3668c2ecf20Sopenharmony_ci lock_retry_count -= 1; 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci status = -EIO; 3698c2ecf20Sopenharmony_ci } 3708c2ecf20Sopenharmony_ci return status; 3718c2ecf20Sopenharmony_ci} 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_cistatic void init_state(struct stv *state) 3748c2ecf20Sopenharmony_ci{ 3758c2ecf20Sopenharmony_ci u32 clkdiv = 0; 3768c2ecf20Sopenharmony_ci u32 agcmode = 0; 3778c2ecf20Sopenharmony_ci u32 agcref = 2; 3788c2ecf20Sopenharmony_ci u32 agcset = 0xffffffff; 3798c2ecf20Sopenharmony_ci u32 bbmode = 0xffffffff; 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci state->reg[0] = 0x08; 3828c2ecf20Sopenharmony_ci state->reg[1] = 0x41; 3838c2ecf20Sopenharmony_ci state->reg[2] = 0x8f; 3848c2ecf20Sopenharmony_ci state->reg[3] = 0x00; 3858c2ecf20Sopenharmony_ci state->reg[4] = 0xce; 3868c2ecf20Sopenharmony_ci state->reg[5] = 0x54; 3878c2ecf20Sopenharmony_ci state->reg[6] = 0x55; 3888c2ecf20Sopenharmony_ci state->reg[7] = 0x45; 3898c2ecf20Sopenharmony_ci state->reg[8] = 0x46; 3908c2ecf20Sopenharmony_ci state->reg[9] = 0xbd; 3918c2ecf20Sopenharmony_ci state->reg[10] = 0x11; 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci state->ref_freq = 16000; 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci if (clkdiv <= 3) 3968c2ecf20Sopenharmony_ci state->reg[0x00] |= (clkdiv & 0x03); 3978c2ecf20Sopenharmony_ci if (agcmode <= 3) { 3988c2ecf20Sopenharmony_ci state->reg[0x03] |= (agcmode << 5); 3998c2ecf20Sopenharmony_ci if (agcmode == 0x01) 4008c2ecf20Sopenharmony_ci state->reg[0x01] |= 0x30; 4018c2ecf20Sopenharmony_ci } 4028c2ecf20Sopenharmony_ci if (bbmode <= 3) 4038c2ecf20Sopenharmony_ci state->reg[0x01] = (state->reg[0x01] & ~0x30) | (bbmode << 4); 4048c2ecf20Sopenharmony_ci if (agcref <= 7) 4058c2ecf20Sopenharmony_ci state->reg[0x03] |= agcref; 4068c2ecf20Sopenharmony_ci if (agcset <= 31) 4078c2ecf20Sopenharmony_ci state->reg[0x02] = (state->reg[0x02] & ~0x1F) | agcset | 0x40; 4088c2ecf20Sopenharmony_ci} 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_cistatic int attach_init(struct stv *state) 4118c2ecf20Sopenharmony_ci{ 4128c2ecf20Sopenharmony_ci if (write_regs(state, 0, 11)) 4138c2ecf20Sopenharmony_ci return -ENODEV; 4148c2ecf20Sopenharmony_ci return 0; 4158c2ecf20Sopenharmony_ci} 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_cistatic void release(struct dvb_frontend *fe) 4188c2ecf20Sopenharmony_ci{ 4198c2ecf20Sopenharmony_ci kfree(fe->tuner_priv); 4208c2ecf20Sopenharmony_ci fe->tuner_priv = NULL; 4218c2ecf20Sopenharmony_ci} 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_cistatic int set_bandwidth(struct dvb_frontend *fe, u32 cutoff_frequency) 4248c2ecf20Sopenharmony_ci{ 4258c2ecf20Sopenharmony_ci struct stv *state = fe->tuner_priv; 4268c2ecf20Sopenharmony_ci u32 index = (cutoff_frequency + 999999) / 1000000; 4278c2ecf20Sopenharmony_ci int stat = 0; 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci if (index < 6) 4308c2ecf20Sopenharmony_ci index = 6; 4318c2ecf20Sopenharmony_ci if (index > 50) 4328c2ecf20Sopenharmony_ci index = 50; 4338c2ecf20Sopenharmony_ci if ((state->reg[0x08] & ~0xFC) == ((index - 6) << 2)) 4348c2ecf20Sopenharmony_ci return 0; 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci state->reg[0x08] = (state->reg[0x08] & ~0xFC) | ((index - 6) << 2); 4378c2ecf20Sopenharmony_ci state->reg[0x09] = (state->reg[0x09] & ~0x0C) | 0x08; 4388c2ecf20Sopenharmony_ci if (fe->ops.i2c_gate_ctrl) 4398c2ecf20Sopenharmony_ci stat = fe->ops.i2c_gate_ctrl(fe, 1); 4408c2ecf20Sopenharmony_ci if (!stat) { 4418c2ecf20Sopenharmony_ci write_regs(state, 0x08, 2); 4428c2ecf20Sopenharmony_ci wait_for_call_done(state, 0x08); 4438c2ecf20Sopenharmony_ci } 4448c2ecf20Sopenharmony_ci if (fe->ops.i2c_gate_ctrl && !stat) 4458c2ecf20Sopenharmony_ci fe->ops.i2c_gate_ctrl(fe, 0); 4468c2ecf20Sopenharmony_ci return stat; 4478c2ecf20Sopenharmony_ci} 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_cistatic int set_lof(struct stv *state, u32 local_frequency, u32 cutoff_frequency) 4508c2ecf20Sopenharmony_ci{ 4518c2ecf20Sopenharmony_ci u32 index = (cutoff_frequency + 999999) / 1000000; 4528c2ecf20Sopenharmony_ci u32 frequency = (local_frequency + 500) / 1000; 4538c2ecf20Sopenharmony_ci u32 p = 1, psel = 0, fvco, div, frac; 4548c2ecf20Sopenharmony_ci u8 icp, tmp; 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci if (index < 6) 4578c2ecf20Sopenharmony_ci index = 6; 4588c2ecf20Sopenharmony_ci if (index > 50) 4598c2ecf20Sopenharmony_ci index = 50; 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_ci if (frequency <= 1300000) { 4628c2ecf20Sopenharmony_ci p = 4; 4638c2ecf20Sopenharmony_ci psel = 1; 4648c2ecf20Sopenharmony_ci } else { 4658c2ecf20Sopenharmony_ci p = 2; 4668c2ecf20Sopenharmony_ci psel = 0; 4678c2ecf20Sopenharmony_ci } 4688c2ecf20Sopenharmony_ci fvco = frequency * p; 4698c2ecf20Sopenharmony_ci div = fvco / state->ref_freq; 4708c2ecf20Sopenharmony_ci frac = fvco % state->ref_freq; 4718c2ecf20Sopenharmony_ci frac = muldiv32(frac, 0x40000, state->ref_freq); 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci icp = 0; 4748c2ecf20Sopenharmony_ci if (fvco < 2700000) 4758c2ecf20Sopenharmony_ci icp = 0; 4768c2ecf20Sopenharmony_ci else if (fvco < 2950000) 4778c2ecf20Sopenharmony_ci icp = 1; 4788c2ecf20Sopenharmony_ci else if (fvco < 3300000) 4798c2ecf20Sopenharmony_ci icp = 2; 4808c2ecf20Sopenharmony_ci else if (fvco < 3700000) 4818c2ecf20Sopenharmony_ci icp = 3; 4828c2ecf20Sopenharmony_ci else if (fvco < 4200000) 4838c2ecf20Sopenharmony_ci icp = 5; 4848c2ecf20Sopenharmony_ci else if (fvco < 4800000) 4858c2ecf20Sopenharmony_ci icp = 6; 4868c2ecf20Sopenharmony_ci else 4878c2ecf20Sopenharmony_ci icp = 7; 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci state->reg[0x02] |= 0x80; /* LNA IIP3 Mode */ 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_ci state->reg[0x03] = (state->reg[0x03] & ~0x80) | (psel << 7); 4928c2ecf20Sopenharmony_ci state->reg[0x04] = (div & 0xFF); 4938c2ecf20Sopenharmony_ci state->reg[0x05] = (((div >> 8) & 0x01) | ((frac & 0x7F) << 1)) & 0xff; 4948c2ecf20Sopenharmony_ci state->reg[0x06] = ((frac >> 7) & 0xFF); 4958c2ecf20Sopenharmony_ci state->reg[0x07] = (state->reg[0x07] & ~0x07) | ((frac >> 15) & 0x07); 4968c2ecf20Sopenharmony_ci state->reg[0x07] = (state->reg[0x07] & ~0xE0) | (icp << 5); 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci state->reg[0x08] = (state->reg[0x08] & ~0xFC) | ((index - 6) << 2); 4998c2ecf20Sopenharmony_ci /* Start cal vco,CF */ 5008c2ecf20Sopenharmony_ci state->reg[0x09] = (state->reg[0x09] & ~0x0C) | 0x0C; 5018c2ecf20Sopenharmony_ci write_regs(state, 2, 8); 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_ci wait_for_call_done(state, 0x0C); 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_ci usleep_range(10000, 12000); 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_ci read_reg(state, 0x03, &tmp); 5088c2ecf20Sopenharmony_ci if (tmp & 0x10) { 5098c2ecf20Sopenharmony_ci state->reg[0x02] &= ~0x80; /* LNA NF Mode */ 5108c2ecf20Sopenharmony_ci write_regs(state, 2, 1); 5118c2ecf20Sopenharmony_ci } 5128c2ecf20Sopenharmony_ci read_reg(state, 0x08, &tmp); 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_ci state->frequency = frequency; 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_ci return 0; 5178c2ecf20Sopenharmony_ci} 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_cistatic int set_params(struct dvb_frontend *fe) 5208c2ecf20Sopenharmony_ci{ 5218c2ecf20Sopenharmony_ci struct stv *state = fe->tuner_priv; 5228c2ecf20Sopenharmony_ci struct dtv_frontend_properties *p = &fe->dtv_property_cache; 5238c2ecf20Sopenharmony_ci u32 freq, cutoff; 5248c2ecf20Sopenharmony_ci int stat = 0; 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_ci if (p->delivery_system != SYS_DVBS && p->delivery_system != SYS_DVBS2) 5278c2ecf20Sopenharmony_ci return -EINVAL; 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_ci freq = p->frequency * 1000; 5308c2ecf20Sopenharmony_ci cutoff = 5000000 + muldiv32(p->symbol_rate, 135, 200); 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_ci if (fe->ops.i2c_gate_ctrl) 5338c2ecf20Sopenharmony_ci stat = fe->ops.i2c_gate_ctrl(fe, 1); 5348c2ecf20Sopenharmony_ci if (!stat) 5358c2ecf20Sopenharmony_ci set_lof(state, freq, cutoff); 5368c2ecf20Sopenharmony_ci if (fe->ops.i2c_gate_ctrl && !stat) 5378c2ecf20Sopenharmony_ci fe->ops.i2c_gate_ctrl(fe, 0); 5388c2ecf20Sopenharmony_ci return 0; 5398c2ecf20Sopenharmony_ci} 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_cistatic s32 table_lookup(const struct slookup *table, 5428c2ecf20Sopenharmony_ci int table_size, u16 reg_value) 5438c2ecf20Sopenharmony_ci{ 5448c2ecf20Sopenharmony_ci s32 gain; 5458c2ecf20Sopenharmony_ci s32 reg_diff; 5468c2ecf20Sopenharmony_ci int imin = 0; 5478c2ecf20Sopenharmony_ci int imax = table_size - 1; 5488c2ecf20Sopenharmony_ci int i; 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_ci /* Assumes Table[0].RegValue < Table[imax].RegValue */ 5518c2ecf20Sopenharmony_ci if (reg_value <= table[0].reg_value) { 5528c2ecf20Sopenharmony_ci gain = table[0].value; 5538c2ecf20Sopenharmony_ci } else if (reg_value >= table[imax].reg_value) { 5548c2ecf20Sopenharmony_ci gain = table[imax].value; 5558c2ecf20Sopenharmony_ci } else { 5568c2ecf20Sopenharmony_ci while ((imax - imin) > 1) { 5578c2ecf20Sopenharmony_ci i = (imax + imin) / 2; 5588c2ecf20Sopenharmony_ci if ((table[imin].reg_value <= reg_value) && 5598c2ecf20Sopenharmony_ci (reg_value <= table[i].reg_value)) 5608c2ecf20Sopenharmony_ci imax = i; 5618c2ecf20Sopenharmony_ci else 5628c2ecf20Sopenharmony_ci imin = i; 5638c2ecf20Sopenharmony_ci } 5648c2ecf20Sopenharmony_ci reg_diff = table[imax].reg_value - table[imin].reg_value; 5658c2ecf20Sopenharmony_ci gain = table[imin].value; 5668c2ecf20Sopenharmony_ci if (reg_diff != 0) 5678c2ecf20Sopenharmony_ci gain += ((s32)(reg_value - table[imin].reg_value) * 5688c2ecf20Sopenharmony_ci (s32)(table[imax].value 5698c2ecf20Sopenharmony_ci - table[imin].value)) / reg_diff; 5708c2ecf20Sopenharmony_ci } 5718c2ecf20Sopenharmony_ci return gain; 5728c2ecf20Sopenharmony_ci} 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_cistatic int get_rf_strength(struct dvb_frontend *fe, u16 *st) 5758c2ecf20Sopenharmony_ci{ 5768c2ecf20Sopenharmony_ci struct stv *state = fe->tuner_priv; 5778c2ecf20Sopenharmony_ci u16 rfagc = *st; 5788c2ecf20Sopenharmony_ci s32 gain; 5798c2ecf20Sopenharmony_ci 5808c2ecf20Sopenharmony_ci if ((state->reg[0x03] & 0x60) == 0) { 5818c2ecf20Sopenharmony_ci /* RF Mode, Read AGC ADC */ 5828c2ecf20Sopenharmony_ci u8 reg = 0; 5838c2ecf20Sopenharmony_ci int stat = 0; 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_ci if (fe->ops.i2c_gate_ctrl) 5868c2ecf20Sopenharmony_ci stat = fe->ops.i2c_gate_ctrl(fe, 1); 5878c2ecf20Sopenharmony_ci if (!stat) { 5888c2ecf20Sopenharmony_ci write_reg(state, 0x02, state->reg[0x02] | 0x20); 5898c2ecf20Sopenharmony_ci read_reg(state, 2, ®); 5908c2ecf20Sopenharmony_ci if (reg & 0x20) 5918c2ecf20Sopenharmony_ci read_reg(state, 2, ®); 5928c2ecf20Sopenharmony_ci } 5938c2ecf20Sopenharmony_ci if (fe->ops.i2c_gate_ctrl && !stat) 5948c2ecf20Sopenharmony_ci fe->ops.i2c_gate_ctrl(fe, 0); 5958c2ecf20Sopenharmony_ci 5968c2ecf20Sopenharmony_ci if ((state->reg[0x02] & 0x80) == 0) 5978c2ecf20Sopenharmony_ci /* NF */ 5988c2ecf20Sopenharmony_ci gain = table_lookup(lnagain_nf_lookup, 5998c2ecf20Sopenharmony_ci ARRAY_SIZE(lnagain_nf_lookup), 6008c2ecf20Sopenharmony_ci reg & 0x1F); 6018c2ecf20Sopenharmony_ci else 6028c2ecf20Sopenharmony_ci /* IIP3 */ 6038c2ecf20Sopenharmony_ci gain = table_lookup(lnagain_iip3_lookup, 6048c2ecf20Sopenharmony_ci ARRAY_SIZE(lnagain_iip3_lookup), 6058c2ecf20Sopenharmony_ci reg & 0x1F); 6068c2ecf20Sopenharmony_ci 6078c2ecf20Sopenharmony_ci gain += table_lookup(gain_rfagc_lookup, 6088c2ecf20Sopenharmony_ci ARRAY_SIZE(gain_rfagc_lookup), rfagc); 6098c2ecf20Sopenharmony_ci 6108c2ecf20Sopenharmony_ci gain -= 2400; 6118c2ecf20Sopenharmony_ci } else { 6128c2ecf20Sopenharmony_ci /* Channel Mode */ 6138c2ecf20Sopenharmony_ci if ((state->reg[0x02] & 0x80) == 0) { 6148c2ecf20Sopenharmony_ci /* NF */ 6158c2ecf20Sopenharmony_ci gain = table_lookup( 6168c2ecf20Sopenharmony_ci gain_channel_agc_nf_lookup, 6178c2ecf20Sopenharmony_ci ARRAY_SIZE(gain_channel_agc_nf_lookup), rfagc); 6188c2ecf20Sopenharmony_ci 6198c2ecf20Sopenharmony_ci gain += 600; 6208c2ecf20Sopenharmony_ci } else { 6218c2ecf20Sopenharmony_ci /* IIP3 */ 6228c2ecf20Sopenharmony_ci gain = table_lookup( 6238c2ecf20Sopenharmony_ci gain_channel_agc_iip3_lookup, 6248c2ecf20Sopenharmony_ci ARRAY_SIZE(gain_channel_agc_iip3_lookup), 6258c2ecf20Sopenharmony_ci rfagc); 6268c2ecf20Sopenharmony_ci } 6278c2ecf20Sopenharmony_ci } 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_ci if (state->frequency > 0) 6308c2ecf20Sopenharmony_ci /* Tilt correction ( 0.00016 dB/MHz ) */ 6318c2ecf20Sopenharmony_ci gain -= ((((s32)(state->frequency / 1000) - 1550) * 2) / 12); 6328c2ecf20Sopenharmony_ci 6338c2ecf20Sopenharmony_ci /* + (BBGain * 10); */ 6348c2ecf20Sopenharmony_ci gain += (s32)((state->reg[0x01] & 0xC0) >> 6) * 600 - 1300; 6358c2ecf20Sopenharmony_ci 6368c2ecf20Sopenharmony_ci if (gain < 0) 6378c2ecf20Sopenharmony_ci gain = 0; 6388c2ecf20Sopenharmony_ci else if (gain > 10000) 6398c2ecf20Sopenharmony_ci gain = 10000; 6408c2ecf20Sopenharmony_ci 6418c2ecf20Sopenharmony_ci *st = 10000 - gain; 6428c2ecf20Sopenharmony_ci 6438c2ecf20Sopenharmony_ci return 0; 6448c2ecf20Sopenharmony_ci} 6458c2ecf20Sopenharmony_ci 6468c2ecf20Sopenharmony_cistatic const struct dvb_tuner_ops tuner_ops = { 6478c2ecf20Sopenharmony_ci .info = { 6488c2ecf20Sopenharmony_ci .name = "ST STV6111", 6498c2ecf20Sopenharmony_ci .frequency_min_hz = 950 * MHz, 6508c2ecf20Sopenharmony_ci .frequency_max_hz = 2150 * MHz, 6518c2ecf20Sopenharmony_ci }, 6528c2ecf20Sopenharmony_ci .set_params = set_params, 6538c2ecf20Sopenharmony_ci .release = release, 6548c2ecf20Sopenharmony_ci .get_rf_strength = get_rf_strength, 6558c2ecf20Sopenharmony_ci .set_bandwidth = set_bandwidth, 6568c2ecf20Sopenharmony_ci}; 6578c2ecf20Sopenharmony_ci 6588c2ecf20Sopenharmony_cistruct dvb_frontend *stv6111_attach(struct dvb_frontend *fe, 6598c2ecf20Sopenharmony_ci struct i2c_adapter *i2c, u8 adr) 6608c2ecf20Sopenharmony_ci{ 6618c2ecf20Sopenharmony_ci struct stv *state; 6628c2ecf20Sopenharmony_ci int stat = -ENODEV; 6638c2ecf20Sopenharmony_ci int gatestat = 0; 6648c2ecf20Sopenharmony_ci 6658c2ecf20Sopenharmony_ci state = kzalloc(sizeof(*state), GFP_KERNEL); 6668c2ecf20Sopenharmony_ci if (!state) 6678c2ecf20Sopenharmony_ci return NULL; 6688c2ecf20Sopenharmony_ci state->adr = adr; 6698c2ecf20Sopenharmony_ci state->i2c = i2c; 6708c2ecf20Sopenharmony_ci memcpy(&fe->ops.tuner_ops, &tuner_ops, sizeof(struct dvb_tuner_ops)); 6718c2ecf20Sopenharmony_ci init_state(state); 6728c2ecf20Sopenharmony_ci 6738c2ecf20Sopenharmony_ci if (fe->ops.i2c_gate_ctrl) 6748c2ecf20Sopenharmony_ci gatestat = fe->ops.i2c_gate_ctrl(fe, 1); 6758c2ecf20Sopenharmony_ci if (!gatestat) 6768c2ecf20Sopenharmony_ci stat = attach_init(state); 6778c2ecf20Sopenharmony_ci if (fe->ops.i2c_gate_ctrl && !gatestat) 6788c2ecf20Sopenharmony_ci fe->ops.i2c_gate_ctrl(fe, 0); 6798c2ecf20Sopenharmony_ci if (stat < 0) { 6808c2ecf20Sopenharmony_ci kfree(state); 6818c2ecf20Sopenharmony_ci return NULL; 6828c2ecf20Sopenharmony_ci } 6838c2ecf20Sopenharmony_ci fe->tuner_priv = state; 6848c2ecf20Sopenharmony_ci return fe; 6858c2ecf20Sopenharmony_ci} 6868c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(stv6111_attach); 6878c2ecf20Sopenharmony_ci 6888c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("ST STV6111 satellite tuner driver"); 6898c2ecf20Sopenharmony_ciMODULE_AUTHOR("Ralph Metzler, Manfred Voelkel"); 6908c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 691