Path: blob/master/drivers/media/dvb/frontends/cxd2820r_c.c
15112 views
/*1* Sony CXD2820R demodulator driver2*3* Copyright (C) 2010 Antti Palosaari <[email protected]>4*5* This program is free software; you can redistribute it and/or modify6* it under the terms of the GNU General Public License as published by7* the Free Software Foundation; either version 2 of the License, or8* (at your option) any later version.9*10* This program is distributed in the hope that it will be useful,11* but WITHOUT ANY WARRANTY; without even the implied warranty of12* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the13* GNU General Public License for more details.14*15* You should have received a copy of the GNU General Public License along16* with this program; if not, write to the Free Software Foundation, Inc.,17* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.18*/192021#include "cxd2820r_priv.h"2223int cxd2820r_set_frontend_c(struct dvb_frontend *fe,24struct dvb_frontend_parameters *params)25{26struct cxd2820r_priv *priv = fe->demodulator_priv;27struct dtv_frontend_properties *c = &fe->dtv_property_cache;28int ret, i;29u8 buf[2];30u16 if_ctl;31u64 num;32struct reg_val_mask tab[] = {33{ 0x00080, 0x01, 0xff },34{ 0x00081, 0x05, 0xff },35{ 0x00085, 0x07, 0xff },36{ 0x00088, 0x01, 0xff },3738{ 0x00082, 0x20, 0x60 },39{ 0x1016a, 0x48, 0xff },40{ 0x100a5, 0x00, 0x01 },41{ 0x10020, 0x06, 0x07 },42{ 0x10059, 0x50, 0xff },43{ 0x10087, 0x0c, 0x3c },44{ 0x1008b, 0x07, 0xff },45{ 0x1001f, priv->cfg.if_agc_polarity << 7, 0x80 },46{ 0x10070, priv->cfg.ts_mode, 0xff },47};4849dbg("%s: RF=%d SR=%d", __func__, c->frequency, c->symbol_rate);5051/* update GPIOs */52ret = cxd2820r_gpio(fe);53if (ret)54goto error;5556/* program tuner */57if (fe->ops.tuner_ops.set_params)58fe->ops.tuner_ops.set_params(fe, params);5960if (priv->delivery_system != SYS_DVBC_ANNEX_AC) {61for (i = 0; i < ARRAY_SIZE(tab); i++) {62ret = cxd2820r_wr_reg_mask(priv, tab[i].reg,63tab[i].val, tab[i].mask);64if (ret)65goto error;66}67}6869priv->delivery_system = SYS_DVBC_ANNEX_AC;70priv->ber_running = 0; /* tune stops BER counter */7172num = priv->cfg.if_dvbc;73num *= 0x4000;74if_ctl = cxd2820r_div_u64_round_closest(num, 41000);75buf[0] = (if_ctl >> 8) & 0x3f;76buf[1] = (if_ctl >> 0) & 0xff;7778ret = cxd2820r_wr_regs(priv, 0x10042, buf, 2);79if (ret)80goto error;8182ret = cxd2820r_wr_reg(priv, 0x000ff, 0x08);83if (ret)84goto error;8586ret = cxd2820r_wr_reg(priv, 0x000fe, 0x01);87if (ret)88goto error;8990return ret;91error:92dbg("%s: failed:%d", __func__, ret);93return ret;94}9596int cxd2820r_get_frontend_c(struct dvb_frontend *fe,97struct dvb_frontend_parameters *p)98{99struct cxd2820r_priv *priv = fe->demodulator_priv;100struct dtv_frontend_properties *c = &fe->dtv_property_cache;101int ret;102u8 buf[2];103104ret = cxd2820r_rd_regs(priv, 0x1001a, buf, 2);105if (ret)106goto error;107108c->symbol_rate = 2500 * ((buf[0] & 0x0f) << 8 | buf[1]);109110ret = cxd2820r_rd_reg(priv, 0x10019, &buf[0]);111if (ret)112goto error;113114switch ((buf[0] >> 0) & 0x03) {115case 0:116c->modulation = QAM_16;117break;118case 1:119c->modulation = QAM_32;120break;121case 2:122c->modulation = QAM_64;123break;124case 3:125c->modulation = QAM_128;126break;127case 4:128c->modulation = QAM_256;129break;130}131132switch ((buf[0] >> 7) & 0x01) {133case 0:134c->inversion = INVERSION_OFF;135break;136case 1:137c->inversion = INVERSION_ON;138break;139}140141return ret;142error:143dbg("%s: failed:%d", __func__, ret);144return ret;145}146147int cxd2820r_read_ber_c(struct dvb_frontend *fe, u32 *ber)148{149struct cxd2820r_priv *priv = fe->demodulator_priv;150int ret;151u8 buf[3], start_ber = 0;152*ber = 0;153154if (priv->ber_running) {155ret = cxd2820r_rd_regs(priv, 0x10076, buf, sizeof(buf));156if (ret)157goto error;158159if ((buf[2] >> 7) & 0x01 || (buf[2] >> 4) & 0x01) {160*ber = (buf[2] & 0x0f) << 16 | buf[1] << 8 | buf[0];161start_ber = 1;162}163} else {164priv->ber_running = 1;165start_ber = 1;166}167168if (start_ber) {169/* (re)start BER */170ret = cxd2820r_wr_reg(priv, 0x10079, 0x01);171if (ret)172goto error;173}174175return ret;176error:177dbg("%s: failed:%d", __func__, ret);178return ret;179}180181int cxd2820r_read_signal_strength_c(struct dvb_frontend *fe,182u16 *strength)183{184struct cxd2820r_priv *priv = fe->demodulator_priv;185int ret;186u8 buf[2];187u16 tmp;188189ret = cxd2820r_rd_regs(priv, 0x10049, buf, sizeof(buf));190if (ret)191goto error;192193tmp = (buf[0] & 0x03) << 8 | buf[1];194tmp = (~tmp & 0x03ff);195196if (tmp == 512)197/* ~no signal */198tmp = 0;199else if (tmp > 350)200tmp = 350;201202/* scale value to 0x0000-0xffff */203*strength = tmp * 0xffff / (350-0);204205return ret;206error:207dbg("%s: failed:%d", __func__, ret);208return ret;209}210211int cxd2820r_read_snr_c(struct dvb_frontend *fe, u16 *snr)212{213struct cxd2820r_priv *priv = fe->demodulator_priv;214int ret;215u8 tmp;216unsigned int A, B;217/* report SNR in dB * 10 */218219ret = cxd2820r_rd_reg(priv, 0x10019, &tmp);220if (ret)221goto error;222223if (((tmp >> 0) & 0x03) % 2) {224A = 875;225B = 650;226} else {227A = 950;228B = 760;229}230231ret = cxd2820r_rd_reg(priv, 0x1004d, &tmp);232if (ret)233goto error;234235#define CXD2820R_LOG2_E_24 24204406 /* log2(e) << 24 */236if (tmp)237*snr = A * (intlog2(B / tmp) >> 5) / (CXD2820R_LOG2_E_24 >> 5)238/ 10;239else240*snr = 0;241242return ret;243error:244dbg("%s: failed:%d", __func__, ret);245return ret;246}247248int cxd2820r_read_ucblocks_c(struct dvb_frontend *fe, u32 *ucblocks)249{250*ucblocks = 0;251/* no way to read ? */252return 0;253}254255int cxd2820r_read_status_c(struct dvb_frontend *fe, fe_status_t *status)256{257struct cxd2820r_priv *priv = fe->demodulator_priv;258int ret;259u8 buf[2];260*status = 0;261262ret = cxd2820r_rd_regs(priv, 0x10088, buf, sizeof(buf));263if (ret)264goto error;265266if (((buf[0] >> 0) & 0x01) == 1) {267*status |= FE_HAS_SIGNAL | FE_HAS_CARRIER |268FE_HAS_VITERBI | FE_HAS_SYNC;269270if (((buf[1] >> 3) & 0x01) == 1) {271*status |= FE_HAS_SIGNAL | FE_HAS_CARRIER |272FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK;273}274}275276dbg("%s: lock=%02x %02x", __func__, buf[0], buf[1]);277278return ret;279error:280dbg("%s: failed:%d", __func__, ret);281return ret;282}283284int cxd2820r_init_c(struct dvb_frontend *fe)285{286struct cxd2820r_priv *priv = fe->demodulator_priv;287int ret;288289ret = cxd2820r_wr_reg(priv, 0x00085, 0x07);290if (ret)291goto error;292293return ret;294error:295dbg("%s: failed:%d", __func__, ret);296return ret;297}298299int cxd2820r_sleep_c(struct dvb_frontend *fe)300{301struct cxd2820r_priv *priv = fe->demodulator_priv;302int ret, i;303struct reg_val_mask tab[] = {304{ 0x000ff, 0x1f, 0xff },305{ 0x00085, 0x00, 0xff },306{ 0x00088, 0x01, 0xff },307{ 0x00081, 0x00, 0xff },308{ 0x00080, 0x00, 0xff },309};310311dbg("%s", __func__);312313priv->delivery_system = SYS_UNDEFINED;314315for (i = 0; i < ARRAY_SIZE(tab); i++) {316ret = cxd2820r_wr_reg_mask(priv, tab[i].reg, tab[i].val,317tab[i].mask);318if (ret)319goto error;320}321322return ret;323error:324dbg("%s: failed:%d", __func__, ret);325return ret;326}327328int cxd2820r_get_tune_settings_c(struct dvb_frontend *fe,329struct dvb_frontend_tune_settings *s)330{331s->min_delay_ms = 500;332s->step_size = 0; /* no zigzag */333s->max_drift = 0;334335return 0;336}337338339340