18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Sony CXD2820R demodulator driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2010 Antti Palosaari <crope@iki.fi> 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include "cxd2820r_priv.h" 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ciint cxd2820r_set_frontend_c(struct dvb_frontend *fe) 128c2ecf20Sopenharmony_ci{ 138c2ecf20Sopenharmony_ci struct cxd2820r_priv *priv = fe->demodulator_priv; 148c2ecf20Sopenharmony_ci struct i2c_client *client = priv->client[0]; 158c2ecf20Sopenharmony_ci struct dtv_frontend_properties *c = &fe->dtv_property_cache; 168c2ecf20Sopenharmony_ci int ret; 178c2ecf20Sopenharmony_ci unsigned int utmp; 188c2ecf20Sopenharmony_ci u8 buf[2]; 198c2ecf20Sopenharmony_ci u32 if_frequency; 208c2ecf20Sopenharmony_ci struct reg_val_mask tab[] = { 218c2ecf20Sopenharmony_ci { 0x00080, 0x01, 0xff }, 228c2ecf20Sopenharmony_ci { 0x00081, 0x05, 0xff }, 238c2ecf20Sopenharmony_ci { 0x00085, 0x07, 0xff }, 248c2ecf20Sopenharmony_ci { 0x00088, 0x01, 0xff }, 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci { 0x00082, 0x20, 0x60 }, 278c2ecf20Sopenharmony_ci { 0x1016a, 0x48, 0xff }, 288c2ecf20Sopenharmony_ci { 0x100a5, 0x00, 0x01 }, 298c2ecf20Sopenharmony_ci { 0x10020, 0x06, 0x07 }, 308c2ecf20Sopenharmony_ci { 0x10059, 0x50, 0xff }, 318c2ecf20Sopenharmony_ci { 0x10087, 0x0c, 0x3c }, 328c2ecf20Sopenharmony_ci { 0x1008b, 0x07, 0xff }, 338c2ecf20Sopenharmony_ci { 0x1001f, priv->if_agc_polarity << 7, 0x80 }, 348c2ecf20Sopenharmony_ci { 0x10070, priv->ts_mode, 0xff }, 358c2ecf20Sopenharmony_ci { 0x10071, !priv->ts_clk_inv << 4, 0x10 }, 368c2ecf20Sopenharmony_ci }; 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci dev_dbg(&client->dev, 398c2ecf20Sopenharmony_ci "delivery_system=%d modulation=%d frequency=%u symbol_rate=%u inversion=%d\n", 408c2ecf20Sopenharmony_ci c->delivery_system, c->modulation, c->frequency, 418c2ecf20Sopenharmony_ci c->symbol_rate, c->inversion); 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci /* program tuner */ 448c2ecf20Sopenharmony_ci if (fe->ops.tuner_ops.set_params) 458c2ecf20Sopenharmony_ci fe->ops.tuner_ops.set_params(fe); 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci if (priv->delivery_system != SYS_DVBC_ANNEX_A) { 488c2ecf20Sopenharmony_ci ret = cxd2820r_wr_reg_val_mask_tab(priv, tab, ARRAY_SIZE(tab)); 498c2ecf20Sopenharmony_ci if (ret) 508c2ecf20Sopenharmony_ci goto error; 518c2ecf20Sopenharmony_ci } 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci priv->delivery_system = SYS_DVBC_ANNEX_A; 548c2ecf20Sopenharmony_ci priv->ber_running = false; /* tune stops BER counter */ 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci /* program IF frequency */ 578c2ecf20Sopenharmony_ci if (fe->ops.tuner_ops.get_if_frequency) { 588c2ecf20Sopenharmony_ci ret = fe->ops.tuner_ops.get_if_frequency(fe, &if_frequency); 598c2ecf20Sopenharmony_ci if (ret) 608c2ecf20Sopenharmony_ci goto error; 618c2ecf20Sopenharmony_ci dev_dbg(&client->dev, "if_frequency=%u\n", if_frequency); 628c2ecf20Sopenharmony_ci } else { 638c2ecf20Sopenharmony_ci ret = -EINVAL; 648c2ecf20Sopenharmony_ci goto error; 658c2ecf20Sopenharmony_ci } 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci utmp = 0x4000 - DIV_ROUND_CLOSEST_ULL((u64)if_frequency * 0x4000, CXD2820R_CLK); 688c2ecf20Sopenharmony_ci buf[0] = (utmp >> 8) & 0xff; 698c2ecf20Sopenharmony_ci buf[1] = (utmp >> 0) & 0xff; 708c2ecf20Sopenharmony_ci ret = regmap_bulk_write(priv->regmap[1], 0x0042, buf, 2); 718c2ecf20Sopenharmony_ci if (ret) 728c2ecf20Sopenharmony_ci goto error; 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci ret = regmap_write(priv->regmap[0], 0x00ff, 0x08); 758c2ecf20Sopenharmony_ci if (ret) 768c2ecf20Sopenharmony_ci goto error; 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci ret = regmap_write(priv->regmap[0], 0x00fe, 0x01); 798c2ecf20Sopenharmony_ci if (ret) 808c2ecf20Sopenharmony_ci goto error; 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci return ret; 838c2ecf20Sopenharmony_cierror: 848c2ecf20Sopenharmony_ci dev_dbg(&client->dev, "failed=%d\n", ret); 858c2ecf20Sopenharmony_ci return ret; 868c2ecf20Sopenharmony_ci} 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ciint cxd2820r_get_frontend_c(struct dvb_frontend *fe, 898c2ecf20Sopenharmony_ci struct dtv_frontend_properties *c) 908c2ecf20Sopenharmony_ci{ 918c2ecf20Sopenharmony_ci struct cxd2820r_priv *priv = fe->demodulator_priv; 928c2ecf20Sopenharmony_ci struct i2c_client *client = priv->client[0]; 938c2ecf20Sopenharmony_ci int ret; 948c2ecf20Sopenharmony_ci unsigned int utmp; 958c2ecf20Sopenharmony_ci u8 buf[2]; 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci dev_dbg(&client->dev, "\n"); 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci ret = regmap_bulk_read(priv->regmap[1], 0x001a, buf, 2); 1008c2ecf20Sopenharmony_ci if (ret) 1018c2ecf20Sopenharmony_ci goto error; 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci c->symbol_rate = 2500 * ((buf[0] & 0x0f) << 8 | buf[1]); 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci ret = regmap_read(priv->regmap[1], 0x0019, &utmp); 1068c2ecf20Sopenharmony_ci if (ret) 1078c2ecf20Sopenharmony_ci goto error; 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci switch ((utmp >> 0) & 0x07) { 1108c2ecf20Sopenharmony_ci case 0: 1118c2ecf20Sopenharmony_ci c->modulation = QAM_16; 1128c2ecf20Sopenharmony_ci break; 1138c2ecf20Sopenharmony_ci case 1: 1148c2ecf20Sopenharmony_ci c->modulation = QAM_32; 1158c2ecf20Sopenharmony_ci break; 1168c2ecf20Sopenharmony_ci case 2: 1178c2ecf20Sopenharmony_ci c->modulation = QAM_64; 1188c2ecf20Sopenharmony_ci break; 1198c2ecf20Sopenharmony_ci case 3: 1208c2ecf20Sopenharmony_ci c->modulation = QAM_128; 1218c2ecf20Sopenharmony_ci break; 1228c2ecf20Sopenharmony_ci case 4: 1238c2ecf20Sopenharmony_ci c->modulation = QAM_256; 1248c2ecf20Sopenharmony_ci break; 1258c2ecf20Sopenharmony_ci } 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci switch ((utmp >> 7) & 0x01) { 1288c2ecf20Sopenharmony_ci case 0: 1298c2ecf20Sopenharmony_ci c->inversion = INVERSION_OFF; 1308c2ecf20Sopenharmony_ci break; 1318c2ecf20Sopenharmony_ci case 1: 1328c2ecf20Sopenharmony_ci c->inversion = INVERSION_ON; 1338c2ecf20Sopenharmony_ci break; 1348c2ecf20Sopenharmony_ci } 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci return ret; 1378c2ecf20Sopenharmony_cierror: 1388c2ecf20Sopenharmony_ci dev_dbg(&client->dev, "failed=%d\n", ret); 1398c2ecf20Sopenharmony_ci return ret; 1408c2ecf20Sopenharmony_ci} 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ciint cxd2820r_read_status_c(struct dvb_frontend *fe, enum fe_status *status) 1438c2ecf20Sopenharmony_ci{ 1448c2ecf20Sopenharmony_ci struct cxd2820r_priv *priv = fe->demodulator_priv; 1458c2ecf20Sopenharmony_ci struct i2c_client *client = priv->client[0]; 1468c2ecf20Sopenharmony_ci struct dtv_frontend_properties *c = &fe->dtv_property_cache; 1478c2ecf20Sopenharmony_ci int ret; 1488c2ecf20Sopenharmony_ci unsigned int utmp, utmp1, utmp2; 1498c2ecf20Sopenharmony_ci u8 buf[3]; 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci /* Lock detection */ 1528c2ecf20Sopenharmony_ci ret = regmap_bulk_read(priv->regmap[1], 0x0088, &buf[0], 1); 1538c2ecf20Sopenharmony_ci if (ret) 1548c2ecf20Sopenharmony_ci goto error; 1558c2ecf20Sopenharmony_ci ret = regmap_bulk_read(priv->regmap[1], 0x0073, &buf[1], 1); 1568c2ecf20Sopenharmony_ci if (ret) 1578c2ecf20Sopenharmony_ci goto error; 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci utmp1 = (buf[0] >> 0) & 0x01; 1608c2ecf20Sopenharmony_ci utmp2 = (buf[1] >> 3) & 0x01; 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci if (utmp1 == 1 && utmp2 == 1) { 1638c2ecf20Sopenharmony_ci *status = FE_HAS_SIGNAL | FE_HAS_CARRIER | 1648c2ecf20Sopenharmony_ci FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK; 1658c2ecf20Sopenharmony_ci } else if (utmp1 == 1 || utmp2 == 1) { 1668c2ecf20Sopenharmony_ci *status = FE_HAS_SIGNAL | FE_HAS_CARRIER | 1678c2ecf20Sopenharmony_ci FE_HAS_VITERBI | FE_HAS_SYNC; 1688c2ecf20Sopenharmony_ci } else { 1698c2ecf20Sopenharmony_ci *status = 0; 1708c2ecf20Sopenharmony_ci } 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci dev_dbg(&client->dev, "status=%02x raw=%*ph sync=%u ts=%u\n", 1738c2ecf20Sopenharmony_ci *status, 2, buf, utmp1, utmp2); 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci /* Signal strength */ 1768c2ecf20Sopenharmony_ci if (*status & FE_HAS_SIGNAL) { 1778c2ecf20Sopenharmony_ci unsigned int strength; 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci ret = regmap_bulk_read(priv->regmap[1], 0x0049, buf, 2); 1808c2ecf20Sopenharmony_ci if (ret) 1818c2ecf20Sopenharmony_ci goto error; 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci utmp = buf[0] << 8 | buf[1] << 0; 1848c2ecf20Sopenharmony_ci utmp = 511 - sign_extend32(utmp, 9); 1858c2ecf20Sopenharmony_ci /* Scale value to 0x0000-0xffff */ 1868c2ecf20Sopenharmony_ci strength = utmp << 6 | utmp >> 4; 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci c->strength.len = 1; 1898c2ecf20Sopenharmony_ci c->strength.stat[0].scale = FE_SCALE_RELATIVE; 1908c2ecf20Sopenharmony_ci c->strength.stat[0].uvalue = strength; 1918c2ecf20Sopenharmony_ci } else { 1928c2ecf20Sopenharmony_ci c->strength.len = 1; 1938c2ecf20Sopenharmony_ci c->strength.stat[0].scale = FE_SCALE_NOT_AVAILABLE; 1948c2ecf20Sopenharmony_ci } 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci /* CNR */ 1978c2ecf20Sopenharmony_ci if (*status & FE_HAS_VITERBI) { 1988c2ecf20Sopenharmony_ci unsigned int cnr, const_a, const_b; 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci ret = regmap_read(priv->regmap[1], 0x0019, &utmp); 2018c2ecf20Sopenharmony_ci if (ret) 2028c2ecf20Sopenharmony_ci goto error; 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci if (((utmp >> 0) & 0x03) % 2) { 2058c2ecf20Sopenharmony_ci const_a = 8750; 2068c2ecf20Sopenharmony_ci const_b = 650; 2078c2ecf20Sopenharmony_ci } else { 2088c2ecf20Sopenharmony_ci const_a = 9500; 2098c2ecf20Sopenharmony_ci const_b = 760; 2108c2ecf20Sopenharmony_ci } 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci ret = regmap_read(priv->regmap[1], 0x004d, &utmp); 2138c2ecf20Sopenharmony_ci if (ret) 2148c2ecf20Sopenharmony_ci goto error; 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci #define CXD2820R_LOG2_E_24 24204406 /* log2(e) << 24 */ 2178c2ecf20Sopenharmony_ci if (utmp) 2188c2ecf20Sopenharmony_ci cnr = div_u64((u64)(intlog2(const_b) - intlog2(utmp)) 2198c2ecf20Sopenharmony_ci * const_a, CXD2820R_LOG2_E_24); 2208c2ecf20Sopenharmony_ci else 2218c2ecf20Sopenharmony_ci cnr = 0; 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci c->cnr.len = 1; 2248c2ecf20Sopenharmony_ci c->cnr.stat[0].scale = FE_SCALE_DECIBEL; 2258c2ecf20Sopenharmony_ci c->cnr.stat[0].svalue = cnr; 2268c2ecf20Sopenharmony_ci } else { 2278c2ecf20Sopenharmony_ci c->cnr.len = 1; 2288c2ecf20Sopenharmony_ci c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE; 2298c2ecf20Sopenharmony_ci } 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci /* BER */ 2328c2ecf20Sopenharmony_ci if (*status & FE_HAS_SYNC) { 2338c2ecf20Sopenharmony_ci unsigned int post_bit_error; 2348c2ecf20Sopenharmony_ci bool start_ber; 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci if (priv->ber_running) { 2378c2ecf20Sopenharmony_ci ret = regmap_bulk_read(priv->regmap[1], 0x0076, buf, 3); 2388c2ecf20Sopenharmony_ci if (ret) 2398c2ecf20Sopenharmony_ci goto error; 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci if ((buf[2] >> 7) & 0x01) { 2428c2ecf20Sopenharmony_ci post_bit_error = buf[2] << 16 | buf[1] << 8 | 2438c2ecf20Sopenharmony_ci buf[0] << 0; 2448c2ecf20Sopenharmony_ci post_bit_error &= 0x0fffff; 2458c2ecf20Sopenharmony_ci start_ber = true; 2468c2ecf20Sopenharmony_ci } else { 2478c2ecf20Sopenharmony_ci post_bit_error = 0; 2488c2ecf20Sopenharmony_ci start_ber = false; 2498c2ecf20Sopenharmony_ci } 2508c2ecf20Sopenharmony_ci } else { 2518c2ecf20Sopenharmony_ci post_bit_error = 0; 2528c2ecf20Sopenharmony_ci start_ber = true; 2538c2ecf20Sopenharmony_ci } 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci if (start_ber) { 2568c2ecf20Sopenharmony_ci ret = regmap_write(priv->regmap[1], 0x0079, 0x01); 2578c2ecf20Sopenharmony_ci if (ret) 2588c2ecf20Sopenharmony_ci goto error; 2598c2ecf20Sopenharmony_ci priv->ber_running = true; 2608c2ecf20Sopenharmony_ci } 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci priv->post_bit_error += post_bit_error; 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci c->post_bit_error.len = 1; 2658c2ecf20Sopenharmony_ci c->post_bit_error.stat[0].scale = FE_SCALE_COUNTER; 2668c2ecf20Sopenharmony_ci c->post_bit_error.stat[0].uvalue = priv->post_bit_error; 2678c2ecf20Sopenharmony_ci } else { 2688c2ecf20Sopenharmony_ci c->post_bit_error.len = 1; 2698c2ecf20Sopenharmony_ci c->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; 2708c2ecf20Sopenharmony_ci } 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci return ret; 2738c2ecf20Sopenharmony_cierror: 2748c2ecf20Sopenharmony_ci dev_dbg(&client->dev, "failed=%d\n", ret); 2758c2ecf20Sopenharmony_ci return ret; 2768c2ecf20Sopenharmony_ci} 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ciint cxd2820r_init_c(struct dvb_frontend *fe) 2798c2ecf20Sopenharmony_ci{ 2808c2ecf20Sopenharmony_ci struct cxd2820r_priv *priv = fe->demodulator_priv; 2818c2ecf20Sopenharmony_ci struct i2c_client *client = priv->client[0]; 2828c2ecf20Sopenharmony_ci int ret; 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci dev_dbg(&client->dev, "\n"); 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci ret = regmap_write(priv->regmap[0], 0x0085, 0x07); 2878c2ecf20Sopenharmony_ci if (ret) 2888c2ecf20Sopenharmony_ci goto error; 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci return ret; 2918c2ecf20Sopenharmony_cierror: 2928c2ecf20Sopenharmony_ci dev_dbg(&client->dev, "failed=%d\n", ret); 2938c2ecf20Sopenharmony_ci return ret; 2948c2ecf20Sopenharmony_ci} 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ciint cxd2820r_sleep_c(struct dvb_frontend *fe) 2978c2ecf20Sopenharmony_ci{ 2988c2ecf20Sopenharmony_ci struct cxd2820r_priv *priv = fe->demodulator_priv; 2998c2ecf20Sopenharmony_ci struct i2c_client *client = priv->client[0]; 3008c2ecf20Sopenharmony_ci int ret; 3018c2ecf20Sopenharmony_ci static const struct reg_val_mask tab[] = { 3028c2ecf20Sopenharmony_ci { 0x000ff, 0x1f, 0xff }, 3038c2ecf20Sopenharmony_ci { 0x00085, 0x00, 0xff }, 3048c2ecf20Sopenharmony_ci { 0x00088, 0x01, 0xff }, 3058c2ecf20Sopenharmony_ci { 0x00081, 0x00, 0xff }, 3068c2ecf20Sopenharmony_ci { 0x00080, 0x00, 0xff }, 3078c2ecf20Sopenharmony_ci }; 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci dev_dbg(&client->dev, "\n"); 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci priv->delivery_system = SYS_UNDEFINED; 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci ret = cxd2820r_wr_reg_val_mask_tab(priv, tab, ARRAY_SIZE(tab)); 3148c2ecf20Sopenharmony_ci if (ret) 3158c2ecf20Sopenharmony_ci goto error; 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci return ret; 3188c2ecf20Sopenharmony_cierror: 3198c2ecf20Sopenharmony_ci dev_dbg(&client->dev, "failed=%d\n", ret); 3208c2ecf20Sopenharmony_ci return ret; 3218c2ecf20Sopenharmony_ci} 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ciint cxd2820r_get_tune_settings_c(struct dvb_frontend *fe, 3248c2ecf20Sopenharmony_ci struct dvb_frontend_tune_settings *s) 3258c2ecf20Sopenharmony_ci{ 3268c2ecf20Sopenharmony_ci s->min_delay_ms = 500; 3278c2ecf20Sopenharmony_ci s->step_size = 0; /* no zigzag */ 3288c2ecf20Sopenharmony_ci s->max_drift = 0; 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci return 0; 3318c2ecf20Sopenharmony_ci} 332