18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Support for Legend Silicon GB20600 (a.k.a DMB-TH) demodulator 48c2ecf20Sopenharmony_ci * LGS8913, LGS8GL5, LGS8G75 58c2ecf20Sopenharmony_ci * experimental support LGS8G42, LGS8G52 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Copyright (C) 2007-2009 David T.L. Wong <davidtlwong@gmail.com> 88c2ecf20Sopenharmony_ci * Copyright (C) 2008 Sirius International (Hong Kong) Limited 98c2ecf20Sopenharmony_ci * Timothy Lee <timothy.lee@siriushk.com> (for initial work on LGS8GL5) 108c2ecf20Sopenharmony_ci */ 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include <asm/div64.h> 138c2ecf20Sopenharmony_ci#include <linux/firmware.h> 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci#include <media/dvb_frontend.h> 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#include "lgs8gxx.h" 188c2ecf20Sopenharmony_ci#include "lgs8gxx_priv.h" 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#define dprintk(args...) \ 218c2ecf20Sopenharmony_ci do { \ 228c2ecf20Sopenharmony_ci if (debug) \ 238c2ecf20Sopenharmony_ci printk(KERN_DEBUG "lgs8gxx: " args); \ 248c2ecf20Sopenharmony_ci } while (0) 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_cistatic int debug; 278c2ecf20Sopenharmony_cistatic int fake_signal_str = 1; 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci#define LGS8GXX_FIRMWARE "lgs8g75.fw" 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_cimodule_param(debug, int, 0644); 328c2ecf20Sopenharmony_ciMODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_cimodule_param(fake_signal_str, int, 0644); 358c2ecf20Sopenharmony_ciMODULE_PARM_DESC(fake_signal_str, "fake signal strength for LGS8913." 368c2ecf20Sopenharmony_ci"Signal strength calculation is slow.(default:on)."); 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci/* LGS8GXX internal helper functions */ 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_cistatic int lgs8gxx_write_reg(struct lgs8gxx_state *priv, u8 reg, u8 data) 418c2ecf20Sopenharmony_ci{ 428c2ecf20Sopenharmony_ci int ret; 438c2ecf20Sopenharmony_ci u8 buf[] = { reg, data }; 448c2ecf20Sopenharmony_ci struct i2c_msg msg = { .flags = 0, .buf = buf, .len = 2 }; 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci msg.addr = priv->config->demod_address; 478c2ecf20Sopenharmony_ci if (priv->config->prod != LGS8GXX_PROD_LGS8G75 && reg >= 0xC0) 488c2ecf20Sopenharmony_ci msg.addr += 0x02; 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci if (debug >= 2) 518c2ecf20Sopenharmony_ci dprintk("%s: reg=0x%02X, data=0x%02X\n", __func__, reg, data); 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci ret = i2c_transfer(priv->i2c, &msg, 1); 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci if (ret != 1) 568c2ecf20Sopenharmony_ci dprintk("%s: error reg=0x%x, data=0x%x, ret=%i\n", 578c2ecf20Sopenharmony_ci __func__, reg, data, ret); 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci return (ret != 1) ? -1 : 0; 608c2ecf20Sopenharmony_ci} 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_cistatic int lgs8gxx_read_reg(struct lgs8gxx_state *priv, u8 reg, u8 *p_data) 638c2ecf20Sopenharmony_ci{ 648c2ecf20Sopenharmony_ci int ret; 658c2ecf20Sopenharmony_ci u8 dev_addr; 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci u8 b0[] = { reg }; 688c2ecf20Sopenharmony_ci u8 b1[] = { 0 }; 698c2ecf20Sopenharmony_ci struct i2c_msg msg[] = { 708c2ecf20Sopenharmony_ci { .flags = 0, .buf = b0, .len = 1 }, 718c2ecf20Sopenharmony_ci { .flags = I2C_M_RD, .buf = b1, .len = 1 }, 728c2ecf20Sopenharmony_ci }; 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci dev_addr = priv->config->demod_address; 758c2ecf20Sopenharmony_ci if (priv->config->prod != LGS8GXX_PROD_LGS8G75 && reg >= 0xC0) 768c2ecf20Sopenharmony_ci dev_addr += 0x02; 778c2ecf20Sopenharmony_ci msg[1].addr = msg[0].addr = dev_addr; 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci ret = i2c_transfer(priv->i2c, msg, 2); 808c2ecf20Sopenharmony_ci if (ret != 2) { 818c2ecf20Sopenharmony_ci dprintk("%s: error reg=0x%x, ret=%i\n", __func__, reg, ret); 828c2ecf20Sopenharmony_ci return -1; 838c2ecf20Sopenharmony_ci } 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci *p_data = b1[0]; 868c2ecf20Sopenharmony_ci if (debug >= 2) 878c2ecf20Sopenharmony_ci dprintk("%s: reg=0x%02X, data=0x%02X\n", __func__, reg, b1[0]); 888c2ecf20Sopenharmony_ci return 0; 898c2ecf20Sopenharmony_ci} 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_cistatic int lgs8gxx_soft_reset(struct lgs8gxx_state *priv) 928c2ecf20Sopenharmony_ci{ 938c2ecf20Sopenharmony_ci lgs8gxx_write_reg(priv, 0x02, 0x00); 948c2ecf20Sopenharmony_ci msleep(1); 958c2ecf20Sopenharmony_ci lgs8gxx_write_reg(priv, 0x02, 0x01); 968c2ecf20Sopenharmony_ci msleep(100); 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci return 0; 998c2ecf20Sopenharmony_ci} 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_cistatic int wait_reg_mask(struct lgs8gxx_state *priv, u8 reg, u8 mask, 1028c2ecf20Sopenharmony_ci u8 val, u8 delay, u8 tries) 1038c2ecf20Sopenharmony_ci{ 1048c2ecf20Sopenharmony_ci u8 t; 1058c2ecf20Sopenharmony_ci int i; 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci for (i = 0; i < tries; i++) { 1088c2ecf20Sopenharmony_ci lgs8gxx_read_reg(priv, reg, &t); 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci if ((t & mask) == val) 1118c2ecf20Sopenharmony_ci return 0; 1128c2ecf20Sopenharmony_ci msleep(delay); 1138c2ecf20Sopenharmony_ci } 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci return 1; 1168c2ecf20Sopenharmony_ci} 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_cistatic int lgs8gxx_set_ad_mode(struct lgs8gxx_state *priv) 1198c2ecf20Sopenharmony_ci{ 1208c2ecf20Sopenharmony_ci const struct lgs8gxx_config *config = priv->config; 1218c2ecf20Sopenharmony_ci u8 if_conf; 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci if_conf = 0x10; /* AGC output on, RF_AGC output off; */ 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci if_conf |= 1268c2ecf20Sopenharmony_ci ((config->ext_adc) ? 0x80 : 0x00) | 1278c2ecf20Sopenharmony_ci ((config->if_neg_center) ? 0x04 : 0x00) | 1288c2ecf20Sopenharmony_ci ((config->if_freq == 0) ? 0x08 : 0x00) | /* Baseband */ 1298c2ecf20Sopenharmony_ci ((config->adc_signed) ? 0x02 : 0x00) | 1308c2ecf20Sopenharmony_ci ((config->if_neg_edge) ? 0x01 : 0x00); 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci if (config->ext_adc && 1338c2ecf20Sopenharmony_ci (config->prod == LGS8GXX_PROD_LGS8G52)) { 1348c2ecf20Sopenharmony_ci lgs8gxx_write_reg(priv, 0xBA, 0x40); 1358c2ecf20Sopenharmony_ci } 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci lgs8gxx_write_reg(priv, 0x07, if_conf); 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci return 0; 1408c2ecf20Sopenharmony_ci} 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_cistatic int lgs8gxx_set_if_freq(struct lgs8gxx_state *priv, u32 freq /*in kHz*/) 1438c2ecf20Sopenharmony_ci{ 1448c2ecf20Sopenharmony_ci u64 val; 1458c2ecf20Sopenharmony_ci u32 v32; 1468c2ecf20Sopenharmony_ci u32 if_clk; 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci if_clk = priv->config->if_clk_freq; 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci val = freq; 1518c2ecf20Sopenharmony_ci if (freq != 0) { 1528c2ecf20Sopenharmony_ci val <<= 32; 1538c2ecf20Sopenharmony_ci if (if_clk != 0) 1548c2ecf20Sopenharmony_ci do_div(val, if_clk); 1558c2ecf20Sopenharmony_ci v32 = val & 0xFFFFFFFF; 1568c2ecf20Sopenharmony_ci dprintk("Set IF Freq to %dkHz\n", freq); 1578c2ecf20Sopenharmony_ci } else { 1588c2ecf20Sopenharmony_ci v32 = 0; 1598c2ecf20Sopenharmony_ci dprintk("Set IF Freq to baseband\n"); 1608c2ecf20Sopenharmony_ci } 1618c2ecf20Sopenharmony_ci dprintk("AFC_INIT_FREQ = 0x%08X\n", v32); 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci if (priv->config->prod == LGS8GXX_PROD_LGS8G75) { 1648c2ecf20Sopenharmony_ci lgs8gxx_write_reg(priv, 0x08, 0xFF & (v32)); 1658c2ecf20Sopenharmony_ci lgs8gxx_write_reg(priv, 0x09, 0xFF & (v32 >> 8)); 1668c2ecf20Sopenharmony_ci lgs8gxx_write_reg(priv, 0x0A, 0xFF & (v32 >> 16)); 1678c2ecf20Sopenharmony_ci lgs8gxx_write_reg(priv, 0x0B, 0xFF & (v32 >> 24)); 1688c2ecf20Sopenharmony_ci } else { 1698c2ecf20Sopenharmony_ci lgs8gxx_write_reg(priv, 0x09, 0xFF & (v32)); 1708c2ecf20Sopenharmony_ci lgs8gxx_write_reg(priv, 0x0A, 0xFF & (v32 >> 8)); 1718c2ecf20Sopenharmony_ci lgs8gxx_write_reg(priv, 0x0B, 0xFF & (v32 >> 16)); 1728c2ecf20Sopenharmony_ci lgs8gxx_write_reg(priv, 0x0C, 0xFF & (v32 >> 24)); 1738c2ecf20Sopenharmony_ci } 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci return 0; 1768c2ecf20Sopenharmony_ci} 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_cistatic int lgs8gxx_get_afc_phase(struct lgs8gxx_state *priv) 1798c2ecf20Sopenharmony_ci{ 1808c2ecf20Sopenharmony_ci u64 val; 1818c2ecf20Sopenharmony_ci u32 v32 = 0; 1828c2ecf20Sopenharmony_ci u8 reg_addr, t; 1838c2ecf20Sopenharmony_ci int i; 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci if (priv->config->prod == LGS8GXX_PROD_LGS8G75) 1868c2ecf20Sopenharmony_ci reg_addr = 0x23; 1878c2ecf20Sopenharmony_ci else 1888c2ecf20Sopenharmony_ci reg_addr = 0x48; 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci for (i = 0; i < 4; i++) { 1918c2ecf20Sopenharmony_ci lgs8gxx_read_reg(priv, reg_addr, &t); 1928c2ecf20Sopenharmony_ci v32 <<= 8; 1938c2ecf20Sopenharmony_ci v32 |= t; 1948c2ecf20Sopenharmony_ci reg_addr--; 1958c2ecf20Sopenharmony_ci } 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci val = v32; 1988c2ecf20Sopenharmony_ci val *= priv->config->if_clk_freq; 1998c2ecf20Sopenharmony_ci val >>= 32; 2008c2ecf20Sopenharmony_ci dprintk("AFC = %u kHz\n", (u32)val); 2018c2ecf20Sopenharmony_ci return 0; 2028c2ecf20Sopenharmony_ci} 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_cistatic int lgs8gxx_set_mode_auto(struct lgs8gxx_state *priv) 2058c2ecf20Sopenharmony_ci{ 2068c2ecf20Sopenharmony_ci u8 t; 2078c2ecf20Sopenharmony_ci u8 prod = priv->config->prod; 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci if (prod == LGS8GXX_PROD_LGS8913) 2108c2ecf20Sopenharmony_ci lgs8gxx_write_reg(priv, 0xC6, 0x01); 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci if (prod == LGS8GXX_PROD_LGS8G75) { 2138c2ecf20Sopenharmony_ci lgs8gxx_read_reg(priv, 0x0C, &t); 2148c2ecf20Sopenharmony_ci t &= (~0x04); 2158c2ecf20Sopenharmony_ci lgs8gxx_write_reg(priv, 0x0C, t | 0x80); 2168c2ecf20Sopenharmony_ci lgs8gxx_write_reg(priv, 0x39, 0x00); 2178c2ecf20Sopenharmony_ci lgs8gxx_write_reg(priv, 0x3D, 0x04); 2188c2ecf20Sopenharmony_ci } else if (prod == LGS8GXX_PROD_LGS8913 || 2198c2ecf20Sopenharmony_ci prod == LGS8GXX_PROD_LGS8GL5 || 2208c2ecf20Sopenharmony_ci prod == LGS8GXX_PROD_LGS8G42 || 2218c2ecf20Sopenharmony_ci prod == LGS8GXX_PROD_LGS8G52 || 2228c2ecf20Sopenharmony_ci prod == LGS8GXX_PROD_LGS8G54) { 2238c2ecf20Sopenharmony_ci lgs8gxx_read_reg(priv, 0x7E, &t); 2248c2ecf20Sopenharmony_ci lgs8gxx_write_reg(priv, 0x7E, t | 0x01); 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci /* clear FEC self reset */ 2278c2ecf20Sopenharmony_ci lgs8gxx_read_reg(priv, 0xC5, &t); 2288c2ecf20Sopenharmony_ci lgs8gxx_write_reg(priv, 0xC5, t & 0xE0); 2298c2ecf20Sopenharmony_ci } 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci if (prod == LGS8GXX_PROD_LGS8913) { 2328c2ecf20Sopenharmony_ci /* FEC auto detect */ 2338c2ecf20Sopenharmony_ci lgs8gxx_write_reg(priv, 0xC1, 0x03); 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci lgs8gxx_read_reg(priv, 0x7C, &t); 2368c2ecf20Sopenharmony_ci t = (t & 0x8C) | 0x03; 2378c2ecf20Sopenharmony_ci lgs8gxx_write_reg(priv, 0x7C, t); 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci /* BER test mode */ 2408c2ecf20Sopenharmony_ci lgs8gxx_read_reg(priv, 0xC3, &t); 2418c2ecf20Sopenharmony_ci t = (t & 0xEF) | 0x10; 2428c2ecf20Sopenharmony_ci lgs8gxx_write_reg(priv, 0xC3, t); 2438c2ecf20Sopenharmony_ci } 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci if (priv->config->prod == LGS8GXX_PROD_LGS8G52) 2468c2ecf20Sopenharmony_ci lgs8gxx_write_reg(priv, 0xD9, 0x40); 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci return 0; 2498c2ecf20Sopenharmony_ci} 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_cistatic int lgs8gxx_set_mode_manual(struct lgs8gxx_state *priv) 2528c2ecf20Sopenharmony_ci{ 2538c2ecf20Sopenharmony_ci u8 t; 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci if (priv->config->prod == LGS8GXX_PROD_LGS8G75) { 2568c2ecf20Sopenharmony_ci u8 t2; 2578c2ecf20Sopenharmony_ci lgs8gxx_read_reg(priv, 0x0C, &t); 2588c2ecf20Sopenharmony_ci t &= (~0x80); 2598c2ecf20Sopenharmony_ci lgs8gxx_write_reg(priv, 0x0C, t); 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci lgs8gxx_read_reg(priv, 0x0C, &t); 2628c2ecf20Sopenharmony_ci lgs8gxx_read_reg(priv, 0x19, &t2); 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci if (((t&0x03) == 0x01) && (t2&0x01)) { 2658c2ecf20Sopenharmony_ci lgs8gxx_write_reg(priv, 0x6E, 0x05); 2668c2ecf20Sopenharmony_ci lgs8gxx_write_reg(priv, 0x39, 0x02); 2678c2ecf20Sopenharmony_ci lgs8gxx_write_reg(priv, 0x39, 0x03); 2688c2ecf20Sopenharmony_ci lgs8gxx_write_reg(priv, 0x3D, 0x05); 2698c2ecf20Sopenharmony_ci lgs8gxx_write_reg(priv, 0x3E, 0x28); 2708c2ecf20Sopenharmony_ci lgs8gxx_write_reg(priv, 0x53, 0x80); 2718c2ecf20Sopenharmony_ci } else { 2728c2ecf20Sopenharmony_ci lgs8gxx_write_reg(priv, 0x6E, 0x3F); 2738c2ecf20Sopenharmony_ci lgs8gxx_write_reg(priv, 0x39, 0x00); 2748c2ecf20Sopenharmony_ci lgs8gxx_write_reg(priv, 0x3D, 0x04); 2758c2ecf20Sopenharmony_ci } 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci lgs8gxx_soft_reset(priv); 2788c2ecf20Sopenharmony_ci return 0; 2798c2ecf20Sopenharmony_ci } 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci /* turn off auto-detect; manual settings */ 2828c2ecf20Sopenharmony_ci lgs8gxx_write_reg(priv, 0x7E, 0); 2838c2ecf20Sopenharmony_ci if (priv->config->prod == LGS8GXX_PROD_LGS8913) 2848c2ecf20Sopenharmony_ci lgs8gxx_write_reg(priv, 0xC1, 0); 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci lgs8gxx_read_reg(priv, 0xC5, &t); 2878c2ecf20Sopenharmony_ci t = (t & 0xE0) | 0x06; 2888c2ecf20Sopenharmony_ci lgs8gxx_write_reg(priv, 0xC5, t); 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci lgs8gxx_soft_reset(priv); 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci return 0; 2938c2ecf20Sopenharmony_ci} 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_cistatic int lgs8gxx_is_locked(struct lgs8gxx_state *priv, u8 *locked) 2968c2ecf20Sopenharmony_ci{ 2978c2ecf20Sopenharmony_ci int ret = 0; 2988c2ecf20Sopenharmony_ci u8 t; 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci if (priv->config->prod == LGS8GXX_PROD_LGS8G75) 3018c2ecf20Sopenharmony_ci ret = lgs8gxx_read_reg(priv, 0x13, &t); 3028c2ecf20Sopenharmony_ci else 3038c2ecf20Sopenharmony_ci ret = lgs8gxx_read_reg(priv, 0x4B, &t); 3048c2ecf20Sopenharmony_ci if (ret != 0) 3058c2ecf20Sopenharmony_ci return ret; 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci if (priv->config->prod == LGS8GXX_PROD_LGS8G75) 3088c2ecf20Sopenharmony_ci *locked = ((t & 0x80) == 0x80) ? 1 : 0; 3098c2ecf20Sopenharmony_ci else 3108c2ecf20Sopenharmony_ci *locked = ((t & 0xC0) == 0xC0) ? 1 : 0; 3118c2ecf20Sopenharmony_ci return 0; 3128c2ecf20Sopenharmony_ci} 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci/* Wait for Code Acquisition Lock */ 3158c2ecf20Sopenharmony_cistatic int lgs8gxx_wait_ca_lock(struct lgs8gxx_state *priv, u8 *locked) 3168c2ecf20Sopenharmony_ci{ 3178c2ecf20Sopenharmony_ci int ret = 0; 3188c2ecf20Sopenharmony_ci u8 reg, mask, val; 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci if (priv->config->prod == LGS8GXX_PROD_LGS8G75) { 3218c2ecf20Sopenharmony_ci reg = 0x13; 3228c2ecf20Sopenharmony_ci mask = 0x80; 3238c2ecf20Sopenharmony_ci val = 0x80; 3248c2ecf20Sopenharmony_ci } else { 3258c2ecf20Sopenharmony_ci reg = 0x4B; 3268c2ecf20Sopenharmony_ci mask = 0xC0; 3278c2ecf20Sopenharmony_ci val = 0xC0; 3288c2ecf20Sopenharmony_ci } 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci ret = wait_reg_mask(priv, reg, mask, val, 50, 40); 3318c2ecf20Sopenharmony_ci *locked = (ret == 0) ? 1 : 0; 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci return 0; 3348c2ecf20Sopenharmony_ci} 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_cistatic int lgs8gxx_is_autodetect_finished(struct lgs8gxx_state *priv, 3378c2ecf20Sopenharmony_ci u8 *finished) 3388c2ecf20Sopenharmony_ci{ 3398c2ecf20Sopenharmony_ci int ret = 0; 3408c2ecf20Sopenharmony_ci u8 reg, mask, val; 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci if (priv->config->prod == LGS8GXX_PROD_LGS8G75) { 3438c2ecf20Sopenharmony_ci reg = 0x1f; 3448c2ecf20Sopenharmony_ci mask = 0xC0; 3458c2ecf20Sopenharmony_ci val = 0x80; 3468c2ecf20Sopenharmony_ci } else { 3478c2ecf20Sopenharmony_ci reg = 0xA4; 3488c2ecf20Sopenharmony_ci mask = 0x03; 3498c2ecf20Sopenharmony_ci val = 0x01; 3508c2ecf20Sopenharmony_ci } 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci ret = wait_reg_mask(priv, reg, mask, val, 10, 20); 3538c2ecf20Sopenharmony_ci *finished = (ret == 0) ? 1 : 0; 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci return 0; 3568c2ecf20Sopenharmony_ci} 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_cistatic int lgs8gxx_autolock_gi(struct lgs8gxx_state *priv, u8 gi, u8 cpn, 3598c2ecf20Sopenharmony_ci u8 *locked) 3608c2ecf20Sopenharmony_ci{ 3618c2ecf20Sopenharmony_ci int err = 0; 3628c2ecf20Sopenharmony_ci u8 ad_fini = 0; 3638c2ecf20Sopenharmony_ci u8 t1, t2; 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci if (gi == GI_945) 3668c2ecf20Sopenharmony_ci dprintk("try GI 945\n"); 3678c2ecf20Sopenharmony_ci else if (gi == GI_595) 3688c2ecf20Sopenharmony_ci dprintk("try GI 595\n"); 3698c2ecf20Sopenharmony_ci else if (gi == GI_420) 3708c2ecf20Sopenharmony_ci dprintk("try GI 420\n"); 3718c2ecf20Sopenharmony_ci if (priv->config->prod == LGS8GXX_PROD_LGS8G75) { 3728c2ecf20Sopenharmony_ci lgs8gxx_read_reg(priv, 0x0C, &t1); 3738c2ecf20Sopenharmony_ci lgs8gxx_read_reg(priv, 0x18, &t2); 3748c2ecf20Sopenharmony_ci t1 &= ~(GI_MASK); 3758c2ecf20Sopenharmony_ci t1 |= gi; 3768c2ecf20Sopenharmony_ci t2 &= 0xFE; 3778c2ecf20Sopenharmony_ci t2 |= cpn ? 0x01 : 0x00; 3788c2ecf20Sopenharmony_ci lgs8gxx_write_reg(priv, 0x0C, t1); 3798c2ecf20Sopenharmony_ci lgs8gxx_write_reg(priv, 0x18, t2); 3808c2ecf20Sopenharmony_ci } else { 3818c2ecf20Sopenharmony_ci lgs8gxx_write_reg(priv, 0x04, gi); 3828c2ecf20Sopenharmony_ci } 3838c2ecf20Sopenharmony_ci lgs8gxx_soft_reset(priv); 3848c2ecf20Sopenharmony_ci err = lgs8gxx_wait_ca_lock(priv, locked); 3858c2ecf20Sopenharmony_ci if (err || !(*locked)) 3868c2ecf20Sopenharmony_ci return err; 3878c2ecf20Sopenharmony_ci err = lgs8gxx_is_autodetect_finished(priv, &ad_fini); 3888c2ecf20Sopenharmony_ci if (err != 0) 3898c2ecf20Sopenharmony_ci return err; 3908c2ecf20Sopenharmony_ci if (ad_fini) { 3918c2ecf20Sopenharmony_ci dprintk("auto detect finished\n"); 3928c2ecf20Sopenharmony_ci } else 3938c2ecf20Sopenharmony_ci *locked = 0; 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci return 0; 3968c2ecf20Sopenharmony_ci} 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_cistatic int lgs8gxx_auto_detect(struct lgs8gxx_state *priv, 3998c2ecf20Sopenharmony_ci u8 *detected_param, u8 *gi) 4008c2ecf20Sopenharmony_ci{ 4018c2ecf20Sopenharmony_ci int i, j; 4028c2ecf20Sopenharmony_ci int err = 0; 4038c2ecf20Sopenharmony_ci u8 locked = 0, tmp_gi; 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci dprintk("%s\n", __func__); 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci lgs8gxx_set_mode_auto(priv); 4088c2ecf20Sopenharmony_ci if (priv->config->prod == LGS8GXX_PROD_LGS8G75) { 4098c2ecf20Sopenharmony_ci lgs8gxx_write_reg(priv, 0x67, 0xAA); 4108c2ecf20Sopenharmony_ci lgs8gxx_write_reg(priv, 0x6E, 0x3F); 4118c2ecf20Sopenharmony_ci } else { 4128c2ecf20Sopenharmony_ci /* Guard Interval */ 4138c2ecf20Sopenharmony_ci lgs8gxx_write_reg(priv, 0x03, 00); 4148c2ecf20Sopenharmony_ci } 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci for (i = 0; i < 2; i++) { 4178c2ecf20Sopenharmony_ci for (j = 0; j < 2; j++) { 4188c2ecf20Sopenharmony_ci tmp_gi = GI_945; 4198c2ecf20Sopenharmony_ci err = lgs8gxx_autolock_gi(priv, GI_945, j, &locked); 4208c2ecf20Sopenharmony_ci if (err) 4218c2ecf20Sopenharmony_ci goto out; 4228c2ecf20Sopenharmony_ci if (locked) 4238c2ecf20Sopenharmony_ci goto locked; 4248c2ecf20Sopenharmony_ci } 4258c2ecf20Sopenharmony_ci for (j = 0; j < 2; j++) { 4268c2ecf20Sopenharmony_ci tmp_gi = GI_420; 4278c2ecf20Sopenharmony_ci err = lgs8gxx_autolock_gi(priv, GI_420, j, &locked); 4288c2ecf20Sopenharmony_ci if (err) 4298c2ecf20Sopenharmony_ci goto out; 4308c2ecf20Sopenharmony_ci if (locked) 4318c2ecf20Sopenharmony_ci goto locked; 4328c2ecf20Sopenharmony_ci } 4338c2ecf20Sopenharmony_ci tmp_gi = GI_595; 4348c2ecf20Sopenharmony_ci err = lgs8gxx_autolock_gi(priv, GI_595, 1, &locked); 4358c2ecf20Sopenharmony_ci if (err) 4368c2ecf20Sopenharmony_ci goto out; 4378c2ecf20Sopenharmony_ci if (locked) 4388c2ecf20Sopenharmony_ci goto locked; 4398c2ecf20Sopenharmony_ci } 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_cilocked: 4428c2ecf20Sopenharmony_ci if ((err == 0) && (locked == 1)) { 4438c2ecf20Sopenharmony_ci u8 t; 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci if (priv->config->prod != LGS8GXX_PROD_LGS8G75) { 4468c2ecf20Sopenharmony_ci lgs8gxx_read_reg(priv, 0xA2, &t); 4478c2ecf20Sopenharmony_ci *detected_param = t; 4488c2ecf20Sopenharmony_ci } else { 4498c2ecf20Sopenharmony_ci lgs8gxx_read_reg(priv, 0x1F, &t); 4508c2ecf20Sopenharmony_ci *detected_param = t & 0x3F; 4518c2ecf20Sopenharmony_ci } 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci if (tmp_gi == GI_945) 4548c2ecf20Sopenharmony_ci dprintk("GI 945 locked\n"); 4558c2ecf20Sopenharmony_ci else if (tmp_gi == GI_595) 4568c2ecf20Sopenharmony_ci dprintk("GI 595 locked\n"); 4578c2ecf20Sopenharmony_ci else if (tmp_gi == GI_420) 4588c2ecf20Sopenharmony_ci dprintk("GI 420 locked\n"); 4598c2ecf20Sopenharmony_ci *gi = tmp_gi; 4608c2ecf20Sopenharmony_ci } 4618c2ecf20Sopenharmony_ci if (!locked) 4628c2ecf20Sopenharmony_ci err = -1; 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_ciout: 4658c2ecf20Sopenharmony_ci return err; 4668c2ecf20Sopenharmony_ci} 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_cistatic void lgs8gxx_auto_lock(struct lgs8gxx_state *priv) 4698c2ecf20Sopenharmony_ci{ 4708c2ecf20Sopenharmony_ci s8 err; 4718c2ecf20Sopenharmony_ci u8 gi = 0x2; 4728c2ecf20Sopenharmony_ci u8 detected_param = 0; 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci err = lgs8gxx_auto_detect(priv, &detected_param, &gi); 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ci if (err != 0) { 4778c2ecf20Sopenharmony_ci dprintk("lgs8gxx_auto_detect failed\n"); 4788c2ecf20Sopenharmony_ci } else 4798c2ecf20Sopenharmony_ci dprintk("detected param = 0x%02X\n", detected_param); 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_ci /* Apply detected parameters */ 4828c2ecf20Sopenharmony_ci if (priv->config->prod == LGS8GXX_PROD_LGS8913) { 4838c2ecf20Sopenharmony_ci u8 inter_leave_len = detected_param & TIM_MASK ; 4848c2ecf20Sopenharmony_ci /* Fix 8913 time interleaver detection bug */ 4858c2ecf20Sopenharmony_ci inter_leave_len = (inter_leave_len == TIM_MIDDLE) ? 0x60 : 0x40; 4868c2ecf20Sopenharmony_ci detected_param &= CF_MASK | SC_MASK | LGS_FEC_MASK; 4878c2ecf20Sopenharmony_ci detected_param |= inter_leave_len; 4888c2ecf20Sopenharmony_ci } 4898c2ecf20Sopenharmony_ci if (priv->config->prod == LGS8GXX_PROD_LGS8G75) { 4908c2ecf20Sopenharmony_ci u8 t; 4918c2ecf20Sopenharmony_ci lgs8gxx_read_reg(priv, 0x19, &t); 4928c2ecf20Sopenharmony_ci t &= 0x81; 4938c2ecf20Sopenharmony_ci t |= detected_param << 1; 4948c2ecf20Sopenharmony_ci lgs8gxx_write_reg(priv, 0x19, t); 4958c2ecf20Sopenharmony_ci } else { 4968c2ecf20Sopenharmony_ci lgs8gxx_write_reg(priv, 0x7D, detected_param); 4978c2ecf20Sopenharmony_ci if (priv->config->prod == LGS8GXX_PROD_LGS8913) 4988c2ecf20Sopenharmony_ci lgs8gxx_write_reg(priv, 0xC0, detected_param); 4998c2ecf20Sopenharmony_ci } 5008c2ecf20Sopenharmony_ci /* lgs8gxx_soft_reset(priv); */ 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_ci /* Enter manual mode */ 5038c2ecf20Sopenharmony_ci lgs8gxx_set_mode_manual(priv); 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_ci switch (gi) { 5068c2ecf20Sopenharmony_ci case GI_945: 5078c2ecf20Sopenharmony_ci priv->curr_gi = 945; break; 5088c2ecf20Sopenharmony_ci case GI_595: 5098c2ecf20Sopenharmony_ci priv->curr_gi = 595; break; 5108c2ecf20Sopenharmony_ci case GI_420: 5118c2ecf20Sopenharmony_ci priv->curr_gi = 420; break; 5128c2ecf20Sopenharmony_ci default: 5138c2ecf20Sopenharmony_ci priv->curr_gi = 945; break; 5148c2ecf20Sopenharmony_ci } 5158c2ecf20Sopenharmony_ci} 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_cistatic int lgs8gxx_set_mpeg_mode(struct lgs8gxx_state *priv, 5188c2ecf20Sopenharmony_ci u8 serial, u8 clk_pol, u8 clk_gated) 5198c2ecf20Sopenharmony_ci{ 5208c2ecf20Sopenharmony_ci int ret = 0; 5218c2ecf20Sopenharmony_ci u8 t, reg_addr; 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_ci reg_addr = (priv->config->prod == LGS8GXX_PROD_LGS8G75) ? 0x30 : 0xC2; 5248c2ecf20Sopenharmony_ci ret = lgs8gxx_read_reg(priv, reg_addr, &t); 5258c2ecf20Sopenharmony_ci if (ret != 0) 5268c2ecf20Sopenharmony_ci return ret; 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_ci t &= 0xF8; 5298c2ecf20Sopenharmony_ci t |= serial ? TS_SERIAL : TS_PARALLEL; 5308c2ecf20Sopenharmony_ci t |= clk_pol ? TS_CLK_INVERTED : TS_CLK_NORMAL; 5318c2ecf20Sopenharmony_ci t |= clk_gated ? TS_CLK_GATED : TS_CLK_FREERUN; 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_ci ret = lgs8gxx_write_reg(priv, reg_addr, t); 5348c2ecf20Sopenharmony_ci if (ret != 0) 5358c2ecf20Sopenharmony_ci return ret; 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_ci return 0; 5388c2ecf20Sopenharmony_ci} 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_ci/* A/D input peak-to-peak voltage range */ 5418c2ecf20Sopenharmony_cistatic int lgs8g75_set_adc_vpp(struct lgs8gxx_state *priv, 5428c2ecf20Sopenharmony_ci u8 sel) 5438c2ecf20Sopenharmony_ci{ 5448c2ecf20Sopenharmony_ci u8 r26 = 0x73, r27 = 0x90; 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_ci if (priv->config->prod != LGS8GXX_PROD_LGS8G75) 5478c2ecf20Sopenharmony_ci return 0; 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_ci r26 |= (sel & 0x01) << 7; 5508c2ecf20Sopenharmony_ci r27 |= (sel & 0x02) >> 1; 5518c2ecf20Sopenharmony_ci lgs8gxx_write_reg(priv, 0x26, r26); 5528c2ecf20Sopenharmony_ci lgs8gxx_write_reg(priv, 0x27, r27); 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ci return 0; 5558c2ecf20Sopenharmony_ci} 5568c2ecf20Sopenharmony_ci 5578c2ecf20Sopenharmony_ci/* LGS8913 demod frontend functions */ 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_cistatic int lgs8913_init(struct lgs8gxx_state *priv) 5608c2ecf20Sopenharmony_ci{ 5618c2ecf20Sopenharmony_ci u8 t; 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_ci /* LGS8913 specific */ 5648c2ecf20Sopenharmony_ci lgs8gxx_write_reg(priv, 0xc1, 0x3); 5658c2ecf20Sopenharmony_ci 5668c2ecf20Sopenharmony_ci lgs8gxx_read_reg(priv, 0x7c, &t); 5678c2ecf20Sopenharmony_ci lgs8gxx_write_reg(priv, 0x7c, (t&0x8c) | 0x3); 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_ci /* LGS8913 specific */ 5708c2ecf20Sopenharmony_ci lgs8gxx_read_reg(priv, 0xc3, &t); 5718c2ecf20Sopenharmony_ci lgs8gxx_write_reg(priv, 0xc3, t&0x10); 5728c2ecf20Sopenharmony_ci 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_ci return 0; 5758c2ecf20Sopenharmony_ci} 5768c2ecf20Sopenharmony_ci 5778c2ecf20Sopenharmony_cistatic int lgs8g75_init_data(struct lgs8gxx_state *priv) 5788c2ecf20Sopenharmony_ci{ 5798c2ecf20Sopenharmony_ci const struct firmware *fw; 5808c2ecf20Sopenharmony_ci int rc; 5818c2ecf20Sopenharmony_ci int i; 5828c2ecf20Sopenharmony_ci 5838c2ecf20Sopenharmony_ci rc = request_firmware(&fw, LGS8GXX_FIRMWARE, &priv->i2c->dev); 5848c2ecf20Sopenharmony_ci if (rc) 5858c2ecf20Sopenharmony_ci return rc; 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_ci lgs8gxx_write_reg(priv, 0xC6, 0x40); 5888c2ecf20Sopenharmony_ci 5898c2ecf20Sopenharmony_ci lgs8gxx_write_reg(priv, 0x3D, 0x04); 5908c2ecf20Sopenharmony_ci lgs8gxx_write_reg(priv, 0x39, 0x00); 5918c2ecf20Sopenharmony_ci 5928c2ecf20Sopenharmony_ci lgs8gxx_write_reg(priv, 0x3A, 0x00); 5938c2ecf20Sopenharmony_ci lgs8gxx_write_reg(priv, 0x38, 0x00); 5948c2ecf20Sopenharmony_ci lgs8gxx_write_reg(priv, 0x3B, 0x00); 5958c2ecf20Sopenharmony_ci lgs8gxx_write_reg(priv, 0x38, 0x00); 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_ci for (i = 0; i < fw->size; i++) { 5988c2ecf20Sopenharmony_ci lgs8gxx_write_reg(priv, 0x38, 0x00); 5998c2ecf20Sopenharmony_ci lgs8gxx_write_reg(priv, 0x3A, (u8)(i&0xff)); 6008c2ecf20Sopenharmony_ci lgs8gxx_write_reg(priv, 0x3B, (u8)(i>>8)); 6018c2ecf20Sopenharmony_ci lgs8gxx_write_reg(priv, 0x3C, fw->data[i]); 6028c2ecf20Sopenharmony_ci } 6038c2ecf20Sopenharmony_ci 6048c2ecf20Sopenharmony_ci lgs8gxx_write_reg(priv, 0x38, 0x00); 6058c2ecf20Sopenharmony_ci 6068c2ecf20Sopenharmony_ci release_firmware(fw); 6078c2ecf20Sopenharmony_ci return 0; 6088c2ecf20Sopenharmony_ci} 6098c2ecf20Sopenharmony_ci 6108c2ecf20Sopenharmony_cistatic int lgs8gxx_init(struct dvb_frontend *fe) 6118c2ecf20Sopenharmony_ci{ 6128c2ecf20Sopenharmony_ci struct lgs8gxx_state *priv = 6138c2ecf20Sopenharmony_ci (struct lgs8gxx_state *)fe->demodulator_priv; 6148c2ecf20Sopenharmony_ci const struct lgs8gxx_config *config = priv->config; 6158c2ecf20Sopenharmony_ci u8 data = 0; 6168c2ecf20Sopenharmony_ci s8 err; 6178c2ecf20Sopenharmony_ci dprintk("%s\n", __func__); 6188c2ecf20Sopenharmony_ci 6198c2ecf20Sopenharmony_ci lgs8gxx_read_reg(priv, 0, &data); 6208c2ecf20Sopenharmony_ci dprintk("reg 0 = 0x%02X\n", data); 6218c2ecf20Sopenharmony_ci 6228c2ecf20Sopenharmony_ci if (config->prod == LGS8GXX_PROD_LGS8G75) 6238c2ecf20Sopenharmony_ci lgs8g75_set_adc_vpp(priv, config->adc_vpp); 6248c2ecf20Sopenharmony_ci 6258c2ecf20Sopenharmony_ci /* Setup MPEG output format */ 6268c2ecf20Sopenharmony_ci err = lgs8gxx_set_mpeg_mode(priv, config->serial_ts, 6278c2ecf20Sopenharmony_ci config->ts_clk_pol, 6288c2ecf20Sopenharmony_ci config->ts_clk_gated); 6298c2ecf20Sopenharmony_ci if (err != 0) 6308c2ecf20Sopenharmony_ci return -EIO; 6318c2ecf20Sopenharmony_ci 6328c2ecf20Sopenharmony_ci if (config->prod == LGS8GXX_PROD_LGS8913) 6338c2ecf20Sopenharmony_ci lgs8913_init(priv); 6348c2ecf20Sopenharmony_ci lgs8gxx_set_if_freq(priv, priv->config->if_freq); 6358c2ecf20Sopenharmony_ci lgs8gxx_set_ad_mode(priv); 6368c2ecf20Sopenharmony_ci 6378c2ecf20Sopenharmony_ci return 0; 6388c2ecf20Sopenharmony_ci} 6398c2ecf20Sopenharmony_ci 6408c2ecf20Sopenharmony_cistatic void lgs8gxx_release(struct dvb_frontend *fe) 6418c2ecf20Sopenharmony_ci{ 6428c2ecf20Sopenharmony_ci struct lgs8gxx_state *state = fe->demodulator_priv; 6438c2ecf20Sopenharmony_ci dprintk("%s\n", __func__); 6448c2ecf20Sopenharmony_ci 6458c2ecf20Sopenharmony_ci kfree(state); 6468c2ecf20Sopenharmony_ci} 6478c2ecf20Sopenharmony_ci 6488c2ecf20Sopenharmony_ci 6498c2ecf20Sopenharmony_cistatic int lgs8gxx_write(struct dvb_frontend *fe, const u8 buf[], int len) 6508c2ecf20Sopenharmony_ci{ 6518c2ecf20Sopenharmony_ci struct lgs8gxx_state *priv = fe->demodulator_priv; 6528c2ecf20Sopenharmony_ci 6538c2ecf20Sopenharmony_ci if (len != 2) 6548c2ecf20Sopenharmony_ci return -EINVAL; 6558c2ecf20Sopenharmony_ci 6568c2ecf20Sopenharmony_ci return lgs8gxx_write_reg(priv, buf[0], buf[1]); 6578c2ecf20Sopenharmony_ci} 6588c2ecf20Sopenharmony_ci 6598c2ecf20Sopenharmony_cistatic int lgs8gxx_set_fe(struct dvb_frontend *fe) 6608c2ecf20Sopenharmony_ci{ 6618c2ecf20Sopenharmony_ci struct dtv_frontend_properties *fe_params = &fe->dtv_property_cache; 6628c2ecf20Sopenharmony_ci struct lgs8gxx_state *priv = fe->demodulator_priv; 6638c2ecf20Sopenharmony_ci 6648c2ecf20Sopenharmony_ci dprintk("%s\n", __func__); 6658c2ecf20Sopenharmony_ci 6668c2ecf20Sopenharmony_ci /* set frequency */ 6678c2ecf20Sopenharmony_ci if (fe->ops.tuner_ops.set_params) { 6688c2ecf20Sopenharmony_ci fe->ops.tuner_ops.set_params(fe); 6698c2ecf20Sopenharmony_ci if (fe->ops.i2c_gate_ctrl) 6708c2ecf20Sopenharmony_ci fe->ops.i2c_gate_ctrl(fe, 0); 6718c2ecf20Sopenharmony_ci } 6728c2ecf20Sopenharmony_ci 6738c2ecf20Sopenharmony_ci /* start auto lock */ 6748c2ecf20Sopenharmony_ci lgs8gxx_auto_lock(priv); 6758c2ecf20Sopenharmony_ci 6768c2ecf20Sopenharmony_ci msleep(10); 6778c2ecf20Sopenharmony_ci 6788c2ecf20Sopenharmony_ci /* TODO: get real readings from device */ 6798c2ecf20Sopenharmony_ci 6808c2ecf20Sopenharmony_ci /* bandwidth */ 6818c2ecf20Sopenharmony_ci fe_params->bandwidth_hz = 8000000; 6828c2ecf20Sopenharmony_ci 6838c2ecf20Sopenharmony_ci fe_params->code_rate_HP = FEC_AUTO; 6848c2ecf20Sopenharmony_ci fe_params->code_rate_LP = FEC_AUTO; 6858c2ecf20Sopenharmony_ci 6868c2ecf20Sopenharmony_ci fe_params->modulation = QAM_AUTO; 6878c2ecf20Sopenharmony_ci 6888c2ecf20Sopenharmony_ci /* transmission mode */ 6898c2ecf20Sopenharmony_ci fe_params->transmission_mode = TRANSMISSION_MODE_AUTO; 6908c2ecf20Sopenharmony_ci 6918c2ecf20Sopenharmony_ci /* guard interval */ 6928c2ecf20Sopenharmony_ci fe_params->guard_interval = GUARD_INTERVAL_AUTO; 6938c2ecf20Sopenharmony_ci 6948c2ecf20Sopenharmony_ci /* hierarchy */ 6958c2ecf20Sopenharmony_ci fe_params->hierarchy = HIERARCHY_NONE; 6968c2ecf20Sopenharmony_ci 6978c2ecf20Sopenharmony_ci return 0; 6988c2ecf20Sopenharmony_ci} 6998c2ecf20Sopenharmony_ci 7008c2ecf20Sopenharmony_cistatic 7018c2ecf20Sopenharmony_ciint lgs8gxx_get_tune_settings(struct dvb_frontend *fe, 7028c2ecf20Sopenharmony_ci struct dvb_frontend_tune_settings *fesettings) 7038c2ecf20Sopenharmony_ci{ 7048c2ecf20Sopenharmony_ci /* FIXME: copy from tda1004x.c */ 7058c2ecf20Sopenharmony_ci fesettings->min_delay_ms = 800; 7068c2ecf20Sopenharmony_ci fesettings->step_size = 0; 7078c2ecf20Sopenharmony_ci fesettings->max_drift = 0; 7088c2ecf20Sopenharmony_ci return 0; 7098c2ecf20Sopenharmony_ci} 7108c2ecf20Sopenharmony_ci 7118c2ecf20Sopenharmony_cistatic int lgs8gxx_read_status(struct dvb_frontend *fe, 7128c2ecf20Sopenharmony_ci enum fe_status *fe_status) 7138c2ecf20Sopenharmony_ci{ 7148c2ecf20Sopenharmony_ci struct lgs8gxx_state *priv = fe->demodulator_priv; 7158c2ecf20Sopenharmony_ci s8 ret; 7168c2ecf20Sopenharmony_ci u8 t, locked = 0; 7178c2ecf20Sopenharmony_ci 7188c2ecf20Sopenharmony_ci dprintk("%s\n", __func__); 7198c2ecf20Sopenharmony_ci *fe_status = 0; 7208c2ecf20Sopenharmony_ci 7218c2ecf20Sopenharmony_ci lgs8gxx_get_afc_phase(priv); 7228c2ecf20Sopenharmony_ci lgs8gxx_is_locked(priv, &locked); 7238c2ecf20Sopenharmony_ci if (priv->config->prod == LGS8GXX_PROD_LGS8G75) { 7248c2ecf20Sopenharmony_ci if (locked) 7258c2ecf20Sopenharmony_ci *fe_status |= FE_HAS_SIGNAL | FE_HAS_CARRIER | 7268c2ecf20Sopenharmony_ci FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK; 7278c2ecf20Sopenharmony_ci return 0; 7288c2ecf20Sopenharmony_ci } 7298c2ecf20Sopenharmony_ci 7308c2ecf20Sopenharmony_ci ret = lgs8gxx_read_reg(priv, 0x4B, &t); 7318c2ecf20Sopenharmony_ci if (ret != 0) 7328c2ecf20Sopenharmony_ci return -EIO; 7338c2ecf20Sopenharmony_ci 7348c2ecf20Sopenharmony_ci dprintk("Reg 0x4B: 0x%02X\n", t); 7358c2ecf20Sopenharmony_ci 7368c2ecf20Sopenharmony_ci *fe_status = 0; 7378c2ecf20Sopenharmony_ci if (priv->config->prod == LGS8GXX_PROD_LGS8913) { 7388c2ecf20Sopenharmony_ci if ((t & 0x40) == 0x40) 7398c2ecf20Sopenharmony_ci *fe_status |= FE_HAS_SIGNAL | FE_HAS_CARRIER; 7408c2ecf20Sopenharmony_ci if ((t & 0x80) == 0x80) 7418c2ecf20Sopenharmony_ci *fe_status |= FE_HAS_VITERBI | FE_HAS_SYNC | 7428c2ecf20Sopenharmony_ci FE_HAS_LOCK; 7438c2ecf20Sopenharmony_ci } else { 7448c2ecf20Sopenharmony_ci if ((t & 0x80) == 0x80) 7458c2ecf20Sopenharmony_ci *fe_status |= FE_HAS_SIGNAL | FE_HAS_CARRIER | 7468c2ecf20Sopenharmony_ci FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK; 7478c2ecf20Sopenharmony_ci } 7488c2ecf20Sopenharmony_ci 7498c2ecf20Sopenharmony_ci /* success */ 7508c2ecf20Sopenharmony_ci dprintk("%s: fe_status=0x%x\n", __func__, *fe_status); 7518c2ecf20Sopenharmony_ci return 0; 7528c2ecf20Sopenharmony_ci} 7538c2ecf20Sopenharmony_ci 7548c2ecf20Sopenharmony_cistatic int lgs8gxx_read_signal_agc(struct lgs8gxx_state *priv, u16 *signal) 7558c2ecf20Sopenharmony_ci{ 7568c2ecf20Sopenharmony_ci u16 v; 7578c2ecf20Sopenharmony_ci u8 agc_lvl[2], cat; 7588c2ecf20Sopenharmony_ci 7598c2ecf20Sopenharmony_ci dprintk("%s()\n", __func__); 7608c2ecf20Sopenharmony_ci lgs8gxx_read_reg(priv, 0x3F, &agc_lvl[0]); 7618c2ecf20Sopenharmony_ci lgs8gxx_read_reg(priv, 0x3E, &agc_lvl[1]); 7628c2ecf20Sopenharmony_ci 7638c2ecf20Sopenharmony_ci v = agc_lvl[0]; 7648c2ecf20Sopenharmony_ci v <<= 8; 7658c2ecf20Sopenharmony_ci v |= agc_lvl[1]; 7668c2ecf20Sopenharmony_ci 7678c2ecf20Sopenharmony_ci dprintk("agc_lvl: 0x%04X\n", v); 7688c2ecf20Sopenharmony_ci 7698c2ecf20Sopenharmony_ci if (v < 0x100) 7708c2ecf20Sopenharmony_ci cat = 0; 7718c2ecf20Sopenharmony_ci else if (v < 0x190) 7728c2ecf20Sopenharmony_ci cat = 5; 7738c2ecf20Sopenharmony_ci else if (v < 0x2A8) 7748c2ecf20Sopenharmony_ci cat = 4; 7758c2ecf20Sopenharmony_ci else if (v < 0x381) 7768c2ecf20Sopenharmony_ci cat = 3; 7778c2ecf20Sopenharmony_ci else if (v < 0x400) 7788c2ecf20Sopenharmony_ci cat = 2; 7798c2ecf20Sopenharmony_ci else if (v == 0x400) 7808c2ecf20Sopenharmony_ci cat = 1; 7818c2ecf20Sopenharmony_ci else 7828c2ecf20Sopenharmony_ci cat = 0; 7838c2ecf20Sopenharmony_ci 7848c2ecf20Sopenharmony_ci *signal = cat * 65535 / 5; 7858c2ecf20Sopenharmony_ci 7868c2ecf20Sopenharmony_ci return 0; 7878c2ecf20Sopenharmony_ci} 7888c2ecf20Sopenharmony_ci 7898c2ecf20Sopenharmony_cistatic int lgs8913_read_signal_strength(struct lgs8gxx_state *priv, u16 *signal) 7908c2ecf20Sopenharmony_ci{ 7918c2ecf20Sopenharmony_ci u8 t; s8 ret; 7928c2ecf20Sopenharmony_ci s16 max_strength = 0; 7938c2ecf20Sopenharmony_ci u8 str; 7948c2ecf20Sopenharmony_ci u16 i, gi = priv->curr_gi; 7958c2ecf20Sopenharmony_ci 7968c2ecf20Sopenharmony_ci dprintk("%s\n", __func__); 7978c2ecf20Sopenharmony_ci 7988c2ecf20Sopenharmony_ci ret = lgs8gxx_read_reg(priv, 0x4B, &t); 7998c2ecf20Sopenharmony_ci if (ret != 0) 8008c2ecf20Sopenharmony_ci return -EIO; 8018c2ecf20Sopenharmony_ci 8028c2ecf20Sopenharmony_ci if (fake_signal_str) { 8038c2ecf20Sopenharmony_ci if ((t & 0xC0) == 0xC0) { 8048c2ecf20Sopenharmony_ci dprintk("Fake signal strength\n"); 8058c2ecf20Sopenharmony_ci *signal = 0x7FFF; 8068c2ecf20Sopenharmony_ci } else 8078c2ecf20Sopenharmony_ci *signal = 0; 8088c2ecf20Sopenharmony_ci return 0; 8098c2ecf20Sopenharmony_ci } 8108c2ecf20Sopenharmony_ci 8118c2ecf20Sopenharmony_ci dprintk("gi = %d\n", gi); 8128c2ecf20Sopenharmony_ci for (i = 0; i < gi; i++) { 8138c2ecf20Sopenharmony_ci 8148c2ecf20Sopenharmony_ci if ((i & 0xFF) == 0) 8158c2ecf20Sopenharmony_ci lgs8gxx_write_reg(priv, 0x84, 0x03 & (i >> 8)); 8168c2ecf20Sopenharmony_ci lgs8gxx_write_reg(priv, 0x83, i & 0xFF); 8178c2ecf20Sopenharmony_ci 8188c2ecf20Sopenharmony_ci lgs8gxx_read_reg(priv, 0x94, &str); 8198c2ecf20Sopenharmony_ci if (max_strength < str) 8208c2ecf20Sopenharmony_ci max_strength = str; 8218c2ecf20Sopenharmony_ci } 8228c2ecf20Sopenharmony_ci 8238c2ecf20Sopenharmony_ci *signal = max_strength; 8248c2ecf20Sopenharmony_ci dprintk("%s: signal=0x%02X\n", __func__, *signal); 8258c2ecf20Sopenharmony_ci 8268c2ecf20Sopenharmony_ci lgs8gxx_read_reg(priv, 0x95, &t); 8278c2ecf20Sopenharmony_ci dprintk("%s: AVG Noise=0x%02X\n", __func__, t); 8288c2ecf20Sopenharmony_ci 8298c2ecf20Sopenharmony_ci return 0; 8308c2ecf20Sopenharmony_ci} 8318c2ecf20Sopenharmony_ci 8328c2ecf20Sopenharmony_cistatic int lgs8g75_read_signal_strength(struct lgs8gxx_state *priv, u16 *signal) 8338c2ecf20Sopenharmony_ci{ 8348c2ecf20Sopenharmony_ci u8 t; 8358c2ecf20Sopenharmony_ci s16 v = 0; 8368c2ecf20Sopenharmony_ci 8378c2ecf20Sopenharmony_ci dprintk("%s\n", __func__); 8388c2ecf20Sopenharmony_ci 8398c2ecf20Sopenharmony_ci lgs8gxx_read_reg(priv, 0xB1, &t); 8408c2ecf20Sopenharmony_ci v |= t; 8418c2ecf20Sopenharmony_ci v <<= 8; 8428c2ecf20Sopenharmony_ci lgs8gxx_read_reg(priv, 0xB0, &t); 8438c2ecf20Sopenharmony_ci v |= t; 8448c2ecf20Sopenharmony_ci 8458c2ecf20Sopenharmony_ci *signal = v; 8468c2ecf20Sopenharmony_ci dprintk("%s: signal=0x%02X\n", __func__, *signal); 8478c2ecf20Sopenharmony_ci 8488c2ecf20Sopenharmony_ci return 0; 8498c2ecf20Sopenharmony_ci} 8508c2ecf20Sopenharmony_ci 8518c2ecf20Sopenharmony_cistatic int lgs8gxx_read_signal_strength(struct dvb_frontend *fe, u16 *signal) 8528c2ecf20Sopenharmony_ci{ 8538c2ecf20Sopenharmony_ci struct lgs8gxx_state *priv = fe->demodulator_priv; 8548c2ecf20Sopenharmony_ci 8558c2ecf20Sopenharmony_ci if (priv->config->prod == LGS8GXX_PROD_LGS8913) 8568c2ecf20Sopenharmony_ci return lgs8913_read_signal_strength(priv, signal); 8578c2ecf20Sopenharmony_ci else if (priv->config->prod == LGS8GXX_PROD_LGS8G75) 8588c2ecf20Sopenharmony_ci return lgs8g75_read_signal_strength(priv, signal); 8598c2ecf20Sopenharmony_ci else 8608c2ecf20Sopenharmony_ci return lgs8gxx_read_signal_agc(priv, signal); 8618c2ecf20Sopenharmony_ci} 8628c2ecf20Sopenharmony_ci 8638c2ecf20Sopenharmony_cistatic int lgs8gxx_read_snr(struct dvb_frontend *fe, u16 *snr) 8648c2ecf20Sopenharmony_ci{ 8658c2ecf20Sopenharmony_ci struct lgs8gxx_state *priv = fe->demodulator_priv; 8668c2ecf20Sopenharmony_ci u8 t; 8678c2ecf20Sopenharmony_ci *snr = 0; 8688c2ecf20Sopenharmony_ci 8698c2ecf20Sopenharmony_ci if (priv->config->prod == LGS8GXX_PROD_LGS8G75) 8708c2ecf20Sopenharmony_ci lgs8gxx_read_reg(priv, 0x34, &t); 8718c2ecf20Sopenharmony_ci else 8728c2ecf20Sopenharmony_ci lgs8gxx_read_reg(priv, 0x95, &t); 8738c2ecf20Sopenharmony_ci dprintk("AVG Noise=0x%02X\n", t); 8748c2ecf20Sopenharmony_ci *snr = 256 - t; 8758c2ecf20Sopenharmony_ci *snr <<= 8; 8768c2ecf20Sopenharmony_ci dprintk("snr=0x%x\n", *snr); 8778c2ecf20Sopenharmony_ci 8788c2ecf20Sopenharmony_ci return 0; 8798c2ecf20Sopenharmony_ci} 8808c2ecf20Sopenharmony_ci 8818c2ecf20Sopenharmony_cistatic int lgs8gxx_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) 8828c2ecf20Sopenharmony_ci{ 8838c2ecf20Sopenharmony_ci *ucblocks = 0; 8848c2ecf20Sopenharmony_ci dprintk("%s: ucblocks=0x%x\n", __func__, *ucblocks); 8858c2ecf20Sopenharmony_ci return 0; 8868c2ecf20Sopenharmony_ci} 8878c2ecf20Sopenharmony_ci 8888c2ecf20Sopenharmony_cistatic void packet_counter_start(struct lgs8gxx_state *priv) 8898c2ecf20Sopenharmony_ci{ 8908c2ecf20Sopenharmony_ci u8 orig, t; 8918c2ecf20Sopenharmony_ci 8928c2ecf20Sopenharmony_ci if (priv->config->prod == LGS8GXX_PROD_LGS8G75) { 8938c2ecf20Sopenharmony_ci lgs8gxx_read_reg(priv, 0x30, &orig); 8948c2ecf20Sopenharmony_ci orig &= 0xE7; 8958c2ecf20Sopenharmony_ci t = orig | 0x10; 8968c2ecf20Sopenharmony_ci lgs8gxx_write_reg(priv, 0x30, t); 8978c2ecf20Sopenharmony_ci t = orig | 0x18; 8988c2ecf20Sopenharmony_ci lgs8gxx_write_reg(priv, 0x30, t); 8998c2ecf20Sopenharmony_ci t = orig | 0x10; 9008c2ecf20Sopenharmony_ci lgs8gxx_write_reg(priv, 0x30, t); 9018c2ecf20Sopenharmony_ci } else { 9028c2ecf20Sopenharmony_ci lgs8gxx_write_reg(priv, 0xC6, 0x01); 9038c2ecf20Sopenharmony_ci lgs8gxx_write_reg(priv, 0xC6, 0x41); 9048c2ecf20Sopenharmony_ci lgs8gxx_write_reg(priv, 0xC6, 0x01); 9058c2ecf20Sopenharmony_ci } 9068c2ecf20Sopenharmony_ci} 9078c2ecf20Sopenharmony_ci 9088c2ecf20Sopenharmony_cistatic void packet_counter_stop(struct lgs8gxx_state *priv) 9098c2ecf20Sopenharmony_ci{ 9108c2ecf20Sopenharmony_ci u8 t; 9118c2ecf20Sopenharmony_ci 9128c2ecf20Sopenharmony_ci if (priv->config->prod == LGS8GXX_PROD_LGS8G75) { 9138c2ecf20Sopenharmony_ci lgs8gxx_read_reg(priv, 0x30, &t); 9148c2ecf20Sopenharmony_ci t &= 0xE7; 9158c2ecf20Sopenharmony_ci lgs8gxx_write_reg(priv, 0x30, t); 9168c2ecf20Sopenharmony_ci } else { 9178c2ecf20Sopenharmony_ci lgs8gxx_write_reg(priv, 0xC6, 0x81); 9188c2ecf20Sopenharmony_ci } 9198c2ecf20Sopenharmony_ci} 9208c2ecf20Sopenharmony_ci 9218c2ecf20Sopenharmony_cistatic int lgs8gxx_read_ber(struct dvb_frontend *fe, u32 *ber) 9228c2ecf20Sopenharmony_ci{ 9238c2ecf20Sopenharmony_ci struct lgs8gxx_state *priv = fe->demodulator_priv; 9248c2ecf20Sopenharmony_ci u8 reg_err, reg_total, t; 9258c2ecf20Sopenharmony_ci u32 total_cnt = 0, err_cnt = 0; 9268c2ecf20Sopenharmony_ci int i; 9278c2ecf20Sopenharmony_ci 9288c2ecf20Sopenharmony_ci dprintk("%s\n", __func__); 9298c2ecf20Sopenharmony_ci 9308c2ecf20Sopenharmony_ci packet_counter_start(priv); 9318c2ecf20Sopenharmony_ci msleep(200); 9328c2ecf20Sopenharmony_ci packet_counter_stop(priv); 9338c2ecf20Sopenharmony_ci 9348c2ecf20Sopenharmony_ci if (priv->config->prod == LGS8GXX_PROD_LGS8G75) { 9358c2ecf20Sopenharmony_ci reg_total = 0x28; reg_err = 0x2C; 9368c2ecf20Sopenharmony_ci } else { 9378c2ecf20Sopenharmony_ci reg_total = 0xD0; reg_err = 0xD4; 9388c2ecf20Sopenharmony_ci } 9398c2ecf20Sopenharmony_ci 9408c2ecf20Sopenharmony_ci for (i = 0; i < 4; i++) { 9418c2ecf20Sopenharmony_ci total_cnt <<= 8; 9428c2ecf20Sopenharmony_ci lgs8gxx_read_reg(priv, reg_total+3-i, &t); 9438c2ecf20Sopenharmony_ci total_cnt |= t; 9448c2ecf20Sopenharmony_ci } 9458c2ecf20Sopenharmony_ci for (i = 0; i < 4; i++) { 9468c2ecf20Sopenharmony_ci err_cnt <<= 8; 9478c2ecf20Sopenharmony_ci lgs8gxx_read_reg(priv, reg_err+3-i, &t); 9488c2ecf20Sopenharmony_ci err_cnt |= t; 9498c2ecf20Sopenharmony_ci } 9508c2ecf20Sopenharmony_ci dprintk("error=%d total=%d\n", err_cnt, total_cnt); 9518c2ecf20Sopenharmony_ci 9528c2ecf20Sopenharmony_ci if (total_cnt == 0) 9538c2ecf20Sopenharmony_ci *ber = 0; 9548c2ecf20Sopenharmony_ci else 9558c2ecf20Sopenharmony_ci *ber = err_cnt * 100 / total_cnt; 9568c2ecf20Sopenharmony_ci 9578c2ecf20Sopenharmony_ci dprintk("%s: ber=0x%x\n", __func__, *ber); 9588c2ecf20Sopenharmony_ci return 0; 9598c2ecf20Sopenharmony_ci} 9608c2ecf20Sopenharmony_ci 9618c2ecf20Sopenharmony_cistatic int lgs8gxx_i2c_gate_ctrl(struct dvb_frontend *fe, int enable) 9628c2ecf20Sopenharmony_ci{ 9638c2ecf20Sopenharmony_ci struct lgs8gxx_state *priv = fe->demodulator_priv; 9648c2ecf20Sopenharmony_ci 9658c2ecf20Sopenharmony_ci if (priv->config->tuner_address == 0) 9668c2ecf20Sopenharmony_ci return 0; 9678c2ecf20Sopenharmony_ci if (enable) { 9688c2ecf20Sopenharmony_ci u8 v = 0x80 | priv->config->tuner_address; 9698c2ecf20Sopenharmony_ci return lgs8gxx_write_reg(priv, 0x01, v); 9708c2ecf20Sopenharmony_ci } 9718c2ecf20Sopenharmony_ci return lgs8gxx_write_reg(priv, 0x01, 0); 9728c2ecf20Sopenharmony_ci} 9738c2ecf20Sopenharmony_ci 9748c2ecf20Sopenharmony_cistatic const struct dvb_frontend_ops lgs8gxx_ops = { 9758c2ecf20Sopenharmony_ci .delsys = { SYS_DTMB }, 9768c2ecf20Sopenharmony_ci .info = { 9778c2ecf20Sopenharmony_ci .name = "Legend Silicon LGS8913/LGS8GXX DMB-TH", 9788c2ecf20Sopenharmony_ci .frequency_min_hz = 474 * MHz, 9798c2ecf20Sopenharmony_ci .frequency_max_hz = 858 * MHz, 9808c2ecf20Sopenharmony_ci .frequency_stepsize_hz = 10 * kHz, 9818c2ecf20Sopenharmony_ci .caps = 9828c2ecf20Sopenharmony_ci FE_CAN_FEC_AUTO | 9838c2ecf20Sopenharmony_ci FE_CAN_QAM_AUTO | 9848c2ecf20Sopenharmony_ci FE_CAN_TRANSMISSION_MODE_AUTO | 9858c2ecf20Sopenharmony_ci FE_CAN_GUARD_INTERVAL_AUTO 9868c2ecf20Sopenharmony_ci }, 9878c2ecf20Sopenharmony_ci 9888c2ecf20Sopenharmony_ci .release = lgs8gxx_release, 9898c2ecf20Sopenharmony_ci 9908c2ecf20Sopenharmony_ci .init = lgs8gxx_init, 9918c2ecf20Sopenharmony_ci .write = lgs8gxx_write, 9928c2ecf20Sopenharmony_ci .i2c_gate_ctrl = lgs8gxx_i2c_gate_ctrl, 9938c2ecf20Sopenharmony_ci 9948c2ecf20Sopenharmony_ci .set_frontend = lgs8gxx_set_fe, 9958c2ecf20Sopenharmony_ci .get_tune_settings = lgs8gxx_get_tune_settings, 9968c2ecf20Sopenharmony_ci 9978c2ecf20Sopenharmony_ci .read_status = lgs8gxx_read_status, 9988c2ecf20Sopenharmony_ci .read_ber = lgs8gxx_read_ber, 9998c2ecf20Sopenharmony_ci .read_signal_strength = lgs8gxx_read_signal_strength, 10008c2ecf20Sopenharmony_ci .read_snr = lgs8gxx_read_snr, 10018c2ecf20Sopenharmony_ci .read_ucblocks = lgs8gxx_read_ucblocks, 10028c2ecf20Sopenharmony_ci}; 10038c2ecf20Sopenharmony_ci 10048c2ecf20Sopenharmony_cistruct dvb_frontend *lgs8gxx_attach(const struct lgs8gxx_config *config, 10058c2ecf20Sopenharmony_ci struct i2c_adapter *i2c) 10068c2ecf20Sopenharmony_ci{ 10078c2ecf20Sopenharmony_ci struct lgs8gxx_state *priv = NULL; 10088c2ecf20Sopenharmony_ci u8 data = 0; 10098c2ecf20Sopenharmony_ci 10108c2ecf20Sopenharmony_ci dprintk("%s()\n", __func__); 10118c2ecf20Sopenharmony_ci 10128c2ecf20Sopenharmony_ci if (config == NULL || i2c == NULL) 10138c2ecf20Sopenharmony_ci return NULL; 10148c2ecf20Sopenharmony_ci 10158c2ecf20Sopenharmony_ci priv = kzalloc(sizeof(struct lgs8gxx_state), GFP_KERNEL); 10168c2ecf20Sopenharmony_ci if (priv == NULL) 10178c2ecf20Sopenharmony_ci goto error_out; 10188c2ecf20Sopenharmony_ci 10198c2ecf20Sopenharmony_ci priv->config = config; 10208c2ecf20Sopenharmony_ci priv->i2c = i2c; 10218c2ecf20Sopenharmony_ci 10228c2ecf20Sopenharmony_ci /* check if the demod is there */ 10238c2ecf20Sopenharmony_ci if (lgs8gxx_read_reg(priv, 0, &data) != 0) { 10248c2ecf20Sopenharmony_ci dprintk("%s lgs8gxx not found at i2c addr 0x%02X\n", 10258c2ecf20Sopenharmony_ci __func__, priv->config->demod_address); 10268c2ecf20Sopenharmony_ci goto error_out; 10278c2ecf20Sopenharmony_ci } 10288c2ecf20Sopenharmony_ci 10298c2ecf20Sopenharmony_ci lgs8gxx_read_reg(priv, 1, &data); 10308c2ecf20Sopenharmony_ci 10318c2ecf20Sopenharmony_ci memcpy(&priv->frontend.ops, &lgs8gxx_ops, 10328c2ecf20Sopenharmony_ci sizeof(struct dvb_frontend_ops)); 10338c2ecf20Sopenharmony_ci priv->frontend.demodulator_priv = priv; 10348c2ecf20Sopenharmony_ci 10358c2ecf20Sopenharmony_ci if (config->prod == LGS8GXX_PROD_LGS8G75) 10368c2ecf20Sopenharmony_ci lgs8g75_init_data(priv); 10378c2ecf20Sopenharmony_ci 10388c2ecf20Sopenharmony_ci return &priv->frontend; 10398c2ecf20Sopenharmony_ci 10408c2ecf20Sopenharmony_cierror_out: 10418c2ecf20Sopenharmony_ci dprintk("%s() error_out\n", __func__); 10428c2ecf20Sopenharmony_ci kfree(priv); 10438c2ecf20Sopenharmony_ci return NULL; 10448c2ecf20Sopenharmony_ci 10458c2ecf20Sopenharmony_ci} 10468c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(lgs8gxx_attach); 10478c2ecf20Sopenharmony_ci 10488c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Legend Silicon LGS8913/LGS8GXX DMB-TH demodulator driver"); 10498c2ecf20Sopenharmony_ciMODULE_AUTHOR("David T. L. Wong <davidtlwong@gmail.com>"); 10508c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 10518c2ecf20Sopenharmony_ciMODULE_FIRMWARE(LGS8GXX_FIRMWARE); 1052