18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Support for OR51132 (pcHDTV HD-3000) - VSB/QAM 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2007 Trent Piepho <xyzzy@speakeasy.org> 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Copyright (C) 2005 Kirk Lapray <kirk_lapray@bigfoot.com> 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * Based on code from Jack Kelliher (kelliher@xmission.com) 108c2ecf20Sopenharmony_ci * Copyright (C) 2002 & pcHDTV, inc. 118c2ecf20Sopenharmony_ci*/ 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci/* 148c2ecf20Sopenharmony_ci * This driver needs two external firmware files. Please copy 158c2ecf20Sopenharmony_ci * "dvb-fe-or51132-vsb.fw" and "dvb-fe-or51132-qam.fw" to 168c2ecf20Sopenharmony_ci * /usr/lib/hotplug/firmware/ or /lib/firmware/ 178c2ecf20Sopenharmony_ci * (depending on configuration of firmware hotplug). 188c2ecf20Sopenharmony_ci */ 198c2ecf20Sopenharmony_ci#define OR51132_VSB_FIRMWARE "dvb-fe-or51132-vsb.fw" 208c2ecf20Sopenharmony_ci#define OR51132_QAM_FIRMWARE "dvb-fe-or51132-qam.fw" 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#include <linux/kernel.h> 238c2ecf20Sopenharmony_ci#include <linux/module.h> 248c2ecf20Sopenharmony_ci#include <linux/init.h> 258c2ecf20Sopenharmony_ci#include <linux/delay.h> 268c2ecf20Sopenharmony_ci#include <linux/string.h> 278c2ecf20Sopenharmony_ci#include <linux/slab.h> 288c2ecf20Sopenharmony_ci#include <asm/byteorder.h> 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci#include <media/dvb_math.h> 318c2ecf20Sopenharmony_ci#include <media/dvb_frontend.h> 328c2ecf20Sopenharmony_ci#include "or51132.h" 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_cistatic int debug; 358c2ecf20Sopenharmony_ci#define dprintk(args...) \ 368c2ecf20Sopenharmony_ci do { \ 378c2ecf20Sopenharmony_ci if (debug) printk(KERN_DEBUG "or51132: " args); \ 388c2ecf20Sopenharmony_ci } while (0) 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_cistruct or51132_state 428c2ecf20Sopenharmony_ci{ 438c2ecf20Sopenharmony_ci struct i2c_adapter* i2c; 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci /* Configuration settings */ 468c2ecf20Sopenharmony_ci const struct or51132_config* config; 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci struct dvb_frontend frontend; 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci /* Demodulator private data */ 518c2ecf20Sopenharmony_ci enum fe_modulation current_modulation; 528c2ecf20Sopenharmony_ci u32 snr; /* Result of last SNR calculation */ 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci /* Tuner private data */ 558c2ecf20Sopenharmony_ci u32 current_frequency; 568c2ecf20Sopenharmony_ci}; 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci/* Write buffer to demod */ 608c2ecf20Sopenharmony_cistatic int or51132_writebuf(struct or51132_state *state, const u8 *buf, int len) 618c2ecf20Sopenharmony_ci{ 628c2ecf20Sopenharmony_ci int err; 638c2ecf20Sopenharmony_ci struct i2c_msg msg = { .addr = state->config->demod_address, 648c2ecf20Sopenharmony_ci .flags = 0, .buf = (u8*)buf, .len = len }; 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci /* msleep(20); */ /* doesn't appear to be necessary */ 678c2ecf20Sopenharmony_ci if ((err = i2c_transfer(state->i2c, &msg, 1)) != 1) { 688c2ecf20Sopenharmony_ci printk(KERN_WARNING "or51132: I2C write (addr 0x%02x len %d) error: %d\n", 698c2ecf20Sopenharmony_ci msg.addr, msg.len, err); 708c2ecf20Sopenharmony_ci return -EREMOTEIO; 718c2ecf20Sopenharmony_ci } 728c2ecf20Sopenharmony_ci return 0; 738c2ecf20Sopenharmony_ci} 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci/* Write constant bytes, e.g. or51132_writebytes(state, 0x04, 0x42, 0x00); 768c2ecf20Sopenharmony_ci Less code and more efficient that loading a buffer on the stack with 778c2ecf20Sopenharmony_ci the bytes to send and then calling or51132_writebuf() on that. */ 788c2ecf20Sopenharmony_ci#define or51132_writebytes(state, data...) \ 798c2ecf20Sopenharmony_ci ({ static const u8 _data[] = {data}; \ 808c2ecf20Sopenharmony_ci or51132_writebuf(state, _data, sizeof(_data)); }) 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci/* Read data from demod into buffer. Returns 0 on success. */ 838c2ecf20Sopenharmony_cistatic int or51132_readbuf(struct or51132_state *state, u8 *buf, int len) 848c2ecf20Sopenharmony_ci{ 858c2ecf20Sopenharmony_ci int err; 868c2ecf20Sopenharmony_ci struct i2c_msg msg = { .addr = state->config->demod_address, 878c2ecf20Sopenharmony_ci .flags = I2C_M_RD, .buf = buf, .len = len }; 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci /* msleep(20); */ /* doesn't appear to be necessary */ 908c2ecf20Sopenharmony_ci if ((err = i2c_transfer(state->i2c, &msg, 1)) != 1) { 918c2ecf20Sopenharmony_ci printk(KERN_WARNING "or51132: I2C read (addr 0x%02x len %d) error: %d\n", 928c2ecf20Sopenharmony_ci msg.addr, msg.len, err); 938c2ecf20Sopenharmony_ci return -EREMOTEIO; 948c2ecf20Sopenharmony_ci } 958c2ecf20Sopenharmony_ci return 0; 968c2ecf20Sopenharmony_ci} 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci/* Reads a 16-bit demod register. Returns <0 on error. */ 998c2ecf20Sopenharmony_cistatic int or51132_readreg(struct or51132_state *state, u8 reg) 1008c2ecf20Sopenharmony_ci{ 1018c2ecf20Sopenharmony_ci u8 buf[2] = { 0x04, reg }; 1028c2ecf20Sopenharmony_ci struct i2c_msg msg[2] = { 1038c2ecf20Sopenharmony_ci {.addr = state->config->demod_address, .flags = 0, 1048c2ecf20Sopenharmony_ci .buf = buf, .len = 2 }, 1058c2ecf20Sopenharmony_ci {.addr = state->config->demod_address, .flags = I2C_M_RD, 1068c2ecf20Sopenharmony_ci .buf = buf, .len = 2 }}; 1078c2ecf20Sopenharmony_ci int err; 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci if ((err = i2c_transfer(state->i2c, msg, 2)) != 2) { 1108c2ecf20Sopenharmony_ci printk(KERN_WARNING "or51132: I2C error reading register %d: %d\n", 1118c2ecf20Sopenharmony_ci reg, err); 1128c2ecf20Sopenharmony_ci return -EREMOTEIO; 1138c2ecf20Sopenharmony_ci } 1148c2ecf20Sopenharmony_ci return buf[0] | (buf[1] << 8); 1158c2ecf20Sopenharmony_ci} 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_cistatic int or51132_load_firmware (struct dvb_frontend* fe, const struct firmware *fw) 1188c2ecf20Sopenharmony_ci{ 1198c2ecf20Sopenharmony_ci struct or51132_state* state = fe->demodulator_priv; 1208c2ecf20Sopenharmony_ci static const u8 run_buf[] = {0x7F,0x01}; 1218c2ecf20Sopenharmony_ci u8 rec_buf[8]; 1228c2ecf20Sopenharmony_ci u32 firmwareAsize, firmwareBsize; 1238c2ecf20Sopenharmony_ci int i,ret; 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci dprintk("Firmware is %zd bytes\n",fw->size); 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci /* Get size of firmware A and B */ 1288c2ecf20Sopenharmony_ci firmwareAsize = le32_to_cpu(*((__le32*)fw->data)); 1298c2ecf20Sopenharmony_ci dprintk("FirmwareA is %i bytes\n",firmwareAsize); 1308c2ecf20Sopenharmony_ci firmwareBsize = le32_to_cpu(*((__le32*)(fw->data+4))); 1318c2ecf20Sopenharmony_ci dprintk("FirmwareB is %i bytes\n",firmwareBsize); 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci /* Upload firmware */ 1348c2ecf20Sopenharmony_ci if ((ret = or51132_writebuf(state, &fw->data[8], firmwareAsize))) { 1358c2ecf20Sopenharmony_ci printk(KERN_WARNING "or51132: load_firmware error 1\n"); 1368c2ecf20Sopenharmony_ci return ret; 1378c2ecf20Sopenharmony_ci } 1388c2ecf20Sopenharmony_ci if ((ret = or51132_writebuf(state, &fw->data[8+firmwareAsize], 1398c2ecf20Sopenharmony_ci firmwareBsize))) { 1408c2ecf20Sopenharmony_ci printk(KERN_WARNING "or51132: load_firmware error 2\n"); 1418c2ecf20Sopenharmony_ci return ret; 1428c2ecf20Sopenharmony_ci } 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci if ((ret = or51132_writebuf(state, run_buf, 2))) { 1458c2ecf20Sopenharmony_ci printk(KERN_WARNING "or51132: load_firmware error 3\n"); 1468c2ecf20Sopenharmony_ci return ret; 1478c2ecf20Sopenharmony_ci } 1488c2ecf20Sopenharmony_ci if ((ret = or51132_writebuf(state, run_buf, 2))) { 1498c2ecf20Sopenharmony_ci printk(KERN_WARNING "or51132: load_firmware error 4\n"); 1508c2ecf20Sopenharmony_ci return ret; 1518c2ecf20Sopenharmony_ci } 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci /* 50ms for operation to begin */ 1548c2ecf20Sopenharmony_ci msleep(50); 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci /* Read back ucode version to besure we loaded correctly and are really up and running */ 1578c2ecf20Sopenharmony_ci /* Get uCode version */ 1588c2ecf20Sopenharmony_ci if ((ret = or51132_writebytes(state, 0x10, 0x10, 0x00))) { 1598c2ecf20Sopenharmony_ci printk(KERN_WARNING "or51132: load_firmware error a\n"); 1608c2ecf20Sopenharmony_ci return ret; 1618c2ecf20Sopenharmony_ci } 1628c2ecf20Sopenharmony_ci if ((ret = or51132_writebytes(state, 0x04, 0x17))) { 1638c2ecf20Sopenharmony_ci printk(KERN_WARNING "or51132: load_firmware error b\n"); 1648c2ecf20Sopenharmony_ci return ret; 1658c2ecf20Sopenharmony_ci } 1668c2ecf20Sopenharmony_ci if ((ret = or51132_writebytes(state, 0x00, 0x00))) { 1678c2ecf20Sopenharmony_ci printk(KERN_WARNING "or51132: load_firmware error c\n"); 1688c2ecf20Sopenharmony_ci return ret; 1698c2ecf20Sopenharmony_ci } 1708c2ecf20Sopenharmony_ci for (i=0;i<4;i++) { 1718c2ecf20Sopenharmony_ci /* Once upon a time, this command might have had something 1728c2ecf20Sopenharmony_ci to do with getting the firmware version, but it's 1738c2ecf20Sopenharmony_ci not used anymore: 1748c2ecf20Sopenharmony_ci {0x04,0x00,0x30,0x00,i+1} */ 1758c2ecf20Sopenharmony_ci /* Read 8 bytes, two bytes at a time */ 1768c2ecf20Sopenharmony_ci if ((ret = or51132_readbuf(state, &rec_buf[i*2], 2))) { 1778c2ecf20Sopenharmony_ci printk(KERN_WARNING 1788c2ecf20Sopenharmony_ci "or51132: load_firmware error d - %d\n",i); 1798c2ecf20Sopenharmony_ci return ret; 1808c2ecf20Sopenharmony_ci } 1818c2ecf20Sopenharmony_ci } 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci printk(KERN_WARNING 1848c2ecf20Sopenharmony_ci "or51132: Version: %02X%02X%02X%02X-%02X%02X%02X%02X (%02X%01X-%01X-%02X%01X-%01X)\n", 1858c2ecf20Sopenharmony_ci rec_buf[1],rec_buf[0],rec_buf[3],rec_buf[2], 1868c2ecf20Sopenharmony_ci rec_buf[5],rec_buf[4],rec_buf[7],rec_buf[6], 1878c2ecf20Sopenharmony_ci rec_buf[3],rec_buf[2]>>4,rec_buf[2]&0x0f, 1888c2ecf20Sopenharmony_ci rec_buf[5],rec_buf[4]>>4,rec_buf[4]&0x0f); 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci if ((ret = or51132_writebytes(state, 0x10, 0x00, 0x00))) { 1918c2ecf20Sopenharmony_ci printk(KERN_WARNING "or51132: load_firmware error e\n"); 1928c2ecf20Sopenharmony_ci return ret; 1938c2ecf20Sopenharmony_ci } 1948c2ecf20Sopenharmony_ci return 0; 1958c2ecf20Sopenharmony_ci}; 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_cistatic int or51132_init(struct dvb_frontend* fe) 1988c2ecf20Sopenharmony_ci{ 1998c2ecf20Sopenharmony_ci return 0; 2008c2ecf20Sopenharmony_ci} 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_cistatic int or51132_read_ber(struct dvb_frontend* fe, u32* ber) 2038c2ecf20Sopenharmony_ci{ 2048c2ecf20Sopenharmony_ci *ber = 0; 2058c2ecf20Sopenharmony_ci return 0; 2068c2ecf20Sopenharmony_ci} 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_cistatic int or51132_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks) 2098c2ecf20Sopenharmony_ci{ 2108c2ecf20Sopenharmony_ci *ucblocks = 0; 2118c2ecf20Sopenharmony_ci return 0; 2128c2ecf20Sopenharmony_ci} 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_cistatic int or51132_sleep(struct dvb_frontend* fe) 2158c2ecf20Sopenharmony_ci{ 2168c2ecf20Sopenharmony_ci return 0; 2178c2ecf20Sopenharmony_ci} 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_cistatic int or51132_setmode(struct dvb_frontend* fe) 2208c2ecf20Sopenharmony_ci{ 2218c2ecf20Sopenharmony_ci struct or51132_state* state = fe->demodulator_priv; 2228c2ecf20Sopenharmony_ci u8 cmd_buf1[3] = {0x04, 0x01, 0x5f}; 2238c2ecf20Sopenharmony_ci u8 cmd_buf2[3] = {0x1c, 0x00, 0 }; 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci dprintk("setmode %d\n",(int)state->current_modulation); 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci switch (state->current_modulation) { 2288c2ecf20Sopenharmony_ci case VSB_8: 2298c2ecf20Sopenharmony_ci /* Auto CH, Auto NTSC rej, MPEGser, MPEG2tr, phase noise-high */ 2308c2ecf20Sopenharmony_ci cmd_buf1[2] = 0x50; 2318c2ecf20Sopenharmony_ci /* REC MODE inv IF spectrum, Normal */ 2328c2ecf20Sopenharmony_ci cmd_buf2[1] = 0x03; 2338c2ecf20Sopenharmony_ci /* Channel MODE ATSC/VSB8 */ 2348c2ecf20Sopenharmony_ci cmd_buf2[2] = 0x06; 2358c2ecf20Sopenharmony_ci break; 2368c2ecf20Sopenharmony_ci /* All QAM modes are: 2378c2ecf20Sopenharmony_ci Auto-deinterleave; MPEGser, MPEG2tr, phase noise-high 2388c2ecf20Sopenharmony_ci REC MODE Normal Carrier Lock */ 2398c2ecf20Sopenharmony_ci case QAM_AUTO: 2408c2ecf20Sopenharmony_ci /* Channel MODE Auto QAM64/256 */ 2418c2ecf20Sopenharmony_ci cmd_buf2[2] = 0x4f; 2428c2ecf20Sopenharmony_ci break; 2438c2ecf20Sopenharmony_ci case QAM_256: 2448c2ecf20Sopenharmony_ci /* Channel MODE QAM256 */ 2458c2ecf20Sopenharmony_ci cmd_buf2[2] = 0x45; 2468c2ecf20Sopenharmony_ci break; 2478c2ecf20Sopenharmony_ci case QAM_64: 2488c2ecf20Sopenharmony_ci /* Channel MODE QAM64 */ 2498c2ecf20Sopenharmony_ci cmd_buf2[2] = 0x43; 2508c2ecf20Sopenharmony_ci break; 2518c2ecf20Sopenharmony_ci default: 2528c2ecf20Sopenharmony_ci printk(KERN_WARNING 2538c2ecf20Sopenharmony_ci "or51132: setmode: Modulation set to unsupported value (%d)\n", 2548c2ecf20Sopenharmony_ci state->current_modulation); 2558c2ecf20Sopenharmony_ci return -EINVAL; 2568c2ecf20Sopenharmony_ci } 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci /* Set Receiver 1 register */ 2598c2ecf20Sopenharmony_ci if (or51132_writebuf(state, cmd_buf1, 3)) { 2608c2ecf20Sopenharmony_ci printk(KERN_WARNING "or51132: set_mode error 1\n"); 2618c2ecf20Sopenharmony_ci return -EREMOTEIO; 2628c2ecf20Sopenharmony_ci } 2638c2ecf20Sopenharmony_ci dprintk("set #1 to %02x\n", cmd_buf1[2]); 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci /* Set operation mode in Receiver 6 register */ 2668c2ecf20Sopenharmony_ci if (or51132_writebuf(state, cmd_buf2, 3)) { 2678c2ecf20Sopenharmony_ci printk(KERN_WARNING "or51132: set_mode error 2\n"); 2688c2ecf20Sopenharmony_ci return -EREMOTEIO; 2698c2ecf20Sopenharmony_ci } 2708c2ecf20Sopenharmony_ci dprintk("set #6 to 0x%02x%02x\n", cmd_buf2[1], cmd_buf2[2]); 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci return 0; 2738c2ecf20Sopenharmony_ci} 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci/* Some modulations use the same firmware. This classifies modulations 2768c2ecf20Sopenharmony_ci by the firmware they use. */ 2778c2ecf20Sopenharmony_ci#define MOD_FWCLASS_UNKNOWN 0 2788c2ecf20Sopenharmony_ci#define MOD_FWCLASS_VSB 1 2798c2ecf20Sopenharmony_ci#define MOD_FWCLASS_QAM 2 2808c2ecf20Sopenharmony_cistatic int modulation_fw_class(enum fe_modulation modulation) 2818c2ecf20Sopenharmony_ci{ 2828c2ecf20Sopenharmony_ci switch(modulation) { 2838c2ecf20Sopenharmony_ci case VSB_8: 2848c2ecf20Sopenharmony_ci return MOD_FWCLASS_VSB; 2858c2ecf20Sopenharmony_ci case QAM_AUTO: 2868c2ecf20Sopenharmony_ci case QAM_64: 2878c2ecf20Sopenharmony_ci case QAM_256: 2888c2ecf20Sopenharmony_ci return MOD_FWCLASS_QAM; 2898c2ecf20Sopenharmony_ci default: 2908c2ecf20Sopenharmony_ci return MOD_FWCLASS_UNKNOWN; 2918c2ecf20Sopenharmony_ci } 2928c2ecf20Sopenharmony_ci} 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_cistatic int or51132_set_parameters(struct dvb_frontend *fe) 2958c2ecf20Sopenharmony_ci{ 2968c2ecf20Sopenharmony_ci struct dtv_frontend_properties *p = &fe->dtv_property_cache; 2978c2ecf20Sopenharmony_ci int ret; 2988c2ecf20Sopenharmony_ci struct or51132_state* state = fe->demodulator_priv; 2998c2ecf20Sopenharmony_ci const struct firmware *fw; 3008c2ecf20Sopenharmony_ci const char *fwname; 3018c2ecf20Sopenharmony_ci int clock_mode; 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci /* Upload new firmware only if we need a different one */ 3048c2ecf20Sopenharmony_ci if (modulation_fw_class(state->current_modulation) != 3058c2ecf20Sopenharmony_ci modulation_fw_class(p->modulation)) { 3068c2ecf20Sopenharmony_ci switch (modulation_fw_class(p->modulation)) { 3078c2ecf20Sopenharmony_ci case MOD_FWCLASS_VSB: 3088c2ecf20Sopenharmony_ci dprintk("set_parameters VSB MODE\n"); 3098c2ecf20Sopenharmony_ci fwname = OR51132_VSB_FIRMWARE; 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci /* Set non-punctured clock for VSB */ 3128c2ecf20Sopenharmony_ci clock_mode = 0; 3138c2ecf20Sopenharmony_ci break; 3148c2ecf20Sopenharmony_ci case MOD_FWCLASS_QAM: 3158c2ecf20Sopenharmony_ci dprintk("set_parameters QAM MODE\n"); 3168c2ecf20Sopenharmony_ci fwname = OR51132_QAM_FIRMWARE; 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci /* Set punctured clock for QAM */ 3198c2ecf20Sopenharmony_ci clock_mode = 1; 3208c2ecf20Sopenharmony_ci break; 3218c2ecf20Sopenharmony_ci default: 3228c2ecf20Sopenharmony_ci printk("or51132: Modulation type(%d) UNSUPPORTED\n", 3238c2ecf20Sopenharmony_ci p->modulation); 3248c2ecf20Sopenharmony_ci return -1; 3258c2ecf20Sopenharmony_ci } 3268c2ecf20Sopenharmony_ci printk("or51132: Waiting for firmware upload(%s)...\n", 3278c2ecf20Sopenharmony_ci fwname); 3288c2ecf20Sopenharmony_ci ret = request_firmware(&fw, fwname, state->i2c->dev.parent); 3298c2ecf20Sopenharmony_ci if (ret) { 3308c2ecf20Sopenharmony_ci printk(KERN_WARNING "or51132: No firmware uploaded(timeout or file not found?)\n"); 3318c2ecf20Sopenharmony_ci return ret; 3328c2ecf20Sopenharmony_ci } 3338c2ecf20Sopenharmony_ci ret = or51132_load_firmware(fe, fw); 3348c2ecf20Sopenharmony_ci release_firmware(fw); 3358c2ecf20Sopenharmony_ci if (ret) { 3368c2ecf20Sopenharmony_ci printk(KERN_WARNING "or51132: Writing firmware to device failed!\n"); 3378c2ecf20Sopenharmony_ci return ret; 3388c2ecf20Sopenharmony_ci } 3398c2ecf20Sopenharmony_ci printk("or51132: Firmware upload complete.\n"); 3408c2ecf20Sopenharmony_ci state->config->set_ts_params(fe, clock_mode); 3418c2ecf20Sopenharmony_ci } 3428c2ecf20Sopenharmony_ci /* Change only if we are actually changing the modulation */ 3438c2ecf20Sopenharmony_ci if (state->current_modulation != p->modulation) { 3448c2ecf20Sopenharmony_ci state->current_modulation = p->modulation; 3458c2ecf20Sopenharmony_ci or51132_setmode(fe); 3468c2ecf20Sopenharmony_ci } 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci if (fe->ops.tuner_ops.set_params) { 3498c2ecf20Sopenharmony_ci fe->ops.tuner_ops.set_params(fe); 3508c2ecf20Sopenharmony_ci if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0); 3518c2ecf20Sopenharmony_ci } 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci /* Set to current mode */ 3548c2ecf20Sopenharmony_ci or51132_setmode(fe); 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci /* Update current frequency */ 3578c2ecf20Sopenharmony_ci state->current_frequency = p->frequency; 3588c2ecf20Sopenharmony_ci return 0; 3598c2ecf20Sopenharmony_ci} 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_cistatic int or51132_get_parameters(struct dvb_frontend* fe, 3628c2ecf20Sopenharmony_ci struct dtv_frontend_properties *p) 3638c2ecf20Sopenharmony_ci{ 3648c2ecf20Sopenharmony_ci struct or51132_state* state = fe->demodulator_priv; 3658c2ecf20Sopenharmony_ci int status; 3668c2ecf20Sopenharmony_ci int retry = 1; 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_cistart: 3698c2ecf20Sopenharmony_ci /* Receiver Status */ 3708c2ecf20Sopenharmony_ci if ((status = or51132_readreg(state, 0x00)) < 0) { 3718c2ecf20Sopenharmony_ci printk(KERN_WARNING "or51132: get_parameters: error reading receiver status\n"); 3728c2ecf20Sopenharmony_ci return -EREMOTEIO; 3738c2ecf20Sopenharmony_ci } 3748c2ecf20Sopenharmony_ci switch(status&0xff) { 3758c2ecf20Sopenharmony_ci case 0x06: 3768c2ecf20Sopenharmony_ci p->modulation = VSB_8; 3778c2ecf20Sopenharmony_ci break; 3788c2ecf20Sopenharmony_ci case 0x43: 3798c2ecf20Sopenharmony_ci p->modulation = QAM_64; 3808c2ecf20Sopenharmony_ci break; 3818c2ecf20Sopenharmony_ci case 0x45: 3828c2ecf20Sopenharmony_ci p->modulation = QAM_256; 3838c2ecf20Sopenharmony_ci break; 3848c2ecf20Sopenharmony_ci default: 3858c2ecf20Sopenharmony_ci if (retry--) 3868c2ecf20Sopenharmony_ci goto start; 3878c2ecf20Sopenharmony_ci printk(KERN_WARNING "or51132: unknown status 0x%02x\n", 3888c2ecf20Sopenharmony_ci status&0xff); 3898c2ecf20Sopenharmony_ci return -EREMOTEIO; 3908c2ecf20Sopenharmony_ci } 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci /* FIXME: Read frequency from frontend, take AFC into account */ 3938c2ecf20Sopenharmony_ci p->frequency = state->current_frequency; 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci /* FIXME: How to read inversion setting? Receiver 6 register? */ 3968c2ecf20Sopenharmony_ci p->inversion = INVERSION_AUTO; 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci return 0; 3998c2ecf20Sopenharmony_ci} 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_cistatic int or51132_read_status(struct dvb_frontend *fe, enum fe_status *status) 4028c2ecf20Sopenharmony_ci{ 4038c2ecf20Sopenharmony_ci struct or51132_state* state = fe->demodulator_priv; 4048c2ecf20Sopenharmony_ci int reg; 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci /* Receiver Status */ 4078c2ecf20Sopenharmony_ci if ((reg = or51132_readreg(state, 0x00)) < 0) { 4088c2ecf20Sopenharmony_ci printk(KERN_WARNING "or51132: read_status: error reading receiver status: %d\n", reg); 4098c2ecf20Sopenharmony_ci *status = 0; 4108c2ecf20Sopenharmony_ci return -EREMOTEIO; 4118c2ecf20Sopenharmony_ci } 4128c2ecf20Sopenharmony_ci dprintk("%s: read_status %04x\n", __func__, reg); 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci if (reg & 0x0100) /* Receiver Lock */ 4158c2ecf20Sopenharmony_ci *status = FE_HAS_SIGNAL|FE_HAS_CARRIER|FE_HAS_VITERBI| 4168c2ecf20Sopenharmony_ci FE_HAS_SYNC|FE_HAS_LOCK; 4178c2ecf20Sopenharmony_ci else 4188c2ecf20Sopenharmony_ci *status = 0; 4198c2ecf20Sopenharmony_ci return 0; 4208c2ecf20Sopenharmony_ci} 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci/* Calculate SNR estimation (scaled by 2^24) 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci 8-VSB SNR and QAM equations from Oren datasheets 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci For 8-VSB: 4278c2ecf20Sopenharmony_ci SNR[dB] = 10 * log10(897152044.8282 / MSE^2 ) - K 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci Where K = 0 if NTSC rejection filter is OFF; and 4308c2ecf20Sopenharmony_ci K = 3 if NTSC rejection filter is ON 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci For QAM64: 4338c2ecf20Sopenharmony_ci SNR[dB] = 10 * log10(897152044.8282 / MSE^2 ) 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci For QAM256: 4368c2ecf20Sopenharmony_ci SNR[dB] = 10 * log10(907832426.314266 / MSE^2 ) 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci We re-write the snr equation as: 4398c2ecf20Sopenharmony_ci SNR * 2^24 = 10*(c - 2*intlog10(MSE)) 4408c2ecf20Sopenharmony_ci Where for QAM256, c = log10(907832426.314266) * 2^24 4418c2ecf20Sopenharmony_ci and for 8-VSB and QAM64, c = log10(897152044.8282) * 2^24 */ 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_cistatic u32 calculate_snr(u32 mse, u32 c) 4448c2ecf20Sopenharmony_ci{ 4458c2ecf20Sopenharmony_ci if (mse == 0) /* No signal */ 4468c2ecf20Sopenharmony_ci return 0; 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci mse = 2*intlog10(mse); 4498c2ecf20Sopenharmony_ci if (mse > c) { 4508c2ecf20Sopenharmony_ci /* Negative SNR, which is possible, but realisticly the 4518c2ecf20Sopenharmony_ci demod will lose lock before the signal gets this bad. The 4528c2ecf20Sopenharmony_ci API only allows for unsigned values, so just return 0 */ 4538c2ecf20Sopenharmony_ci return 0; 4548c2ecf20Sopenharmony_ci } 4558c2ecf20Sopenharmony_ci return 10*(c - mse); 4568c2ecf20Sopenharmony_ci} 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_cistatic int or51132_read_snr(struct dvb_frontend* fe, u16* snr) 4598c2ecf20Sopenharmony_ci{ 4608c2ecf20Sopenharmony_ci struct or51132_state* state = fe->demodulator_priv; 4618c2ecf20Sopenharmony_ci int noise, reg; 4628c2ecf20Sopenharmony_ci u32 c, usK = 0; 4638c2ecf20Sopenharmony_ci int retry = 1; 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_cistart: 4668c2ecf20Sopenharmony_ci /* SNR after Equalizer */ 4678c2ecf20Sopenharmony_ci noise = or51132_readreg(state, 0x02); 4688c2ecf20Sopenharmony_ci if (noise < 0) { 4698c2ecf20Sopenharmony_ci printk(KERN_WARNING "or51132: read_snr: error reading equalizer\n"); 4708c2ecf20Sopenharmony_ci return -EREMOTEIO; 4718c2ecf20Sopenharmony_ci } 4728c2ecf20Sopenharmony_ci dprintk("read_snr noise (%d)\n", noise); 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci /* Read status, contains modulation type for QAM_AUTO and 4758c2ecf20Sopenharmony_ci NTSC filter for VSB */ 4768c2ecf20Sopenharmony_ci reg = or51132_readreg(state, 0x00); 4778c2ecf20Sopenharmony_ci if (reg < 0) { 4788c2ecf20Sopenharmony_ci printk(KERN_WARNING "or51132: read_snr: error reading receiver status\n"); 4798c2ecf20Sopenharmony_ci return -EREMOTEIO; 4808c2ecf20Sopenharmony_ci } 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ci switch (reg&0xff) { 4838c2ecf20Sopenharmony_ci case 0x06: 4848c2ecf20Sopenharmony_ci if (reg & 0x1000) usK = 3 << 24; 4858c2ecf20Sopenharmony_ci fallthrough; 4868c2ecf20Sopenharmony_ci case 0x43: /* QAM64 */ 4878c2ecf20Sopenharmony_ci c = 150204167; 4888c2ecf20Sopenharmony_ci break; 4898c2ecf20Sopenharmony_ci case 0x45: 4908c2ecf20Sopenharmony_ci c = 150290396; 4918c2ecf20Sopenharmony_ci break; 4928c2ecf20Sopenharmony_ci default: 4938c2ecf20Sopenharmony_ci printk(KERN_WARNING "or51132: unknown status 0x%02x\n", reg&0xff); 4948c2ecf20Sopenharmony_ci if (retry--) goto start; 4958c2ecf20Sopenharmony_ci return -EREMOTEIO; 4968c2ecf20Sopenharmony_ci } 4978c2ecf20Sopenharmony_ci dprintk("%s: modulation %02x, NTSC rej O%s\n", __func__, 4988c2ecf20Sopenharmony_ci reg&0xff, reg&0x1000?"n":"ff"); 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_ci /* Calculate SNR using noise, c, and NTSC rejection correction */ 5018c2ecf20Sopenharmony_ci state->snr = calculate_snr(noise, c) - usK; 5028c2ecf20Sopenharmony_ci *snr = (state->snr) >> 16; 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci dprintk("%s: noise = 0x%08x, snr = %d.%02d dB\n", __func__, noise, 5058c2ecf20Sopenharmony_ci state->snr >> 24, (((state->snr>>8) & 0xffff) * 100) >> 16); 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_ci return 0; 5088c2ecf20Sopenharmony_ci} 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_cistatic int or51132_read_signal_strength(struct dvb_frontend* fe, u16* strength) 5118c2ecf20Sopenharmony_ci{ 5128c2ecf20Sopenharmony_ci /* Calculate Strength from SNR up to 35dB */ 5138c2ecf20Sopenharmony_ci /* Even though the SNR can go higher than 35dB, there is some comfort */ 5148c2ecf20Sopenharmony_ci /* factor in having a range of strong signals that can show at 100% */ 5158c2ecf20Sopenharmony_ci struct or51132_state* state = (struct or51132_state*) fe->demodulator_priv; 5168c2ecf20Sopenharmony_ci u16 snr; 5178c2ecf20Sopenharmony_ci int ret; 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_ci ret = fe->ops.read_snr(fe, &snr); 5208c2ecf20Sopenharmony_ci if (ret != 0) 5218c2ecf20Sopenharmony_ci return ret; 5228c2ecf20Sopenharmony_ci /* Rather than use the 8.8 value snr, use state->snr which is 8.24 */ 5238c2ecf20Sopenharmony_ci /* scale the range 0 - 35*2^24 into 0 - 65535 */ 5248c2ecf20Sopenharmony_ci if (state->snr >= 8960 * 0x10000) 5258c2ecf20Sopenharmony_ci *strength = 0xffff; 5268c2ecf20Sopenharmony_ci else 5278c2ecf20Sopenharmony_ci *strength = state->snr / 8960; 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_ci return 0; 5308c2ecf20Sopenharmony_ci} 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_cistatic int or51132_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings* fe_tune_settings) 5338c2ecf20Sopenharmony_ci{ 5348c2ecf20Sopenharmony_ci fe_tune_settings->min_delay_ms = 500; 5358c2ecf20Sopenharmony_ci fe_tune_settings->step_size = 0; 5368c2ecf20Sopenharmony_ci fe_tune_settings->max_drift = 0; 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_ci return 0; 5398c2ecf20Sopenharmony_ci} 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_cistatic void or51132_release(struct dvb_frontend* fe) 5428c2ecf20Sopenharmony_ci{ 5438c2ecf20Sopenharmony_ci struct or51132_state* state = fe->demodulator_priv; 5448c2ecf20Sopenharmony_ci kfree(state); 5458c2ecf20Sopenharmony_ci} 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_cistatic const struct dvb_frontend_ops or51132_ops; 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_cistruct dvb_frontend* or51132_attach(const struct or51132_config* config, 5508c2ecf20Sopenharmony_ci struct i2c_adapter* i2c) 5518c2ecf20Sopenharmony_ci{ 5528c2ecf20Sopenharmony_ci struct or51132_state* state = NULL; 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ci /* Allocate memory for the internal state */ 5558c2ecf20Sopenharmony_ci state = kzalloc(sizeof(struct or51132_state), GFP_KERNEL); 5568c2ecf20Sopenharmony_ci if (state == NULL) 5578c2ecf20Sopenharmony_ci return NULL; 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_ci /* Setup the state */ 5608c2ecf20Sopenharmony_ci state->config = config; 5618c2ecf20Sopenharmony_ci state->i2c = i2c; 5628c2ecf20Sopenharmony_ci state->current_frequency = -1; 5638c2ecf20Sopenharmony_ci state->current_modulation = -1; 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_ci /* Create dvb_frontend */ 5668c2ecf20Sopenharmony_ci memcpy(&state->frontend.ops, &or51132_ops, sizeof(struct dvb_frontend_ops)); 5678c2ecf20Sopenharmony_ci state->frontend.demodulator_priv = state; 5688c2ecf20Sopenharmony_ci return &state->frontend; 5698c2ecf20Sopenharmony_ci} 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_cistatic const struct dvb_frontend_ops or51132_ops = { 5728c2ecf20Sopenharmony_ci .delsys = { SYS_ATSC, SYS_DVBC_ANNEX_B }, 5738c2ecf20Sopenharmony_ci .info = { 5748c2ecf20Sopenharmony_ci .name = "Oren OR51132 VSB/QAM Frontend", 5758c2ecf20Sopenharmony_ci .frequency_min_hz = 44 * MHz, 5768c2ecf20Sopenharmony_ci .frequency_max_hz = 958 * MHz, 5778c2ecf20Sopenharmony_ci .frequency_stepsize_hz = 166666, 5788c2ecf20Sopenharmony_ci .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | 5798c2ecf20Sopenharmony_ci FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | 5808c2ecf20Sopenharmony_ci FE_CAN_QAM_64 | FE_CAN_QAM_256 | FE_CAN_QAM_AUTO | 5818c2ecf20Sopenharmony_ci FE_CAN_8VSB 5828c2ecf20Sopenharmony_ci }, 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_ci .release = or51132_release, 5858c2ecf20Sopenharmony_ci 5868c2ecf20Sopenharmony_ci .init = or51132_init, 5878c2ecf20Sopenharmony_ci .sleep = or51132_sleep, 5888c2ecf20Sopenharmony_ci 5898c2ecf20Sopenharmony_ci .set_frontend = or51132_set_parameters, 5908c2ecf20Sopenharmony_ci .get_frontend = or51132_get_parameters, 5918c2ecf20Sopenharmony_ci .get_tune_settings = or51132_get_tune_settings, 5928c2ecf20Sopenharmony_ci 5938c2ecf20Sopenharmony_ci .read_status = or51132_read_status, 5948c2ecf20Sopenharmony_ci .read_ber = or51132_read_ber, 5958c2ecf20Sopenharmony_ci .read_signal_strength = or51132_read_signal_strength, 5968c2ecf20Sopenharmony_ci .read_snr = or51132_read_snr, 5978c2ecf20Sopenharmony_ci .read_ucblocks = or51132_read_ucblocks, 5988c2ecf20Sopenharmony_ci}; 5998c2ecf20Sopenharmony_ci 6008c2ecf20Sopenharmony_cimodule_param(debug, int, 0644); 6018c2ecf20Sopenharmony_ciMODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("OR51132 ATSC [pcHDTV HD-3000] (8VSB & ITU J83 AnnexB FEC QAM64/256) Demodulator Driver"); 6048c2ecf20Sopenharmony_ciMODULE_AUTHOR("Kirk Lapray"); 6058c2ecf20Sopenharmony_ciMODULE_AUTHOR("Trent Piepho"); 6068c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 6078c2ecf20Sopenharmony_ci 6088c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(or51132_attach); 609