18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * Support for the Broadcom BCM3510 ATSC demodulator (1st generation Air2PC) 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Copyright (C) 2001-5, B2C2 inc. 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * GPL/Linux driver written by Patrick Boettcher <patrick.boettcher@posteo.de> 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * This driver is "hard-coded" to be used with the 1st generation of 98c2ecf20Sopenharmony_ci * Technisat/B2C2's Air2PC ATSC PCI/USB cards/boxes. The pll-programming 108c2ecf20Sopenharmony_ci * (Panasonic CT10S) is located here, which is actually wrong. Unless there is 118c2ecf20Sopenharmony_ci * another device with a BCM3510, this is no problem. 128c2ecf20Sopenharmony_ci * 138c2ecf20Sopenharmony_ci * The driver works also with QAM64 DVB-C, but had an unreasonable high 148c2ecf20Sopenharmony_ci * UNC. (Tested with the Air2PC ATSC 1st generation) 158c2ecf20Sopenharmony_ci * 168c2ecf20Sopenharmony_ci * You'll need a firmware for this driver in order to get it running. It is 178c2ecf20Sopenharmony_ci * called "dvb-fe-bcm3510-01.fw". 188c2ecf20Sopenharmony_ci * 198c2ecf20Sopenharmony_ci * This program is free software; you can redistribute it and/or modify it 208c2ecf20Sopenharmony_ci * under the terms of the GNU General Public License as published by the Free 218c2ecf20Sopenharmony_ci * Software Foundation; either version 2 of the License, or (at your option) 228c2ecf20Sopenharmony_ci * any later version. 238c2ecf20Sopenharmony_ci * 248c2ecf20Sopenharmony_ci * This program is distributed in the hope that it will be useful, but WITHOUT 258c2ecf20Sopenharmony_ci * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 268c2ecf20Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 278c2ecf20Sopenharmony_ci * more details. 288c2ecf20Sopenharmony_ci * 298c2ecf20Sopenharmony_ci * You should have received a copy of the GNU General Public License along with 308c2ecf20Sopenharmony_ci * this program; if not, write to the Free Software Foundation, Inc., 675 Mass 318c2ecf20Sopenharmony_ci * Ave, Cambridge, MA 02139, USA. 328c2ecf20Sopenharmony_ci */ 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci#include <linux/init.h> 358c2ecf20Sopenharmony_ci#include <linux/module.h> 368c2ecf20Sopenharmony_ci#include <linux/device.h> 378c2ecf20Sopenharmony_ci#include <linux/firmware.h> 388c2ecf20Sopenharmony_ci#include <linux/jiffies.h> 398c2ecf20Sopenharmony_ci#include <linux/string.h> 408c2ecf20Sopenharmony_ci#include <linux/slab.h> 418c2ecf20Sopenharmony_ci#include <linux/mutex.h> 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci#include <media/dvb_frontend.h> 448c2ecf20Sopenharmony_ci#include "bcm3510.h" 458c2ecf20Sopenharmony_ci#include "bcm3510_priv.h" 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci/* Max transfer size done by bcm3510_do_hab_cmd() function */ 488c2ecf20Sopenharmony_ci#define MAX_XFER_SIZE 128 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_cistruct bcm3510_state { 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci struct i2c_adapter* i2c; 538c2ecf20Sopenharmony_ci const struct bcm3510_config* config; 548c2ecf20Sopenharmony_ci struct dvb_frontend frontend; 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci /* demodulator private data */ 578c2ecf20Sopenharmony_ci struct mutex hab_mutex; 588c2ecf20Sopenharmony_ci u8 firmware_loaded:1; 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci unsigned long next_status_check; 618c2ecf20Sopenharmony_ci unsigned long status_check_interval; 628c2ecf20Sopenharmony_ci struct bcm3510_hab_cmd_status1 status1; 638c2ecf20Sopenharmony_ci struct bcm3510_hab_cmd_status2 status2; 648c2ecf20Sopenharmony_ci}; 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_cistatic int debug; 678c2ecf20Sopenharmony_cimodule_param(debug, int, 0644); 688c2ecf20Sopenharmony_ciMODULE_PARM_DESC(debug, "set debugging level (1=info,2=i2c (|-able))."); 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci#define dprintk(level,x...) if (level & debug) printk(x) 718c2ecf20Sopenharmony_ci#define dbufout(b,l,m) {\ 728c2ecf20Sopenharmony_ci int i; \ 738c2ecf20Sopenharmony_ci for (i = 0; i < l; i++) \ 748c2ecf20Sopenharmony_ci m("%02x ",b[i]); \ 758c2ecf20Sopenharmony_ci} 768c2ecf20Sopenharmony_ci#define deb_info(args...) dprintk(0x01,args) 778c2ecf20Sopenharmony_ci#define deb_i2c(args...) dprintk(0x02,args) 788c2ecf20Sopenharmony_ci#define deb_hab(args...) dprintk(0x04,args) 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci/* transfer functions */ 818c2ecf20Sopenharmony_cistatic int bcm3510_writebytes (struct bcm3510_state *state, u8 reg, u8 *buf, u8 len) 828c2ecf20Sopenharmony_ci{ 838c2ecf20Sopenharmony_ci u8 b[256]; 848c2ecf20Sopenharmony_ci int err; 858c2ecf20Sopenharmony_ci struct i2c_msg msg = { .addr = state->config->demod_address, .flags = 0, .buf = b, .len = len + 1 }; 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci b[0] = reg; 888c2ecf20Sopenharmony_ci memcpy(&b[1],buf,len); 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci deb_i2c("i2c wr %02x: ",reg); 918c2ecf20Sopenharmony_ci dbufout(buf,len,deb_i2c); 928c2ecf20Sopenharmony_ci deb_i2c("\n"); 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci if ((err = i2c_transfer (state->i2c, &msg, 1)) != 1) { 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci deb_info("%s: i2c write error (addr %02x, reg %02x, err == %i)\n", 978c2ecf20Sopenharmony_ci __func__, state->config->demod_address, reg, err); 988c2ecf20Sopenharmony_ci return -EREMOTEIO; 998c2ecf20Sopenharmony_ci } 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci return 0; 1028c2ecf20Sopenharmony_ci} 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_cistatic int bcm3510_readbytes (struct bcm3510_state *state, u8 reg, u8 *buf, u8 len) 1058c2ecf20Sopenharmony_ci{ 1068c2ecf20Sopenharmony_ci struct i2c_msg msg[] = { 1078c2ecf20Sopenharmony_ci { .addr = state->config->demod_address, .flags = 0, .buf = ®, .len = 1 }, 1088c2ecf20Sopenharmony_ci { .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = buf, .len = len } 1098c2ecf20Sopenharmony_ci }; 1108c2ecf20Sopenharmony_ci int err; 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci memset(buf,0,len); 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci if ((err = i2c_transfer (state->i2c, msg, 2)) != 2) { 1158c2ecf20Sopenharmony_ci deb_info("%s: i2c read error (addr %02x, reg %02x, err == %i)\n", 1168c2ecf20Sopenharmony_ci __func__, state->config->demod_address, reg, err); 1178c2ecf20Sopenharmony_ci return -EREMOTEIO; 1188c2ecf20Sopenharmony_ci } 1198c2ecf20Sopenharmony_ci deb_i2c("i2c rd %02x: ",reg); 1208c2ecf20Sopenharmony_ci dbufout(buf,len,deb_i2c); 1218c2ecf20Sopenharmony_ci deb_i2c("\n"); 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci return 0; 1248c2ecf20Sopenharmony_ci} 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_cistatic int bcm3510_writeB(struct bcm3510_state *state, u8 reg, bcm3510_register_value v) 1278c2ecf20Sopenharmony_ci{ 1288c2ecf20Sopenharmony_ci return bcm3510_writebytes(state,reg,&v.raw,1); 1298c2ecf20Sopenharmony_ci} 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_cistatic int bcm3510_readB(struct bcm3510_state *state, u8 reg, bcm3510_register_value *v) 1328c2ecf20Sopenharmony_ci{ 1338c2ecf20Sopenharmony_ci return bcm3510_readbytes(state,reg,&v->raw,1); 1348c2ecf20Sopenharmony_ci} 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci/* Host Access Buffer transfers */ 1378c2ecf20Sopenharmony_cistatic int bcm3510_hab_get_response(struct bcm3510_state *st, u8 *buf, int len) 1388c2ecf20Sopenharmony_ci{ 1398c2ecf20Sopenharmony_ci bcm3510_register_value v; 1408c2ecf20Sopenharmony_ci int ret,i; 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci v.HABADR_a6.HABADR = 0; 1438c2ecf20Sopenharmony_ci if ((ret = bcm3510_writeB(st,0xa6,v)) < 0) 1448c2ecf20Sopenharmony_ci return ret; 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci for (i = 0; i < len; i++) { 1478c2ecf20Sopenharmony_ci if ((ret = bcm3510_readB(st,0xa7,&v)) < 0) 1488c2ecf20Sopenharmony_ci return ret; 1498c2ecf20Sopenharmony_ci buf[i] = v.HABDATA_a7; 1508c2ecf20Sopenharmony_ci } 1518c2ecf20Sopenharmony_ci return 0; 1528c2ecf20Sopenharmony_ci} 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_cistatic int bcm3510_hab_send_request(struct bcm3510_state *st, u8 *buf, int len) 1558c2ecf20Sopenharmony_ci{ 1568c2ecf20Sopenharmony_ci bcm3510_register_value v,hab; 1578c2ecf20Sopenharmony_ci int ret,i; 1588c2ecf20Sopenharmony_ci unsigned long t; 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci/* Check if any previous HAB request still needs to be serviced by the 1618c2ecf20Sopenharmony_ci * Acquisition Processor before sending new request */ 1628c2ecf20Sopenharmony_ci if ((ret = bcm3510_readB(st,0xa8,&v)) < 0) 1638c2ecf20Sopenharmony_ci return ret; 1648c2ecf20Sopenharmony_ci if (v.HABSTAT_a8.HABR) { 1658c2ecf20Sopenharmony_ci deb_info("HAB is running already - clearing it.\n"); 1668c2ecf20Sopenharmony_ci v.HABSTAT_a8.HABR = 0; 1678c2ecf20Sopenharmony_ci bcm3510_writeB(st,0xa8,v); 1688c2ecf20Sopenharmony_ci// return -EBUSY; 1698c2ecf20Sopenharmony_ci } 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci/* Send the start HAB Address (automatically incremented after write of 1728c2ecf20Sopenharmony_ci * HABDATA) and write the HAB Data */ 1738c2ecf20Sopenharmony_ci hab.HABADR_a6.HABADR = 0; 1748c2ecf20Sopenharmony_ci if ((ret = bcm3510_writeB(st,0xa6,hab)) < 0) 1758c2ecf20Sopenharmony_ci return ret; 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci for (i = 0; i < len; i++) { 1788c2ecf20Sopenharmony_ci hab.HABDATA_a7 = buf[i]; 1798c2ecf20Sopenharmony_ci if ((ret = bcm3510_writeB(st,0xa7,hab)) < 0) 1808c2ecf20Sopenharmony_ci return ret; 1818c2ecf20Sopenharmony_ci } 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci/* Set the HABR bit to indicate AP request in progress (LBHABR allows HABR to 1848c2ecf20Sopenharmony_ci * be written) */ 1858c2ecf20Sopenharmony_ci v.raw = 0; v.HABSTAT_a8.HABR = 1; v.HABSTAT_a8.LDHABR = 1; 1868c2ecf20Sopenharmony_ci if ((ret = bcm3510_writeB(st,0xa8,v)) < 0) 1878c2ecf20Sopenharmony_ci return ret; 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci/* Polling method: Wait until the AP finishes processing the HAB request */ 1908c2ecf20Sopenharmony_ci t = jiffies + 1*HZ; 1918c2ecf20Sopenharmony_ci while (time_before(jiffies, t)) { 1928c2ecf20Sopenharmony_ci deb_info("waiting for HAB to complete\n"); 1938c2ecf20Sopenharmony_ci msleep(10); 1948c2ecf20Sopenharmony_ci if ((ret = bcm3510_readB(st,0xa8,&v)) < 0) 1958c2ecf20Sopenharmony_ci return ret; 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci if (!v.HABSTAT_a8.HABR) 1988c2ecf20Sopenharmony_ci return 0; 1998c2ecf20Sopenharmony_ci } 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci deb_info("send_request execution timed out.\n"); 2028c2ecf20Sopenharmony_ci return -ETIMEDOUT; 2038c2ecf20Sopenharmony_ci} 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_cistatic int bcm3510_do_hab_cmd(struct bcm3510_state *st, u8 cmd, u8 msgid, u8 *obuf, u8 olen, u8 *ibuf, u8 ilen) 2068c2ecf20Sopenharmony_ci{ 2078c2ecf20Sopenharmony_ci u8 ob[MAX_XFER_SIZE], ib[MAX_XFER_SIZE]; 2088c2ecf20Sopenharmony_ci int ret = 0; 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci if (ilen + 2 > sizeof(ib)) { 2118c2ecf20Sopenharmony_ci deb_hab("do_hab_cmd: ilen=%d is too big!\n", ilen); 2128c2ecf20Sopenharmony_ci return -EINVAL; 2138c2ecf20Sopenharmony_ci } 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci if (olen + 2 > sizeof(ob)) { 2168c2ecf20Sopenharmony_ci deb_hab("do_hab_cmd: olen=%d is too big!\n", olen); 2178c2ecf20Sopenharmony_ci return -EINVAL; 2188c2ecf20Sopenharmony_ci } 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci ob[0] = cmd; 2218c2ecf20Sopenharmony_ci ob[1] = msgid; 2228c2ecf20Sopenharmony_ci memcpy(&ob[2],obuf,olen); 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci deb_hab("hab snd: "); 2258c2ecf20Sopenharmony_ci dbufout(ob,olen+2,deb_hab); 2268c2ecf20Sopenharmony_ci deb_hab("\n"); 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci if (mutex_lock_interruptible(&st->hab_mutex) < 0) 2298c2ecf20Sopenharmony_ci return -EAGAIN; 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci if ((ret = bcm3510_hab_send_request(st, ob, olen+2)) < 0 || 2328c2ecf20Sopenharmony_ci (ret = bcm3510_hab_get_response(st, ib, ilen+2)) < 0) 2338c2ecf20Sopenharmony_ci goto error; 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci deb_hab("hab get: "); 2368c2ecf20Sopenharmony_ci dbufout(ib,ilen+2,deb_hab); 2378c2ecf20Sopenharmony_ci deb_hab("\n"); 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci memcpy(ibuf,&ib[2],ilen); 2408c2ecf20Sopenharmony_cierror: 2418c2ecf20Sopenharmony_ci mutex_unlock(&st->hab_mutex); 2428c2ecf20Sopenharmony_ci return ret; 2438c2ecf20Sopenharmony_ci} 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci#if 0 2468c2ecf20Sopenharmony_ci/* not needed, we use a semaphore to prevent HAB races */ 2478c2ecf20Sopenharmony_cistatic int bcm3510_is_ap_ready(struct bcm3510_state *st) 2488c2ecf20Sopenharmony_ci{ 2498c2ecf20Sopenharmony_ci bcm3510_register_value ap,hab; 2508c2ecf20Sopenharmony_ci int ret; 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci if ((ret = bcm3510_readB(st,0xa8,&hab)) < 0 || 2538c2ecf20Sopenharmony_ci (ret = bcm3510_readB(st,0xa2,&ap) < 0)) 2548c2ecf20Sopenharmony_ci return ret; 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci if (ap.APSTAT1_a2.RESET || ap.APSTAT1_a2.IDLE || ap.APSTAT1_a2.STOP || hab.HABSTAT_a8.HABR) { 2578c2ecf20Sopenharmony_ci deb_info("AP is busy\n"); 2588c2ecf20Sopenharmony_ci return -EBUSY; 2598c2ecf20Sopenharmony_ci } 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci return 0; 2628c2ecf20Sopenharmony_ci} 2638c2ecf20Sopenharmony_ci#endif 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_cistatic int bcm3510_bert_reset(struct bcm3510_state *st) 2668c2ecf20Sopenharmony_ci{ 2678c2ecf20Sopenharmony_ci bcm3510_register_value b; 2688c2ecf20Sopenharmony_ci int ret; 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci if ((ret = bcm3510_readB(st,0xfa,&b)) < 0) 2718c2ecf20Sopenharmony_ci return ret; 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci b.BERCTL_fa.RESYNC = 0; bcm3510_writeB(st,0xfa,b); 2748c2ecf20Sopenharmony_ci b.BERCTL_fa.RESYNC = 1; bcm3510_writeB(st,0xfa,b); 2758c2ecf20Sopenharmony_ci b.BERCTL_fa.RESYNC = 0; bcm3510_writeB(st,0xfa,b); 2768c2ecf20Sopenharmony_ci b.BERCTL_fa.CNTCTL = 1; b.BERCTL_fa.BITCNT = 1; bcm3510_writeB(st,0xfa,b); 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci /* clear residual bit counter TODO */ 2798c2ecf20Sopenharmony_ci return 0; 2808c2ecf20Sopenharmony_ci} 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_cistatic int bcm3510_refresh_state(struct bcm3510_state *st) 2838c2ecf20Sopenharmony_ci{ 2848c2ecf20Sopenharmony_ci if (time_after(jiffies,st->next_status_check)) { 2858c2ecf20Sopenharmony_ci bcm3510_do_hab_cmd(st, CMD_STATUS, MSGID_STATUS1, NULL,0, (u8 *)&st->status1, sizeof(st->status1)); 2868c2ecf20Sopenharmony_ci bcm3510_do_hab_cmd(st, CMD_STATUS, MSGID_STATUS2, NULL,0, (u8 *)&st->status2, sizeof(st->status2)); 2878c2ecf20Sopenharmony_ci st->next_status_check = jiffies + (st->status_check_interval*HZ)/1000; 2888c2ecf20Sopenharmony_ci } 2898c2ecf20Sopenharmony_ci return 0; 2908c2ecf20Sopenharmony_ci} 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_cistatic int bcm3510_read_status(struct dvb_frontend *fe, enum fe_status *status) 2938c2ecf20Sopenharmony_ci{ 2948c2ecf20Sopenharmony_ci struct bcm3510_state* st = fe->demodulator_priv; 2958c2ecf20Sopenharmony_ci bcm3510_refresh_state(st); 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci *status = 0; 2988c2ecf20Sopenharmony_ci if (st->status1.STATUS1.RECEIVER_LOCK) 2998c2ecf20Sopenharmony_ci *status |= FE_HAS_LOCK | FE_HAS_SYNC; 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci if (st->status1.STATUS1.FEC_LOCK) 3028c2ecf20Sopenharmony_ci *status |= FE_HAS_VITERBI; 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci if (st->status1.STATUS1.OUT_PLL_LOCK) 3058c2ecf20Sopenharmony_ci *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER; 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci if (*status & FE_HAS_LOCK) 3088c2ecf20Sopenharmony_ci st->status_check_interval = 1500; 3098c2ecf20Sopenharmony_ci else /* more frequently checks if no lock has been achieved yet */ 3108c2ecf20Sopenharmony_ci st->status_check_interval = 500; 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci deb_info("real_status: %02x\n",*status); 3138c2ecf20Sopenharmony_ci return 0; 3148c2ecf20Sopenharmony_ci} 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_cistatic int bcm3510_read_ber(struct dvb_frontend* fe, u32* ber) 3178c2ecf20Sopenharmony_ci{ 3188c2ecf20Sopenharmony_ci struct bcm3510_state* st = fe->demodulator_priv; 3198c2ecf20Sopenharmony_ci bcm3510_refresh_state(st); 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci *ber = (st->status2.LDBER0 << 16) | (st->status2.LDBER1 << 8) | st->status2.LDBER2; 3228c2ecf20Sopenharmony_ci return 0; 3238c2ecf20Sopenharmony_ci} 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_cistatic int bcm3510_read_unc(struct dvb_frontend* fe, u32* unc) 3268c2ecf20Sopenharmony_ci{ 3278c2ecf20Sopenharmony_ci struct bcm3510_state* st = fe->demodulator_priv; 3288c2ecf20Sopenharmony_ci bcm3510_refresh_state(st); 3298c2ecf20Sopenharmony_ci *unc = (st->status2.LDUERC0 << 8) | st->status2.LDUERC1; 3308c2ecf20Sopenharmony_ci return 0; 3318c2ecf20Sopenharmony_ci} 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_cistatic int bcm3510_read_signal_strength(struct dvb_frontend* fe, u16* strength) 3348c2ecf20Sopenharmony_ci{ 3358c2ecf20Sopenharmony_ci struct bcm3510_state* st = fe->demodulator_priv; 3368c2ecf20Sopenharmony_ci s32 t; 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci bcm3510_refresh_state(st); 3398c2ecf20Sopenharmony_ci t = st->status2.SIGNAL; 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci if (t > 190) 3428c2ecf20Sopenharmony_ci t = 190; 3438c2ecf20Sopenharmony_ci if (t < 90) 3448c2ecf20Sopenharmony_ci t = 90; 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci t -= 90; 3478c2ecf20Sopenharmony_ci t = t * 0xff / 100; 3488c2ecf20Sopenharmony_ci /* normalize if necessary */ 3498c2ecf20Sopenharmony_ci *strength = (t << 8) | t; 3508c2ecf20Sopenharmony_ci return 0; 3518c2ecf20Sopenharmony_ci} 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_cistatic int bcm3510_read_snr(struct dvb_frontend* fe, u16* snr) 3548c2ecf20Sopenharmony_ci{ 3558c2ecf20Sopenharmony_ci struct bcm3510_state* st = fe->demodulator_priv; 3568c2ecf20Sopenharmony_ci bcm3510_refresh_state(st); 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci *snr = st->status1.SNR_EST0*1000 + ((st->status1.SNR_EST1*1000) >> 8); 3598c2ecf20Sopenharmony_ci return 0; 3608c2ecf20Sopenharmony_ci} 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci/* tuner frontend programming */ 3638c2ecf20Sopenharmony_cistatic int bcm3510_tuner_cmd(struct bcm3510_state* st,u8 bc, u16 n, u8 a) 3648c2ecf20Sopenharmony_ci{ 3658c2ecf20Sopenharmony_ci struct bcm3510_hab_cmd_tune c; 3668c2ecf20Sopenharmony_ci memset(&c,0,sizeof(struct bcm3510_hab_cmd_tune)); 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci/* I2C Mode disabled, set 16 control / Data pairs */ 3698c2ecf20Sopenharmony_ci c.length = 0x10; 3708c2ecf20Sopenharmony_ci c.clock_width = 0; 3718c2ecf20Sopenharmony_ci/* CS1, CS0, DATA, CLK bits control the tuner RF_AGC_SEL pin is set to 3728c2ecf20Sopenharmony_ci * logic high (as Configuration) */ 3738c2ecf20Sopenharmony_ci c.misc = 0x10; 3748c2ecf20Sopenharmony_ci/* Set duration of the initial state of TUNCTL = 3.34 micro Sec */ 3758c2ecf20Sopenharmony_ci c.TUNCTL_state = 0x40; 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci/* PRESCALER DIVIDE RATIO | BC1_2_3_4; (band switch), 1stosc REFERENCE COUNTER REF_S12 and REF_S11 */ 3788c2ecf20Sopenharmony_ci c.ctl_dat[0].ctrl.size = BITS_8; 3798c2ecf20Sopenharmony_ci c.ctl_dat[0].data = 0x80 | bc; 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci/* Control DATA pin, 1stosc REFERENCE COUNTER REF_S10 to REF_S3 */ 3828c2ecf20Sopenharmony_ci c.ctl_dat[1].ctrl.size = BITS_8; 3838c2ecf20Sopenharmony_ci c.ctl_dat[1].data = 4; 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci/* set CONTROL BIT 1 to 1, 1stosc REFERENCE COUNTER REF_S2 to REF_S1 */ 3868c2ecf20Sopenharmony_ci c.ctl_dat[2].ctrl.size = BITS_3; 3878c2ecf20Sopenharmony_ci c.ctl_dat[2].data = 0x20; 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci/* control CS0 pin, pulse byte ? */ 3908c2ecf20Sopenharmony_ci c.ctl_dat[3].ctrl.size = BITS_3; 3918c2ecf20Sopenharmony_ci c.ctl_dat[3].ctrl.clk_off = 1; 3928c2ecf20Sopenharmony_ci c.ctl_dat[3].ctrl.cs0 = 1; 3938c2ecf20Sopenharmony_ci c.ctl_dat[3].data = 0x40; 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci/* PGM_S18 to PGM_S11 */ 3968c2ecf20Sopenharmony_ci c.ctl_dat[4].ctrl.size = BITS_8; 3978c2ecf20Sopenharmony_ci c.ctl_dat[4].data = n >> 3; 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci/* PGM_S10 to PGM_S8, SWL_S7 to SWL_S3 */ 4008c2ecf20Sopenharmony_ci c.ctl_dat[5].ctrl.size = BITS_8; 4018c2ecf20Sopenharmony_ci c.ctl_dat[5].data = ((n & 0x7) << 5) | (a >> 2); 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci/* SWL_S2 and SWL_S1, set CONTROL BIT 2 to 0 */ 4048c2ecf20Sopenharmony_ci c.ctl_dat[6].ctrl.size = BITS_3; 4058c2ecf20Sopenharmony_ci c.ctl_dat[6].data = (a << 6) & 0xdf; 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci/* control CS0 pin, pulse byte ? */ 4088c2ecf20Sopenharmony_ci c.ctl_dat[7].ctrl.size = BITS_3; 4098c2ecf20Sopenharmony_ci c.ctl_dat[7].ctrl.clk_off = 1; 4108c2ecf20Sopenharmony_ci c.ctl_dat[7].ctrl.cs0 = 1; 4118c2ecf20Sopenharmony_ci c.ctl_dat[7].data = 0x40; 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci/* PRESCALER DIVIDE RATIO, 2ndosc REFERENCE COUNTER REF_S12 and REF_S11 */ 4148c2ecf20Sopenharmony_ci c.ctl_dat[8].ctrl.size = BITS_8; 4158c2ecf20Sopenharmony_ci c.ctl_dat[8].data = 0x80; 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci/* 2ndosc REFERENCE COUNTER REF_S10 to REF_S3 */ 4188c2ecf20Sopenharmony_ci c.ctl_dat[9].ctrl.size = BITS_8; 4198c2ecf20Sopenharmony_ci c.ctl_dat[9].data = 0x10; 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci/* set CONTROL BIT 1 to 1, 2ndosc REFERENCE COUNTER REF_S2 to REF_S1 */ 4228c2ecf20Sopenharmony_ci c.ctl_dat[10].ctrl.size = BITS_3; 4238c2ecf20Sopenharmony_ci c.ctl_dat[10].data = 0x20; 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci/* pulse byte */ 4268c2ecf20Sopenharmony_ci c.ctl_dat[11].ctrl.size = BITS_3; 4278c2ecf20Sopenharmony_ci c.ctl_dat[11].ctrl.clk_off = 1; 4288c2ecf20Sopenharmony_ci c.ctl_dat[11].ctrl.cs1 = 1; 4298c2ecf20Sopenharmony_ci c.ctl_dat[11].data = 0x40; 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci/* PGM_S18 to PGM_S11 */ 4328c2ecf20Sopenharmony_ci c.ctl_dat[12].ctrl.size = BITS_8; 4338c2ecf20Sopenharmony_ci c.ctl_dat[12].data = 0x2a; 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci/* PGM_S10 to PGM_S8 and SWL_S7 to SWL_S3 */ 4368c2ecf20Sopenharmony_ci c.ctl_dat[13].ctrl.size = BITS_8; 4378c2ecf20Sopenharmony_ci c.ctl_dat[13].data = 0x8e; 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci/* SWL_S2 and SWL_S1 and set CONTROL BIT 2 to 0 */ 4408c2ecf20Sopenharmony_ci c.ctl_dat[14].ctrl.size = BITS_3; 4418c2ecf20Sopenharmony_ci c.ctl_dat[14].data = 0; 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci/* Pulse Byte */ 4448c2ecf20Sopenharmony_ci c.ctl_dat[15].ctrl.size = BITS_3; 4458c2ecf20Sopenharmony_ci c.ctl_dat[15].ctrl.clk_off = 1; 4468c2ecf20Sopenharmony_ci c.ctl_dat[15].ctrl.cs1 = 1; 4478c2ecf20Sopenharmony_ci c.ctl_dat[15].data = 0x40; 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci return bcm3510_do_hab_cmd(st,CMD_TUNE, MSGID_TUNE,(u8 *) &c,sizeof(c), NULL, 0); 4508c2ecf20Sopenharmony_ci} 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_cistatic int bcm3510_set_freq(struct bcm3510_state* st,u32 freq) 4538c2ecf20Sopenharmony_ci{ 4548c2ecf20Sopenharmony_ci u8 bc,a; 4558c2ecf20Sopenharmony_ci u16 n; 4568c2ecf20Sopenharmony_ci s32 YIntercept,Tfvco1; 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci freq /= 1000; 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_ci deb_info("%dkHz:",freq); 4618c2ecf20Sopenharmony_ci /* set Band Switch */ 4628c2ecf20Sopenharmony_ci if (freq <= 168000) 4638c2ecf20Sopenharmony_ci bc = 0x1c; 4648c2ecf20Sopenharmony_ci else if (freq <= 378000) 4658c2ecf20Sopenharmony_ci bc = 0x2c; 4668c2ecf20Sopenharmony_ci else 4678c2ecf20Sopenharmony_ci bc = 0x30; 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ci if (freq >= 470000) { 4708c2ecf20Sopenharmony_ci freq -= 470001; 4718c2ecf20Sopenharmony_ci YIntercept = 18805; 4728c2ecf20Sopenharmony_ci } else if (freq >= 90000) { 4738c2ecf20Sopenharmony_ci freq -= 90001; 4748c2ecf20Sopenharmony_ci YIntercept = 15005; 4758c2ecf20Sopenharmony_ci } else if (freq >= 76000){ 4768c2ecf20Sopenharmony_ci freq -= 76001; 4778c2ecf20Sopenharmony_ci YIntercept = 14865; 4788c2ecf20Sopenharmony_ci } else { 4798c2ecf20Sopenharmony_ci freq -= 54001; 4808c2ecf20Sopenharmony_ci YIntercept = 14645; 4818c2ecf20Sopenharmony_ci } 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_ci Tfvco1 = (((freq/6000)*60 + YIntercept)*4)/10; 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_ci n = Tfvco1 >> 6; 4868c2ecf20Sopenharmony_ci a = Tfvco1 & 0x3f; 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_ci deb_info(" BC1_2_3_4: %x, N: %x A: %x\n", bc, n, a); 4898c2ecf20Sopenharmony_ci if (n >= 16 && n <= 2047) 4908c2ecf20Sopenharmony_ci return bcm3510_tuner_cmd(st,bc,n,a); 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_ci return -EINVAL; 4938c2ecf20Sopenharmony_ci} 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_cistatic int bcm3510_set_frontend(struct dvb_frontend *fe) 4968c2ecf20Sopenharmony_ci{ 4978c2ecf20Sopenharmony_ci struct dtv_frontend_properties *c = &fe->dtv_property_cache; 4988c2ecf20Sopenharmony_ci struct bcm3510_state* st = fe->demodulator_priv; 4998c2ecf20Sopenharmony_ci struct bcm3510_hab_cmd_ext_acquire cmd; 5008c2ecf20Sopenharmony_ci struct bcm3510_hab_cmd_bert_control bert; 5018c2ecf20Sopenharmony_ci int ret; 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_ci memset(&cmd,0,sizeof(cmd)); 5048c2ecf20Sopenharmony_ci switch (c->modulation) { 5058c2ecf20Sopenharmony_ci case QAM_256: 5068c2ecf20Sopenharmony_ci cmd.ACQUIRE0.MODE = 0x1; 5078c2ecf20Sopenharmony_ci cmd.ACQUIRE1.SYM_RATE = 0x1; 5088c2ecf20Sopenharmony_ci cmd.ACQUIRE1.IF_FREQ = 0x1; 5098c2ecf20Sopenharmony_ci break; 5108c2ecf20Sopenharmony_ci case QAM_64: 5118c2ecf20Sopenharmony_ci cmd.ACQUIRE0.MODE = 0x2; 5128c2ecf20Sopenharmony_ci cmd.ACQUIRE1.SYM_RATE = 0x2; 5138c2ecf20Sopenharmony_ci cmd.ACQUIRE1.IF_FREQ = 0x1; 5148c2ecf20Sopenharmony_ci break; 5158c2ecf20Sopenharmony_ci#if 0 5168c2ecf20Sopenharmony_ci case QAM_256: 5178c2ecf20Sopenharmony_ci cmd.ACQUIRE0.MODE = 0x3; 5188c2ecf20Sopenharmony_ci break; 5198c2ecf20Sopenharmony_ci case QAM_128: 5208c2ecf20Sopenharmony_ci cmd.ACQUIRE0.MODE = 0x4; 5218c2ecf20Sopenharmony_ci break; 5228c2ecf20Sopenharmony_ci case QAM_64: 5238c2ecf20Sopenharmony_ci cmd.ACQUIRE0.MODE = 0x5; 5248c2ecf20Sopenharmony_ci break; 5258c2ecf20Sopenharmony_ci case QAM_32: 5268c2ecf20Sopenharmony_ci cmd.ACQUIRE0.MODE = 0x6; 5278c2ecf20Sopenharmony_ci break; 5288c2ecf20Sopenharmony_ci case QAM_16: 5298c2ecf20Sopenharmony_ci cmd.ACQUIRE0.MODE = 0x7; 5308c2ecf20Sopenharmony_ci break; 5318c2ecf20Sopenharmony_ci#endif 5328c2ecf20Sopenharmony_ci case VSB_8: 5338c2ecf20Sopenharmony_ci cmd.ACQUIRE0.MODE = 0x8; 5348c2ecf20Sopenharmony_ci cmd.ACQUIRE1.SYM_RATE = 0x0; 5358c2ecf20Sopenharmony_ci cmd.ACQUIRE1.IF_FREQ = 0x0; 5368c2ecf20Sopenharmony_ci break; 5378c2ecf20Sopenharmony_ci case VSB_16: 5388c2ecf20Sopenharmony_ci cmd.ACQUIRE0.MODE = 0x9; 5398c2ecf20Sopenharmony_ci cmd.ACQUIRE1.SYM_RATE = 0x0; 5408c2ecf20Sopenharmony_ci cmd.ACQUIRE1.IF_FREQ = 0x0; 5418c2ecf20Sopenharmony_ci break; 5428c2ecf20Sopenharmony_ci default: 5438c2ecf20Sopenharmony_ci return -EINVAL; 5448c2ecf20Sopenharmony_ci } 5458c2ecf20Sopenharmony_ci cmd.ACQUIRE0.OFFSET = 0; 5468c2ecf20Sopenharmony_ci cmd.ACQUIRE0.NTSCSWEEP = 1; 5478c2ecf20Sopenharmony_ci cmd.ACQUIRE0.FA = 1; 5488c2ecf20Sopenharmony_ci cmd.ACQUIRE0.BW = 0; 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_ci/* if (enableOffset) { 5518c2ecf20Sopenharmony_ci cmd.IF_OFFSET0 = xx; 5528c2ecf20Sopenharmony_ci cmd.IF_OFFSET1 = xx; 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ci cmd.SYM_OFFSET0 = xx; 5558c2ecf20Sopenharmony_ci cmd.SYM_OFFSET1 = xx; 5568c2ecf20Sopenharmony_ci if (enableNtscSweep) { 5578c2ecf20Sopenharmony_ci cmd.NTSC_OFFSET0; 5588c2ecf20Sopenharmony_ci cmd.NTSC_OFFSET1; 5598c2ecf20Sopenharmony_ci } 5608c2ecf20Sopenharmony_ci } */ 5618c2ecf20Sopenharmony_ci bcm3510_do_hab_cmd(st, CMD_ACQUIRE, MSGID_EXT_TUNER_ACQUIRE, (u8 *) &cmd, sizeof(cmd), NULL, 0); 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_ci/* doing it with different MSGIDs, data book and source differs */ 5648c2ecf20Sopenharmony_ci bert.BE = 0; 5658c2ecf20Sopenharmony_ci bert.unused = 0; 5668c2ecf20Sopenharmony_ci bcm3510_do_hab_cmd(st, CMD_STATE_CONTROL, MSGID_BERT_CONTROL, (u8 *) &bert, sizeof(bert), NULL, 0); 5678c2ecf20Sopenharmony_ci bcm3510_do_hab_cmd(st, CMD_STATE_CONTROL, MSGID_BERT_SET, (u8 *) &bert, sizeof(bert), NULL, 0); 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_ci bcm3510_bert_reset(st); 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_ci ret = bcm3510_set_freq(st, c->frequency); 5728c2ecf20Sopenharmony_ci if (ret < 0) 5738c2ecf20Sopenharmony_ci return ret; 5748c2ecf20Sopenharmony_ci 5758c2ecf20Sopenharmony_ci memset(&st->status1,0,sizeof(st->status1)); 5768c2ecf20Sopenharmony_ci memset(&st->status2,0,sizeof(st->status2)); 5778c2ecf20Sopenharmony_ci st->status_check_interval = 500; 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_ci/* Give the AP some time */ 5808c2ecf20Sopenharmony_ci msleep(200); 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_ci return 0; 5838c2ecf20Sopenharmony_ci} 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_cistatic int bcm3510_sleep(struct dvb_frontend* fe) 5868c2ecf20Sopenharmony_ci{ 5878c2ecf20Sopenharmony_ci return 0; 5888c2ecf20Sopenharmony_ci} 5898c2ecf20Sopenharmony_ci 5908c2ecf20Sopenharmony_cistatic int bcm3510_get_tune_settings(struct dvb_frontend *fe, struct dvb_frontend_tune_settings *s) 5918c2ecf20Sopenharmony_ci{ 5928c2ecf20Sopenharmony_ci s->min_delay_ms = 1000; 5938c2ecf20Sopenharmony_ci s->step_size = 0; 5948c2ecf20Sopenharmony_ci s->max_drift = 0; 5958c2ecf20Sopenharmony_ci return 0; 5968c2ecf20Sopenharmony_ci} 5978c2ecf20Sopenharmony_ci 5988c2ecf20Sopenharmony_cistatic void bcm3510_release(struct dvb_frontend* fe) 5998c2ecf20Sopenharmony_ci{ 6008c2ecf20Sopenharmony_ci struct bcm3510_state* state = fe->demodulator_priv; 6018c2ecf20Sopenharmony_ci kfree(state); 6028c2ecf20Sopenharmony_ci} 6038c2ecf20Sopenharmony_ci 6048c2ecf20Sopenharmony_ci/* firmware download: 6058c2ecf20Sopenharmony_ci * firmware file is build up like this: 6068c2ecf20Sopenharmony_ci * 16bit addr, 16bit length, 8byte of length 6078c2ecf20Sopenharmony_ci */ 6088c2ecf20Sopenharmony_ci#define BCM3510_DEFAULT_FIRMWARE "dvb-fe-bcm3510-01.fw" 6098c2ecf20Sopenharmony_ci 6108c2ecf20Sopenharmony_cistatic int bcm3510_write_ram(struct bcm3510_state *st, u16 addr, const u8 *b, 6118c2ecf20Sopenharmony_ci u16 len) 6128c2ecf20Sopenharmony_ci{ 6138c2ecf20Sopenharmony_ci int ret = 0,i; 6148c2ecf20Sopenharmony_ci bcm3510_register_value vH, vL,vD; 6158c2ecf20Sopenharmony_ci 6168c2ecf20Sopenharmony_ci vH.MADRH_a9 = addr >> 8; 6178c2ecf20Sopenharmony_ci vL.MADRL_aa = addr; 6188c2ecf20Sopenharmony_ci if ((ret = bcm3510_writeB(st,0xa9,vH)) < 0) return ret; 6198c2ecf20Sopenharmony_ci if ((ret = bcm3510_writeB(st,0xaa,vL)) < 0) return ret; 6208c2ecf20Sopenharmony_ci 6218c2ecf20Sopenharmony_ci for (i = 0; i < len; i++) { 6228c2ecf20Sopenharmony_ci vD.MDATA_ab = b[i]; 6238c2ecf20Sopenharmony_ci if ((ret = bcm3510_writeB(st,0xab,vD)) < 0) 6248c2ecf20Sopenharmony_ci return ret; 6258c2ecf20Sopenharmony_ci } 6268c2ecf20Sopenharmony_ci 6278c2ecf20Sopenharmony_ci return 0; 6288c2ecf20Sopenharmony_ci} 6298c2ecf20Sopenharmony_ci 6308c2ecf20Sopenharmony_cistatic int bcm3510_download_firmware(struct dvb_frontend* fe) 6318c2ecf20Sopenharmony_ci{ 6328c2ecf20Sopenharmony_ci struct bcm3510_state* st = fe->demodulator_priv; 6338c2ecf20Sopenharmony_ci const struct firmware *fw; 6348c2ecf20Sopenharmony_ci u16 addr,len; 6358c2ecf20Sopenharmony_ci const u8 *b; 6368c2ecf20Sopenharmony_ci int ret,i; 6378c2ecf20Sopenharmony_ci 6388c2ecf20Sopenharmony_ci deb_info("requesting firmware\n"); 6398c2ecf20Sopenharmony_ci if ((ret = st->config->request_firmware(fe, &fw, BCM3510_DEFAULT_FIRMWARE)) < 0) { 6408c2ecf20Sopenharmony_ci err("could not load firmware (%s): %d",BCM3510_DEFAULT_FIRMWARE,ret); 6418c2ecf20Sopenharmony_ci return ret; 6428c2ecf20Sopenharmony_ci } 6438c2ecf20Sopenharmony_ci deb_info("got firmware: %zu\n", fw->size); 6448c2ecf20Sopenharmony_ci 6458c2ecf20Sopenharmony_ci b = fw->data; 6468c2ecf20Sopenharmony_ci for (i = 0; i < fw->size;) { 6478c2ecf20Sopenharmony_ci addr = le16_to_cpu(*((__le16 *)&b[i])); 6488c2ecf20Sopenharmony_ci len = le16_to_cpu(*((__le16 *)&b[i+2])); 6498c2ecf20Sopenharmony_ci deb_info("firmware chunk, addr: 0x%04x, len: 0x%04x, total length: 0x%04zx\n",addr,len,fw->size); 6508c2ecf20Sopenharmony_ci if ((ret = bcm3510_write_ram(st,addr,&b[i+4],len)) < 0) { 6518c2ecf20Sopenharmony_ci err("firmware download failed: %d\n",ret); 6528c2ecf20Sopenharmony_ci release_firmware(fw); 6538c2ecf20Sopenharmony_ci return ret; 6548c2ecf20Sopenharmony_ci } 6558c2ecf20Sopenharmony_ci i += 4 + len; 6568c2ecf20Sopenharmony_ci } 6578c2ecf20Sopenharmony_ci release_firmware(fw); 6588c2ecf20Sopenharmony_ci deb_info("firmware download successfully completed\n"); 6598c2ecf20Sopenharmony_ci return 0; 6608c2ecf20Sopenharmony_ci} 6618c2ecf20Sopenharmony_ci 6628c2ecf20Sopenharmony_cistatic int bcm3510_check_firmware_version(struct bcm3510_state *st) 6638c2ecf20Sopenharmony_ci{ 6648c2ecf20Sopenharmony_ci struct bcm3510_hab_cmd_get_version_info ver; 6658c2ecf20Sopenharmony_ci bcm3510_do_hab_cmd(st,CMD_GET_VERSION_INFO,MSGID_GET_VERSION_INFO,NULL,0,(u8*)&ver,sizeof(ver)); 6668c2ecf20Sopenharmony_ci 6678c2ecf20Sopenharmony_ci deb_info("Version information: 0x%02x 0x%02x 0x%02x 0x%02x\n", 6688c2ecf20Sopenharmony_ci ver.microcode_version, ver.script_version, ver.config_version, ver.demod_version); 6698c2ecf20Sopenharmony_ci 6708c2ecf20Sopenharmony_ci if (ver.script_version == BCM3510_DEF_SCRIPT_VERSION && 6718c2ecf20Sopenharmony_ci ver.config_version == BCM3510_DEF_CONFIG_VERSION && 6728c2ecf20Sopenharmony_ci ver.demod_version == BCM3510_DEF_DEMOD_VERSION) 6738c2ecf20Sopenharmony_ci return 0; 6748c2ecf20Sopenharmony_ci 6758c2ecf20Sopenharmony_ci deb_info("version check failed\n"); 6768c2ecf20Sopenharmony_ci return -ENODEV; 6778c2ecf20Sopenharmony_ci} 6788c2ecf20Sopenharmony_ci 6798c2ecf20Sopenharmony_ci/* (un)resetting the AP */ 6808c2ecf20Sopenharmony_cistatic int bcm3510_reset(struct bcm3510_state *st) 6818c2ecf20Sopenharmony_ci{ 6828c2ecf20Sopenharmony_ci int ret; 6838c2ecf20Sopenharmony_ci unsigned long t; 6848c2ecf20Sopenharmony_ci bcm3510_register_value v; 6858c2ecf20Sopenharmony_ci 6868c2ecf20Sopenharmony_ci bcm3510_readB(st,0xa0,&v); v.HCTL1_a0.RESET = 1; 6878c2ecf20Sopenharmony_ci if ((ret = bcm3510_writeB(st,0xa0,v)) < 0) 6888c2ecf20Sopenharmony_ci return ret; 6898c2ecf20Sopenharmony_ci 6908c2ecf20Sopenharmony_ci t = jiffies + 3*HZ; 6918c2ecf20Sopenharmony_ci while (time_before(jiffies, t)) { 6928c2ecf20Sopenharmony_ci msleep(10); 6938c2ecf20Sopenharmony_ci if ((ret = bcm3510_readB(st,0xa2,&v)) < 0) 6948c2ecf20Sopenharmony_ci return ret; 6958c2ecf20Sopenharmony_ci 6968c2ecf20Sopenharmony_ci if (v.APSTAT1_a2.RESET) 6978c2ecf20Sopenharmony_ci return 0; 6988c2ecf20Sopenharmony_ci } 6998c2ecf20Sopenharmony_ci deb_info("reset timed out\n"); 7008c2ecf20Sopenharmony_ci return -ETIMEDOUT; 7018c2ecf20Sopenharmony_ci} 7028c2ecf20Sopenharmony_ci 7038c2ecf20Sopenharmony_cistatic int bcm3510_clear_reset(struct bcm3510_state *st) 7048c2ecf20Sopenharmony_ci{ 7058c2ecf20Sopenharmony_ci bcm3510_register_value v; 7068c2ecf20Sopenharmony_ci int ret; 7078c2ecf20Sopenharmony_ci unsigned long t; 7088c2ecf20Sopenharmony_ci 7098c2ecf20Sopenharmony_ci v.raw = 0; 7108c2ecf20Sopenharmony_ci if ((ret = bcm3510_writeB(st,0xa0,v)) < 0) 7118c2ecf20Sopenharmony_ci return ret; 7128c2ecf20Sopenharmony_ci 7138c2ecf20Sopenharmony_ci t = jiffies + 3*HZ; 7148c2ecf20Sopenharmony_ci while (time_before(jiffies, t)) { 7158c2ecf20Sopenharmony_ci msleep(10); 7168c2ecf20Sopenharmony_ci if ((ret = bcm3510_readB(st,0xa2,&v)) < 0) 7178c2ecf20Sopenharmony_ci return ret; 7188c2ecf20Sopenharmony_ci 7198c2ecf20Sopenharmony_ci /* verify that reset is cleared */ 7208c2ecf20Sopenharmony_ci if (!v.APSTAT1_a2.RESET) 7218c2ecf20Sopenharmony_ci return 0; 7228c2ecf20Sopenharmony_ci } 7238c2ecf20Sopenharmony_ci deb_info("reset clear timed out\n"); 7248c2ecf20Sopenharmony_ci return -ETIMEDOUT; 7258c2ecf20Sopenharmony_ci} 7268c2ecf20Sopenharmony_ci 7278c2ecf20Sopenharmony_cistatic int bcm3510_init_cold(struct bcm3510_state *st) 7288c2ecf20Sopenharmony_ci{ 7298c2ecf20Sopenharmony_ci int ret; 7308c2ecf20Sopenharmony_ci bcm3510_register_value v; 7318c2ecf20Sopenharmony_ci 7328c2ecf20Sopenharmony_ci /* read Acquisation Processor status register and check it is not in RUN mode */ 7338c2ecf20Sopenharmony_ci if ((ret = bcm3510_readB(st,0xa2,&v)) < 0) 7348c2ecf20Sopenharmony_ci return ret; 7358c2ecf20Sopenharmony_ci if (v.APSTAT1_a2.RUN) { 7368c2ecf20Sopenharmony_ci deb_info("AP is already running - firmware already loaded.\n"); 7378c2ecf20Sopenharmony_ci return 0; 7388c2ecf20Sopenharmony_ci } 7398c2ecf20Sopenharmony_ci 7408c2ecf20Sopenharmony_ci deb_info("reset?\n"); 7418c2ecf20Sopenharmony_ci if ((ret = bcm3510_reset(st)) < 0) 7428c2ecf20Sopenharmony_ci return ret; 7438c2ecf20Sopenharmony_ci 7448c2ecf20Sopenharmony_ci deb_info("tristate?\n"); 7458c2ecf20Sopenharmony_ci /* tri-state */ 7468c2ecf20Sopenharmony_ci v.TSTCTL_2e.CTL = 0; 7478c2ecf20Sopenharmony_ci if ((ret = bcm3510_writeB(st,0x2e,v)) < 0) 7488c2ecf20Sopenharmony_ci return ret; 7498c2ecf20Sopenharmony_ci 7508c2ecf20Sopenharmony_ci deb_info("firmware?\n"); 7518c2ecf20Sopenharmony_ci if ((ret = bcm3510_download_firmware(&st->frontend)) < 0 || 7528c2ecf20Sopenharmony_ci (ret = bcm3510_clear_reset(st)) < 0) 7538c2ecf20Sopenharmony_ci return ret; 7548c2ecf20Sopenharmony_ci 7558c2ecf20Sopenharmony_ci /* anything left here to Let the acquisition processor begin execution at program counter 0000 ??? */ 7568c2ecf20Sopenharmony_ci 7578c2ecf20Sopenharmony_ci return 0; 7588c2ecf20Sopenharmony_ci} 7598c2ecf20Sopenharmony_ci 7608c2ecf20Sopenharmony_cistatic int bcm3510_init(struct dvb_frontend* fe) 7618c2ecf20Sopenharmony_ci{ 7628c2ecf20Sopenharmony_ci struct bcm3510_state* st = fe->demodulator_priv; 7638c2ecf20Sopenharmony_ci bcm3510_register_value j; 7648c2ecf20Sopenharmony_ci struct bcm3510_hab_cmd_set_agc c; 7658c2ecf20Sopenharmony_ci int ret; 7668c2ecf20Sopenharmony_ci 7678c2ecf20Sopenharmony_ci if ((ret = bcm3510_readB(st,0xca,&j)) < 0) 7688c2ecf20Sopenharmony_ci return ret; 7698c2ecf20Sopenharmony_ci 7708c2ecf20Sopenharmony_ci deb_info("JDEC: %02x\n",j.raw); 7718c2ecf20Sopenharmony_ci 7728c2ecf20Sopenharmony_ci switch (j.JDEC_ca.JDEC) { 7738c2ecf20Sopenharmony_ci case JDEC_WAIT_AT_RAM: 7748c2ecf20Sopenharmony_ci deb_info("attempting to download firmware\n"); 7758c2ecf20Sopenharmony_ci if ((ret = bcm3510_init_cold(st)) < 0) 7768c2ecf20Sopenharmony_ci return ret; 7778c2ecf20Sopenharmony_ci fallthrough; 7788c2ecf20Sopenharmony_ci case JDEC_EEPROM_LOAD_WAIT: 7798c2ecf20Sopenharmony_ci deb_info("firmware is loaded\n"); 7808c2ecf20Sopenharmony_ci bcm3510_check_firmware_version(st); 7818c2ecf20Sopenharmony_ci break; 7828c2ecf20Sopenharmony_ci default: 7838c2ecf20Sopenharmony_ci return -ENODEV; 7848c2ecf20Sopenharmony_ci } 7858c2ecf20Sopenharmony_ci 7868c2ecf20Sopenharmony_ci memset(&c,0,1); 7878c2ecf20Sopenharmony_ci c.SEL = 1; 7888c2ecf20Sopenharmony_ci bcm3510_do_hab_cmd(st,CMD_AUTO_PARAM,MSGID_SET_RF_AGC_SEL,(u8 *)&c,sizeof(c),NULL,0); 7898c2ecf20Sopenharmony_ci 7908c2ecf20Sopenharmony_ci return 0; 7918c2ecf20Sopenharmony_ci} 7928c2ecf20Sopenharmony_ci 7938c2ecf20Sopenharmony_ci 7948c2ecf20Sopenharmony_cistatic const struct dvb_frontend_ops bcm3510_ops; 7958c2ecf20Sopenharmony_ci 7968c2ecf20Sopenharmony_cistruct dvb_frontend* bcm3510_attach(const struct bcm3510_config *config, 7978c2ecf20Sopenharmony_ci struct i2c_adapter *i2c) 7988c2ecf20Sopenharmony_ci{ 7998c2ecf20Sopenharmony_ci struct bcm3510_state* state = NULL; 8008c2ecf20Sopenharmony_ci int ret; 8018c2ecf20Sopenharmony_ci bcm3510_register_value v; 8028c2ecf20Sopenharmony_ci 8038c2ecf20Sopenharmony_ci /* allocate memory for the internal state */ 8048c2ecf20Sopenharmony_ci state = kzalloc(sizeof(struct bcm3510_state), GFP_KERNEL); 8058c2ecf20Sopenharmony_ci if (state == NULL) 8068c2ecf20Sopenharmony_ci goto error; 8078c2ecf20Sopenharmony_ci 8088c2ecf20Sopenharmony_ci /* setup the state */ 8098c2ecf20Sopenharmony_ci 8108c2ecf20Sopenharmony_ci state->config = config; 8118c2ecf20Sopenharmony_ci state->i2c = i2c; 8128c2ecf20Sopenharmony_ci 8138c2ecf20Sopenharmony_ci /* create dvb_frontend */ 8148c2ecf20Sopenharmony_ci memcpy(&state->frontend.ops, &bcm3510_ops, sizeof(struct dvb_frontend_ops)); 8158c2ecf20Sopenharmony_ci state->frontend.demodulator_priv = state; 8168c2ecf20Sopenharmony_ci 8178c2ecf20Sopenharmony_ci mutex_init(&state->hab_mutex); 8188c2ecf20Sopenharmony_ci 8198c2ecf20Sopenharmony_ci if ((ret = bcm3510_readB(state,0xe0,&v)) < 0) 8208c2ecf20Sopenharmony_ci goto error; 8218c2ecf20Sopenharmony_ci 8228c2ecf20Sopenharmony_ci deb_info("Revision: 0x%1x, Layer: 0x%1x.\n",v.REVID_e0.REV,v.REVID_e0.LAYER); 8238c2ecf20Sopenharmony_ci 8248c2ecf20Sopenharmony_ci if ((v.REVID_e0.REV != 0x1 && v.REVID_e0.LAYER != 0xb) && /* cold */ 8258c2ecf20Sopenharmony_ci (v.REVID_e0.REV != 0x8 && v.REVID_e0.LAYER != 0x0)) /* warm */ 8268c2ecf20Sopenharmony_ci goto error; 8278c2ecf20Sopenharmony_ci 8288c2ecf20Sopenharmony_ci info("Revision: 0x%1x, Layer: 0x%1x.",v.REVID_e0.REV,v.REVID_e0.LAYER); 8298c2ecf20Sopenharmony_ci 8308c2ecf20Sopenharmony_ci bcm3510_reset(state); 8318c2ecf20Sopenharmony_ci 8328c2ecf20Sopenharmony_ci return &state->frontend; 8338c2ecf20Sopenharmony_ci 8348c2ecf20Sopenharmony_cierror: 8358c2ecf20Sopenharmony_ci kfree(state); 8368c2ecf20Sopenharmony_ci return NULL; 8378c2ecf20Sopenharmony_ci} 8388c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(bcm3510_attach); 8398c2ecf20Sopenharmony_ci 8408c2ecf20Sopenharmony_cistatic const struct dvb_frontend_ops bcm3510_ops = { 8418c2ecf20Sopenharmony_ci .delsys = { SYS_ATSC, SYS_DVBC_ANNEX_B }, 8428c2ecf20Sopenharmony_ci .info = { 8438c2ecf20Sopenharmony_ci .name = "Broadcom BCM3510 VSB/QAM frontend", 8448c2ecf20Sopenharmony_ci .frequency_min_hz = 54 * MHz, 8458c2ecf20Sopenharmony_ci .frequency_max_hz = 803 * MHz, 8468c2ecf20Sopenharmony_ci .caps = 8478c2ecf20Sopenharmony_ci FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | 8488c2ecf20Sopenharmony_ci FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | 8498c2ecf20Sopenharmony_ci FE_CAN_8VSB | FE_CAN_16VSB | 8508c2ecf20Sopenharmony_ci FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_128 | FE_CAN_QAM_256 8518c2ecf20Sopenharmony_ci }, 8528c2ecf20Sopenharmony_ci 8538c2ecf20Sopenharmony_ci .release = bcm3510_release, 8548c2ecf20Sopenharmony_ci 8558c2ecf20Sopenharmony_ci .init = bcm3510_init, 8568c2ecf20Sopenharmony_ci .sleep = bcm3510_sleep, 8578c2ecf20Sopenharmony_ci 8588c2ecf20Sopenharmony_ci .set_frontend = bcm3510_set_frontend, 8598c2ecf20Sopenharmony_ci .get_tune_settings = bcm3510_get_tune_settings, 8608c2ecf20Sopenharmony_ci 8618c2ecf20Sopenharmony_ci .read_status = bcm3510_read_status, 8628c2ecf20Sopenharmony_ci .read_ber = bcm3510_read_ber, 8638c2ecf20Sopenharmony_ci .read_signal_strength = bcm3510_read_signal_strength, 8648c2ecf20Sopenharmony_ci .read_snr = bcm3510_read_snr, 8658c2ecf20Sopenharmony_ci .read_ucblocks = bcm3510_read_unc, 8668c2ecf20Sopenharmony_ci}; 8678c2ecf20Sopenharmony_ci 8688c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Broadcom BCM3510 ATSC (8VSB/16VSB & ITU J83 AnnexB FEC QAM64/256) demodulator driver"); 8698c2ecf20Sopenharmony_ciMODULE_AUTHOR("Patrick Boettcher <patrick.boettcher@posteo.de>"); 8708c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 871