1*50acfb2bSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 23a509bb1SMauro Carvalho Chehab /* 33a509bb1SMauro Carvalho Chehab * Sharp VA3A5JZ921 One Seg Broadcast Module driver 43a509bb1SMauro Carvalho Chehab * This device is labeled as just S. 921 at the top of the frontend can 53a509bb1SMauro Carvalho Chehab * 637e59f87SMauro Carvalho Chehab * Copyright (C) 2009-2010 Mauro Carvalho Chehab 73a509bb1SMauro Carvalho Chehab * Copyright (C) 2009-2010 Douglas Landgraf <dougsland@redhat.com> 83a509bb1SMauro Carvalho Chehab * 93a509bb1SMauro Carvalho Chehab * Developed for Leadership SBTVD 1seg device sold in Brazil 103a509bb1SMauro Carvalho Chehab * 113a509bb1SMauro Carvalho Chehab * Frontend module based on cx24123 driver, getting some info from 123a509bb1SMauro Carvalho Chehab * the old s921 driver. 133a509bb1SMauro Carvalho Chehab * 143a509bb1SMauro Carvalho Chehab * FIXME: Need to port to DVB v5.2 API 153a509bb1SMauro Carvalho Chehab */ 163a509bb1SMauro Carvalho Chehab 173a509bb1SMauro Carvalho Chehab #include <linux/kernel.h> 183a509bb1SMauro Carvalho Chehab #include <asm/div64.h> 193a509bb1SMauro Carvalho Chehab 20fada1935SMauro Carvalho Chehab #include <media/dvb_frontend.h> 213a509bb1SMauro Carvalho Chehab #include "s921.h" 223a509bb1SMauro Carvalho Chehab 233a509bb1SMauro Carvalho Chehab static int debug = 1; 243a509bb1SMauro Carvalho Chehab module_param(debug, int, 0644); 253a509bb1SMauro Carvalho Chehab MODULE_PARM_DESC(debug, "Activates frontend debugging (default:0)"); 263a509bb1SMauro Carvalho Chehab 273a509bb1SMauro Carvalho Chehab #define rc(args...) do { \ 283a509bb1SMauro Carvalho Chehab printk(KERN_ERR "s921: " args); \ 293a509bb1SMauro Carvalho Chehab } while (0) 303a509bb1SMauro Carvalho Chehab 313a509bb1SMauro Carvalho Chehab #define dprintk(args...) \ 323a509bb1SMauro Carvalho Chehab do { \ 333a509bb1SMauro Carvalho Chehab if (debug) { \ 343a509bb1SMauro Carvalho Chehab printk(KERN_DEBUG "s921: %s: ", __func__); \ 353a509bb1SMauro Carvalho Chehab printk(args); \ 363a509bb1SMauro Carvalho Chehab } \ 373a509bb1SMauro Carvalho Chehab } while (0) 383a509bb1SMauro Carvalho Chehab 393a509bb1SMauro Carvalho Chehab struct s921_state { 403a509bb1SMauro Carvalho Chehab struct i2c_adapter *i2c; 413a509bb1SMauro Carvalho Chehab const struct s921_config *config; 423a509bb1SMauro Carvalho Chehab 433a509bb1SMauro Carvalho Chehab struct dvb_frontend frontend; 443a509bb1SMauro Carvalho Chehab 453a509bb1SMauro Carvalho Chehab /* The Demod can't easily provide these, we cache them */ 463a509bb1SMauro Carvalho Chehab u32 currentfreq; 473a509bb1SMauro Carvalho Chehab }; 483a509bb1SMauro Carvalho Chehab 493a509bb1SMauro Carvalho Chehab /* 503a509bb1SMauro Carvalho Chehab * Various tuner defaults need to be established for a given frequency kHz. 513a509bb1SMauro Carvalho Chehab * fixme: The bounds on the bands do not match the doc in real life. 523a509bb1SMauro Carvalho Chehab * fixme: Some of them have been moved, other might need adjustment. 533a509bb1SMauro Carvalho Chehab */ 543a509bb1SMauro Carvalho Chehab static struct s921_bandselect_val { 553a509bb1SMauro Carvalho Chehab u32 freq_low; 563a509bb1SMauro Carvalho Chehab u8 band_reg; 573a509bb1SMauro Carvalho Chehab } s921_bandselect[] = { 583a509bb1SMauro Carvalho Chehab { 0, 0x7b }, 593a509bb1SMauro Carvalho Chehab { 485140000, 0x5b }, 603a509bb1SMauro Carvalho Chehab { 515140000, 0x3b }, 613a509bb1SMauro Carvalho Chehab { 545140000, 0x1b }, 623a509bb1SMauro Carvalho Chehab { 599140000, 0xfb }, 633a509bb1SMauro Carvalho Chehab { 623140000, 0xdb }, 643a509bb1SMauro Carvalho Chehab { 659140000, 0xbb }, 653a509bb1SMauro Carvalho Chehab { 713140000, 0x9b }, 663a509bb1SMauro Carvalho Chehab }; 673a509bb1SMauro Carvalho Chehab 683a509bb1SMauro Carvalho Chehab struct regdata { 693a509bb1SMauro Carvalho Chehab u8 reg; 703a509bb1SMauro Carvalho Chehab u8 data; 713a509bb1SMauro Carvalho Chehab }; 723a509bb1SMauro Carvalho Chehab 733a509bb1SMauro Carvalho Chehab static struct regdata s921_init[] = { 743a509bb1SMauro Carvalho Chehab { 0x01, 0x80 }, /* Probably, a reset sequence */ 753a509bb1SMauro Carvalho Chehab { 0x01, 0x40 }, 763a509bb1SMauro Carvalho Chehab { 0x01, 0x80 }, 773a509bb1SMauro Carvalho Chehab { 0x01, 0x40 }, 783a509bb1SMauro Carvalho Chehab 793a509bb1SMauro Carvalho Chehab { 0x02, 0x00 }, 803a509bb1SMauro Carvalho Chehab { 0x03, 0x40 }, 813a509bb1SMauro Carvalho Chehab { 0x04, 0x01 }, 823a509bb1SMauro Carvalho Chehab { 0x05, 0x00 }, 833a509bb1SMauro Carvalho Chehab { 0x06, 0x00 }, 843a509bb1SMauro Carvalho Chehab { 0x07, 0x00 }, 853a509bb1SMauro Carvalho Chehab { 0x08, 0x00 }, 863a509bb1SMauro Carvalho Chehab { 0x09, 0x00 }, 873a509bb1SMauro Carvalho Chehab { 0x0a, 0x00 }, 883a509bb1SMauro Carvalho Chehab { 0x0b, 0x5a }, 893a509bb1SMauro Carvalho Chehab { 0x0c, 0x00 }, 903a509bb1SMauro Carvalho Chehab { 0x0d, 0x00 }, 913a509bb1SMauro Carvalho Chehab { 0x0f, 0x00 }, 923a509bb1SMauro Carvalho Chehab { 0x13, 0x1b }, 933a509bb1SMauro Carvalho Chehab { 0x14, 0x80 }, 943a509bb1SMauro Carvalho Chehab { 0x15, 0x40 }, 953a509bb1SMauro Carvalho Chehab { 0x17, 0x70 }, 963a509bb1SMauro Carvalho Chehab { 0x18, 0x01 }, 973a509bb1SMauro Carvalho Chehab { 0x19, 0x12 }, 983a509bb1SMauro Carvalho Chehab { 0x1a, 0x01 }, 993a509bb1SMauro Carvalho Chehab { 0x1b, 0x12 }, 1003a509bb1SMauro Carvalho Chehab { 0x1c, 0xa0 }, 1013a509bb1SMauro Carvalho Chehab { 0x1d, 0x00 }, 1023a509bb1SMauro Carvalho Chehab { 0x1e, 0x0a }, 1033a509bb1SMauro Carvalho Chehab { 0x1f, 0x08 }, 1043a509bb1SMauro Carvalho Chehab { 0x20, 0x40 }, 1053a509bb1SMauro Carvalho Chehab { 0x21, 0xff }, 1063a509bb1SMauro Carvalho Chehab { 0x22, 0x4c }, 1073a509bb1SMauro Carvalho Chehab { 0x23, 0x4e }, 1083a509bb1SMauro Carvalho Chehab { 0x24, 0x4c }, 1093a509bb1SMauro Carvalho Chehab { 0x25, 0x00 }, 1103a509bb1SMauro Carvalho Chehab { 0x26, 0x00 }, 1113a509bb1SMauro Carvalho Chehab { 0x27, 0xf4 }, 1123a509bb1SMauro Carvalho Chehab { 0x28, 0x60 }, 1133a509bb1SMauro Carvalho Chehab { 0x29, 0x88 }, 1143a509bb1SMauro Carvalho Chehab { 0x2a, 0x40 }, 1153a509bb1SMauro Carvalho Chehab { 0x2b, 0x40 }, 1163a509bb1SMauro Carvalho Chehab { 0x2c, 0xff }, 1173a509bb1SMauro Carvalho Chehab { 0x2d, 0x00 }, 1183a509bb1SMauro Carvalho Chehab { 0x2e, 0xff }, 1193a509bb1SMauro Carvalho Chehab { 0x2f, 0x00 }, 1203a509bb1SMauro Carvalho Chehab { 0x30, 0x20 }, 1213a509bb1SMauro Carvalho Chehab { 0x31, 0x06 }, 1223a509bb1SMauro Carvalho Chehab { 0x32, 0x0c }, 1233a509bb1SMauro Carvalho Chehab { 0x34, 0x0f }, 1243a509bb1SMauro Carvalho Chehab { 0x37, 0xfe }, 1253a509bb1SMauro Carvalho Chehab { 0x38, 0x00 }, 1263a509bb1SMauro Carvalho Chehab { 0x39, 0x63 }, 1273a509bb1SMauro Carvalho Chehab { 0x3a, 0x10 }, 1283a509bb1SMauro Carvalho Chehab { 0x3b, 0x10 }, 1293a509bb1SMauro Carvalho Chehab { 0x47, 0x00 }, 1303a509bb1SMauro Carvalho Chehab { 0x49, 0xe5 }, 1313a509bb1SMauro Carvalho Chehab { 0x4b, 0x00 }, 1323a509bb1SMauro Carvalho Chehab { 0x50, 0xc0 }, 1333a509bb1SMauro Carvalho Chehab { 0x52, 0x20 }, 1343a509bb1SMauro Carvalho Chehab { 0x54, 0x5a }, 1353a509bb1SMauro Carvalho Chehab { 0x55, 0x5b }, 1363a509bb1SMauro Carvalho Chehab { 0x56, 0x40 }, 1373a509bb1SMauro Carvalho Chehab { 0x57, 0x70 }, 1383a509bb1SMauro Carvalho Chehab { 0x5c, 0x50 }, 1393a509bb1SMauro Carvalho Chehab { 0x5d, 0x00 }, 1403a509bb1SMauro Carvalho Chehab { 0x62, 0x17 }, 1413a509bb1SMauro Carvalho Chehab { 0x63, 0x2f }, 1423a509bb1SMauro Carvalho Chehab { 0x64, 0x6f }, 1433a509bb1SMauro Carvalho Chehab { 0x68, 0x00 }, 1443a509bb1SMauro Carvalho Chehab { 0x69, 0x89 }, 1453a509bb1SMauro Carvalho Chehab { 0x6a, 0x00 }, 1463a509bb1SMauro Carvalho Chehab { 0x6b, 0x00 }, 1473a509bb1SMauro Carvalho Chehab { 0x6c, 0x00 }, 1483a509bb1SMauro Carvalho Chehab { 0x6d, 0x00 }, 1493a509bb1SMauro Carvalho Chehab { 0x6e, 0x00 }, 1503a509bb1SMauro Carvalho Chehab { 0x70, 0x10 }, 1513a509bb1SMauro Carvalho Chehab { 0x71, 0x00 }, 1523a509bb1SMauro Carvalho Chehab { 0x75, 0x00 }, 1533a509bb1SMauro Carvalho Chehab { 0x76, 0x30 }, 1543a509bb1SMauro Carvalho Chehab { 0x77, 0x01 }, 1553a509bb1SMauro Carvalho Chehab { 0xaf, 0x00 }, 1563a509bb1SMauro Carvalho Chehab { 0xb0, 0xa0 }, 1573a509bb1SMauro Carvalho Chehab { 0xb2, 0x3d }, 1583a509bb1SMauro Carvalho Chehab { 0xb3, 0x25 }, 1593a509bb1SMauro Carvalho Chehab { 0xb4, 0x8b }, 1603a509bb1SMauro Carvalho Chehab { 0xb5, 0x4b }, 1613a509bb1SMauro Carvalho Chehab { 0xb6, 0x3f }, 1623a509bb1SMauro Carvalho Chehab { 0xb7, 0xff }, 1633a509bb1SMauro Carvalho Chehab { 0xb8, 0xff }, 1643a509bb1SMauro Carvalho Chehab { 0xb9, 0xfc }, 1653a509bb1SMauro Carvalho Chehab { 0xba, 0x00 }, 1663a509bb1SMauro Carvalho Chehab { 0xbb, 0x00 }, 1673a509bb1SMauro Carvalho Chehab { 0xbc, 0x00 }, 1683a509bb1SMauro Carvalho Chehab { 0xd0, 0x30 }, 1693a509bb1SMauro Carvalho Chehab { 0xe4, 0x84 }, 1703a509bb1SMauro Carvalho Chehab { 0xf0, 0x48 }, 1713a509bb1SMauro Carvalho Chehab { 0xf1, 0x19 }, 1723a509bb1SMauro Carvalho Chehab { 0xf2, 0x5a }, 1733a509bb1SMauro Carvalho Chehab { 0xf3, 0x8e }, 1743a509bb1SMauro Carvalho Chehab { 0xf4, 0x2d }, 1753a509bb1SMauro Carvalho Chehab { 0xf5, 0x07 }, 1763a509bb1SMauro Carvalho Chehab { 0xf6, 0x5a }, 1773a509bb1SMauro Carvalho Chehab { 0xf7, 0xba }, 1783a509bb1SMauro Carvalho Chehab { 0xf8, 0xd7 }, 1793a509bb1SMauro Carvalho Chehab }; 1803a509bb1SMauro Carvalho Chehab 1813a509bb1SMauro Carvalho Chehab static struct regdata s921_prefreq[] = { 1823a509bb1SMauro Carvalho Chehab { 0x47, 0x60 }, 1833a509bb1SMauro Carvalho Chehab { 0x68, 0x00 }, 1843a509bb1SMauro Carvalho Chehab { 0x69, 0x89 }, 1853a509bb1SMauro Carvalho Chehab { 0xf0, 0x48 }, 1863a509bb1SMauro Carvalho Chehab { 0xf1, 0x19 }, 1873a509bb1SMauro Carvalho Chehab }; 1883a509bb1SMauro Carvalho Chehab 1893a509bb1SMauro Carvalho Chehab static struct regdata s921_postfreq[] = { 1903a509bb1SMauro Carvalho Chehab { 0xf5, 0xae }, 1913a509bb1SMauro Carvalho Chehab { 0xf6, 0xb7 }, 1923a509bb1SMauro Carvalho Chehab { 0xf7, 0xba }, 1933a509bb1SMauro Carvalho Chehab { 0xf8, 0xd7 }, 1943a509bb1SMauro Carvalho Chehab { 0x68, 0x0a }, 1953a509bb1SMauro Carvalho Chehab { 0x69, 0x09 }, 1963a509bb1SMauro Carvalho Chehab }; 1973a509bb1SMauro Carvalho Chehab 1983a509bb1SMauro Carvalho Chehab static int s921_i2c_writereg(struct s921_state *state, 1993a509bb1SMauro Carvalho Chehab u8 i2c_addr, int reg, int data) 2003a509bb1SMauro Carvalho Chehab { 2013a509bb1SMauro Carvalho Chehab u8 buf[] = { reg, data }; 2023a509bb1SMauro Carvalho Chehab struct i2c_msg msg = { 2033a509bb1SMauro Carvalho Chehab .addr = i2c_addr, .flags = 0, .buf = buf, .len = 2 2043a509bb1SMauro Carvalho Chehab }; 2053a509bb1SMauro Carvalho Chehab int rc; 2063a509bb1SMauro Carvalho Chehab 2073a509bb1SMauro Carvalho Chehab rc = i2c_transfer(state->i2c, &msg, 1); 2083a509bb1SMauro Carvalho Chehab if (rc != 1) { 2094bd69e7bSMauro Carvalho Chehab printk("%s: writereg rcor(rc == %i, reg == 0x%02x, data == 0x%02x)\n", 2104bd69e7bSMauro Carvalho Chehab __func__, rc, reg, data); 2113a509bb1SMauro Carvalho Chehab return rc; 2123a509bb1SMauro Carvalho Chehab } 2133a509bb1SMauro Carvalho Chehab 2143a509bb1SMauro Carvalho Chehab return 0; 2153a509bb1SMauro Carvalho Chehab } 2163a509bb1SMauro Carvalho Chehab 2173a509bb1SMauro Carvalho Chehab static int s921_i2c_writeregdata(struct s921_state *state, u8 i2c_addr, 2183a509bb1SMauro Carvalho Chehab struct regdata *rd, int size) 2193a509bb1SMauro Carvalho Chehab { 2203a509bb1SMauro Carvalho Chehab int i, rc; 2213a509bb1SMauro Carvalho Chehab 2223a509bb1SMauro Carvalho Chehab for (i = 0; i < size; i++) { 2233a509bb1SMauro Carvalho Chehab rc = s921_i2c_writereg(state, i2c_addr, rd[i].reg, rd[i].data); 2243a509bb1SMauro Carvalho Chehab if (rc < 0) 2253a509bb1SMauro Carvalho Chehab return rc; 2263a509bb1SMauro Carvalho Chehab } 2273a509bb1SMauro Carvalho Chehab return 0; 2283a509bb1SMauro Carvalho Chehab } 2293a509bb1SMauro Carvalho Chehab 2303a509bb1SMauro Carvalho Chehab static int s921_i2c_readreg(struct s921_state *state, u8 i2c_addr, u8 reg) 2313a509bb1SMauro Carvalho Chehab { 2323a509bb1SMauro Carvalho Chehab u8 val; 2333a509bb1SMauro Carvalho Chehab int rc; 2343a509bb1SMauro Carvalho Chehab struct i2c_msg msg[] = { 2353a509bb1SMauro Carvalho Chehab { .addr = i2c_addr, .flags = 0, .buf = ®, .len = 1 }, 2363a509bb1SMauro Carvalho Chehab { .addr = i2c_addr, .flags = I2C_M_RD, .buf = &val, .len = 1 } 2373a509bb1SMauro Carvalho Chehab }; 2383a509bb1SMauro Carvalho Chehab 2393a509bb1SMauro Carvalho Chehab rc = i2c_transfer(state->i2c, msg, 2); 2403a509bb1SMauro Carvalho Chehab 2413a509bb1SMauro Carvalho Chehab if (rc != 2) { 2423a509bb1SMauro Carvalho Chehab rc("%s: reg=0x%x (rcor=%d)\n", __func__, reg, rc); 2433a509bb1SMauro Carvalho Chehab return rc; 2443a509bb1SMauro Carvalho Chehab } 2453a509bb1SMauro Carvalho Chehab 2463a509bb1SMauro Carvalho Chehab return val; 2473a509bb1SMauro Carvalho Chehab } 2483a509bb1SMauro Carvalho Chehab 2493a509bb1SMauro Carvalho Chehab #define s921_readreg(state, reg) \ 2503a509bb1SMauro Carvalho Chehab s921_i2c_readreg(state, state->config->demod_address, reg) 2513a509bb1SMauro Carvalho Chehab #define s921_writereg(state, reg, val) \ 2523a509bb1SMauro Carvalho Chehab s921_i2c_writereg(state, state->config->demod_address, reg, val) 2533a509bb1SMauro Carvalho Chehab #define s921_writeregdata(state, regdata) \ 2543a509bb1SMauro Carvalho Chehab s921_i2c_writeregdata(state, state->config->demod_address, \ 2553a509bb1SMauro Carvalho Chehab regdata, ARRAY_SIZE(regdata)) 2563a509bb1SMauro Carvalho Chehab 25716e3d131SMauro Carvalho Chehab static int s921_pll_tune(struct dvb_frontend *fe) 2583a509bb1SMauro Carvalho Chehab { 25916e3d131SMauro Carvalho Chehab struct dtv_frontend_properties *p = &fe->dtv_property_cache; 2603a509bb1SMauro Carvalho Chehab struct s921_state *state = fe->demodulator_priv; 2613a509bb1SMauro Carvalho Chehab int band, rc, i; 2623a509bb1SMauro Carvalho Chehab unsigned long f_offset; 2633a509bb1SMauro Carvalho Chehab u8 f_switch; 2643a509bb1SMauro Carvalho Chehab u64 offset; 2653a509bb1SMauro Carvalho Chehab 2663a509bb1SMauro Carvalho Chehab dprintk("frequency=%i\n", p->frequency); 2673a509bb1SMauro Carvalho Chehab 2683a509bb1SMauro Carvalho Chehab for (band = 0; band < ARRAY_SIZE(s921_bandselect); band++) 2693a509bb1SMauro Carvalho Chehab if (p->frequency < s921_bandselect[band].freq_low) 2703a509bb1SMauro Carvalho Chehab break; 2713a509bb1SMauro Carvalho Chehab band--; 2723a509bb1SMauro Carvalho Chehab 2733a509bb1SMauro Carvalho Chehab if (band < 0) { 2743a509bb1SMauro Carvalho Chehab rc("%s: frequency out of range\n", __func__); 2753a509bb1SMauro Carvalho Chehab return -EINVAL; 2763a509bb1SMauro Carvalho Chehab } 2773a509bb1SMauro Carvalho Chehab 2783a509bb1SMauro Carvalho Chehab f_switch = s921_bandselect[band].band_reg; 2793a509bb1SMauro Carvalho Chehab 2803a509bb1SMauro Carvalho Chehab offset = ((u64)p->frequency) * 258; 2813a509bb1SMauro Carvalho Chehab do_div(offset, 6000000); 2823a509bb1SMauro Carvalho Chehab f_offset = ((unsigned long)offset) + 2321; 2833a509bb1SMauro Carvalho Chehab 2843a509bb1SMauro Carvalho Chehab rc = s921_writeregdata(state, s921_prefreq); 2853a509bb1SMauro Carvalho Chehab if (rc < 0) 2863a509bb1SMauro Carvalho Chehab return rc; 2873a509bb1SMauro Carvalho Chehab 2883a509bb1SMauro Carvalho Chehab rc = s921_writereg(state, 0xf2, (f_offset >> 8) & 0xff); 2893a509bb1SMauro Carvalho Chehab if (rc < 0) 2903a509bb1SMauro Carvalho Chehab return rc; 2913a509bb1SMauro Carvalho Chehab 2923a509bb1SMauro Carvalho Chehab rc = s921_writereg(state, 0xf3, f_offset & 0xff); 2933a509bb1SMauro Carvalho Chehab if (rc < 0) 2943a509bb1SMauro Carvalho Chehab return rc; 2953a509bb1SMauro Carvalho Chehab 2963a509bb1SMauro Carvalho Chehab rc = s921_writereg(state, 0xf4, f_switch); 2973a509bb1SMauro Carvalho Chehab if (rc < 0) 2983a509bb1SMauro Carvalho Chehab return rc; 2993a509bb1SMauro Carvalho Chehab 3003a509bb1SMauro Carvalho Chehab rc = s921_writeregdata(state, s921_postfreq); 3013a509bb1SMauro Carvalho Chehab if (rc < 0) 3023a509bb1SMauro Carvalho Chehab return rc; 3033a509bb1SMauro Carvalho Chehab 3043a509bb1SMauro Carvalho Chehab for (i = 0 ; i < 6; i++) { 3053a509bb1SMauro Carvalho Chehab rc = s921_readreg(state, 0x80); 3063a509bb1SMauro Carvalho Chehab dprintk("status 0x80: %02x\n", rc); 3073a509bb1SMauro Carvalho Chehab } 3083a509bb1SMauro Carvalho Chehab rc = s921_writereg(state, 0x01, 0x40); 3093a509bb1SMauro Carvalho Chehab if (rc < 0) 3103a509bb1SMauro Carvalho Chehab return rc; 3113a509bb1SMauro Carvalho Chehab 3123a509bb1SMauro Carvalho Chehab rc = s921_readreg(state, 0x01); 3133a509bb1SMauro Carvalho Chehab dprintk("status 0x01: %02x\n", rc); 3143a509bb1SMauro Carvalho Chehab 3153a509bb1SMauro Carvalho Chehab rc = s921_readreg(state, 0x80); 3163a509bb1SMauro Carvalho Chehab dprintk("status 0x80: %02x\n", rc); 3173a509bb1SMauro Carvalho Chehab 3183a509bb1SMauro Carvalho Chehab rc = s921_readreg(state, 0x80); 3193a509bb1SMauro Carvalho Chehab dprintk("status 0x80: %02x\n", rc); 3203a509bb1SMauro Carvalho Chehab 3213a509bb1SMauro Carvalho Chehab rc = s921_readreg(state, 0x32); 3223a509bb1SMauro Carvalho Chehab dprintk("status 0x32: %02x\n", rc); 3233a509bb1SMauro Carvalho Chehab 3243a509bb1SMauro Carvalho Chehab dprintk("pll tune band=%d, pll=%d\n", f_switch, (int)f_offset); 3253a509bb1SMauro Carvalho Chehab 3263a509bb1SMauro Carvalho Chehab return 0; 3273a509bb1SMauro Carvalho Chehab } 3283a509bb1SMauro Carvalho Chehab 3293a509bb1SMauro Carvalho Chehab static int s921_initfe(struct dvb_frontend *fe) 3303a509bb1SMauro Carvalho Chehab { 3313a509bb1SMauro Carvalho Chehab struct s921_state *state = fe->demodulator_priv; 3323a509bb1SMauro Carvalho Chehab int rc; 3333a509bb1SMauro Carvalho Chehab 3343a509bb1SMauro Carvalho Chehab dprintk("\n"); 3353a509bb1SMauro Carvalho Chehab 3363a509bb1SMauro Carvalho Chehab rc = s921_writeregdata(state, s921_init); 3373a509bb1SMauro Carvalho Chehab if (rc < 0) 3383a509bb1SMauro Carvalho Chehab return rc; 3393a509bb1SMauro Carvalho Chehab 3403a509bb1SMauro Carvalho Chehab return 0; 3413a509bb1SMauro Carvalho Chehab } 3423a509bb1SMauro Carvalho Chehab 3430df289a2SMauro Carvalho Chehab static int s921_read_status(struct dvb_frontend *fe, enum fe_status *status) 3443a509bb1SMauro Carvalho Chehab { 3453a509bb1SMauro Carvalho Chehab struct s921_state *state = fe->demodulator_priv; 3463a509bb1SMauro Carvalho Chehab int regstatus, rc; 3473a509bb1SMauro Carvalho Chehab 3483a509bb1SMauro Carvalho Chehab *status = 0; 3493a509bb1SMauro Carvalho Chehab 3503a509bb1SMauro Carvalho Chehab rc = s921_readreg(state, 0x81); 3513a509bb1SMauro Carvalho Chehab if (rc < 0) 3523a509bb1SMauro Carvalho Chehab return rc; 3533a509bb1SMauro Carvalho Chehab 3543a509bb1SMauro Carvalho Chehab regstatus = rc << 8; 3553a509bb1SMauro Carvalho Chehab 3563a509bb1SMauro Carvalho Chehab rc = s921_readreg(state, 0x82); 3573a509bb1SMauro Carvalho Chehab if (rc < 0) 3583a509bb1SMauro Carvalho Chehab return rc; 3593a509bb1SMauro Carvalho Chehab 3603a509bb1SMauro Carvalho Chehab regstatus |= rc; 3613a509bb1SMauro Carvalho Chehab 3623a509bb1SMauro Carvalho Chehab dprintk("status = %04x\n", regstatus); 3633a509bb1SMauro Carvalho Chehab 3643a509bb1SMauro Carvalho Chehab /* Full Sync - We don't know what each bit means on regs 0x81/0x82 */ 3653a509bb1SMauro Carvalho Chehab if ((regstatus & 0xff) == 0x40) { 3663a509bb1SMauro Carvalho Chehab *status = FE_HAS_SIGNAL | 3673a509bb1SMauro Carvalho Chehab FE_HAS_CARRIER | 3683a509bb1SMauro Carvalho Chehab FE_HAS_VITERBI | 3693a509bb1SMauro Carvalho Chehab FE_HAS_SYNC | 3703a509bb1SMauro Carvalho Chehab FE_HAS_LOCK; 3713a509bb1SMauro Carvalho Chehab } else if (regstatus & 0x40) { 3723a509bb1SMauro Carvalho Chehab /* This is close to Full Sync, but not enough to get useful info */ 3733a509bb1SMauro Carvalho Chehab *status = FE_HAS_SIGNAL | 3743a509bb1SMauro Carvalho Chehab FE_HAS_CARRIER | 3753a509bb1SMauro Carvalho Chehab FE_HAS_VITERBI | 3763a509bb1SMauro Carvalho Chehab FE_HAS_SYNC; 3773a509bb1SMauro Carvalho Chehab } 3783a509bb1SMauro Carvalho Chehab 3793a509bb1SMauro Carvalho Chehab return 0; 3803a509bb1SMauro Carvalho Chehab } 3813a509bb1SMauro Carvalho Chehab 3823a509bb1SMauro Carvalho Chehab static int s921_read_signal_strength(struct dvb_frontend *fe, u16 *strength) 3833a509bb1SMauro Carvalho Chehab { 3840df289a2SMauro Carvalho Chehab enum fe_status status; 3853a509bb1SMauro Carvalho Chehab struct s921_state *state = fe->demodulator_priv; 3863a509bb1SMauro Carvalho Chehab int rc; 3873a509bb1SMauro Carvalho Chehab 3883a509bb1SMauro Carvalho Chehab /* FIXME: Use the proper register for it... 0x80? */ 3893a509bb1SMauro Carvalho Chehab rc = s921_read_status(fe, &status); 3903a509bb1SMauro Carvalho Chehab if (rc < 0) 3913a509bb1SMauro Carvalho Chehab return rc; 3923a509bb1SMauro Carvalho Chehab 3933a509bb1SMauro Carvalho Chehab *strength = (status & FE_HAS_LOCK) ? 0xffff : 0; 3943a509bb1SMauro Carvalho Chehab 3953a509bb1SMauro Carvalho Chehab dprintk("strength = 0x%04x\n", *strength); 3963a509bb1SMauro Carvalho Chehab 3973a509bb1SMauro Carvalho Chehab rc = s921_readreg(state, 0x01); 3983a509bb1SMauro Carvalho Chehab dprintk("status 0x01: %02x\n", rc); 3993a509bb1SMauro Carvalho Chehab 4003a509bb1SMauro Carvalho Chehab rc = s921_readreg(state, 0x80); 4013a509bb1SMauro Carvalho Chehab dprintk("status 0x80: %02x\n", rc); 4023a509bb1SMauro Carvalho Chehab 4033a509bb1SMauro Carvalho Chehab rc = s921_readreg(state, 0x32); 4043a509bb1SMauro Carvalho Chehab dprintk("status 0x32: %02x\n", rc); 4053a509bb1SMauro Carvalho Chehab 4063a509bb1SMauro Carvalho Chehab return 0; 4073a509bb1SMauro Carvalho Chehab } 4083a509bb1SMauro Carvalho Chehab 40916e3d131SMauro Carvalho Chehab static int s921_set_frontend(struct dvb_frontend *fe) 4103a509bb1SMauro Carvalho Chehab { 41116e3d131SMauro Carvalho Chehab struct dtv_frontend_properties *p = &fe->dtv_property_cache; 4123a509bb1SMauro Carvalho Chehab struct s921_state *state = fe->demodulator_priv; 4133a509bb1SMauro Carvalho Chehab int rc; 4143a509bb1SMauro Carvalho Chehab 4153a509bb1SMauro Carvalho Chehab dprintk("\n"); 4163a509bb1SMauro Carvalho Chehab 4173a509bb1SMauro Carvalho Chehab /* FIXME: We don't know how to use non-auto mode */ 4183a509bb1SMauro Carvalho Chehab 41916e3d131SMauro Carvalho Chehab rc = s921_pll_tune(fe); 4203a509bb1SMauro Carvalho Chehab if (rc < 0) 4213a509bb1SMauro Carvalho Chehab return rc; 4223a509bb1SMauro Carvalho Chehab 4233a509bb1SMauro Carvalho Chehab state->currentfreq = p->frequency; 4243a509bb1SMauro Carvalho Chehab 4253a509bb1SMauro Carvalho Chehab return 0; 4263a509bb1SMauro Carvalho Chehab } 4273a509bb1SMauro Carvalho Chehab 4287e3e68bcSMauro Carvalho Chehab static int s921_get_frontend(struct dvb_frontend *fe, 4297e3e68bcSMauro Carvalho Chehab struct dtv_frontend_properties *p) 4303a509bb1SMauro Carvalho Chehab { 4313a509bb1SMauro Carvalho Chehab struct s921_state *state = fe->demodulator_priv; 4323a509bb1SMauro Carvalho Chehab 4333a509bb1SMauro Carvalho Chehab /* FIXME: Probably it is possible to get it from regs f1 and f2 */ 4343a509bb1SMauro Carvalho Chehab p->frequency = state->currentfreq; 43563aad05bSMauro Carvalho Chehab p->delivery_system = SYS_ISDBT; 4363a509bb1SMauro Carvalho Chehab 4373a509bb1SMauro Carvalho Chehab return 0; 4383a509bb1SMauro Carvalho Chehab } 4393a509bb1SMauro Carvalho Chehab 4403a509bb1SMauro Carvalho Chehab static int s921_tune(struct dvb_frontend *fe, 4417e072221SMauro Carvalho Chehab bool re_tune, 4423a509bb1SMauro Carvalho Chehab unsigned int mode_flags, 4433a509bb1SMauro Carvalho Chehab unsigned int *delay, 4440df289a2SMauro Carvalho Chehab enum fe_status *status) 4453a509bb1SMauro Carvalho Chehab { 4463a509bb1SMauro Carvalho Chehab int rc = 0; 4473a509bb1SMauro Carvalho Chehab 4483a509bb1SMauro Carvalho Chehab dprintk("\n"); 4493a509bb1SMauro Carvalho Chehab 4507e072221SMauro Carvalho Chehab if (re_tune) 45116e3d131SMauro Carvalho Chehab rc = s921_set_frontend(fe); 4523a509bb1SMauro Carvalho Chehab 4533a509bb1SMauro Carvalho Chehab if (!(mode_flags & FE_TUNE_MODE_ONESHOT)) 4543a509bb1SMauro Carvalho Chehab s921_read_status(fe, status); 4553a509bb1SMauro Carvalho Chehab 4563a509bb1SMauro Carvalho Chehab return rc; 4573a509bb1SMauro Carvalho Chehab } 4583a509bb1SMauro Carvalho Chehab 4598d718e53SLuc Van Oostenryck static enum dvbfe_algo s921_get_algo(struct dvb_frontend *fe) 4603a509bb1SMauro Carvalho Chehab { 46127460adcSMauro Carvalho Chehab return DVBFE_ALGO_HW; 4623a509bb1SMauro Carvalho Chehab } 4633a509bb1SMauro Carvalho Chehab 4643a509bb1SMauro Carvalho Chehab static void s921_release(struct dvb_frontend *fe) 4653a509bb1SMauro Carvalho Chehab { 4663a509bb1SMauro Carvalho Chehab struct s921_state *state = fe->demodulator_priv; 4673a509bb1SMauro Carvalho Chehab 4683a509bb1SMauro Carvalho Chehab dprintk("\n"); 4693a509bb1SMauro Carvalho Chehab kfree(state); 4703a509bb1SMauro Carvalho Chehab } 4713a509bb1SMauro Carvalho Chehab 472bd336e63SMax Kellermann static const struct dvb_frontend_ops s921_ops; 4733a509bb1SMauro Carvalho Chehab 4743a509bb1SMauro Carvalho Chehab struct dvb_frontend *s921_attach(const struct s921_config *config, 4753a509bb1SMauro Carvalho Chehab struct i2c_adapter *i2c) 4763a509bb1SMauro Carvalho Chehab { 4773a509bb1SMauro Carvalho Chehab /* allocate memory for the internal state */ 4783a509bb1SMauro Carvalho Chehab struct s921_state *state = 4793a509bb1SMauro Carvalho Chehab kzalloc(sizeof(struct s921_state), GFP_KERNEL); 4803a509bb1SMauro Carvalho Chehab 4813a509bb1SMauro Carvalho Chehab dprintk("\n"); 4824a0543eeSPeter Senna Tschudin if (!state) { 4833a509bb1SMauro Carvalho Chehab rc("Unable to kzalloc\n"); 4844a0543eeSPeter Senna Tschudin return NULL; 4853a509bb1SMauro Carvalho Chehab } 4863a509bb1SMauro Carvalho Chehab 4873a509bb1SMauro Carvalho Chehab /* setup the state */ 4883a509bb1SMauro Carvalho Chehab state->config = config; 4893a509bb1SMauro Carvalho Chehab state->i2c = i2c; 4903a509bb1SMauro Carvalho Chehab 4913a509bb1SMauro Carvalho Chehab /* create dvb_frontend */ 4923a509bb1SMauro Carvalho Chehab memcpy(&state->frontend.ops, &s921_ops, 4933a509bb1SMauro Carvalho Chehab sizeof(struct dvb_frontend_ops)); 4943a509bb1SMauro Carvalho Chehab state->frontend.demodulator_priv = state; 4953a509bb1SMauro Carvalho Chehab 4963a509bb1SMauro Carvalho Chehab return &state->frontend; 4973a509bb1SMauro Carvalho Chehab } 4983a509bb1SMauro Carvalho Chehab EXPORT_SYMBOL(s921_attach); 4993a509bb1SMauro Carvalho Chehab 500bd336e63SMax Kellermann static const struct dvb_frontend_ops s921_ops = { 50116e3d131SMauro Carvalho Chehab .delsys = { SYS_ISDBT }, 5023a509bb1SMauro Carvalho Chehab /* Use dib8000 values per default */ 5033a509bb1SMauro Carvalho Chehab .info = { 5043a509bb1SMauro Carvalho Chehab .name = "Sharp S921", 505f1b1eabfSMauro Carvalho Chehab .frequency_min_hz = 470 * MHz, 5063a509bb1SMauro Carvalho Chehab /* 5073a509bb1SMauro Carvalho Chehab * Max should be 770MHz instead, according with Sharp docs, 5083a509bb1SMauro Carvalho Chehab * but Leadership doc says it works up to 806 MHz. This is 5093a509bb1SMauro Carvalho Chehab * required to get channel 69, used in Brazil 5103a509bb1SMauro Carvalho Chehab */ 511f1b1eabfSMauro Carvalho Chehab .frequency_max_hz = 806 * MHz, 5123a509bb1SMauro Carvalho Chehab .caps = FE_CAN_INVERSION_AUTO | 5133a509bb1SMauro Carvalho Chehab FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | 5143a509bb1SMauro Carvalho Chehab FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | 5153a509bb1SMauro Carvalho Chehab FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | 5163a509bb1SMauro Carvalho Chehab FE_CAN_QAM_AUTO | FE_CAN_TRANSMISSION_MODE_AUTO | 5173a509bb1SMauro Carvalho Chehab FE_CAN_GUARD_INTERVAL_AUTO | FE_CAN_RECOVER | 5183a509bb1SMauro Carvalho Chehab FE_CAN_HIERARCHY_AUTO, 5193a509bb1SMauro Carvalho Chehab }, 5203a509bb1SMauro Carvalho Chehab 5213a509bb1SMauro Carvalho Chehab .release = s921_release, 5223a509bb1SMauro Carvalho Chehab 5233a509bb1SMauro Carvalho Chehab .init = s921_initfe, 52416e3d131SMauro Carvalho Chehab .set_frontend = s921_set_frontend, 52516e3d131SMauro Carvalho Chehab .get_frontend = s921_get_frontend, 5263a509bb1SMauro Carvalho Chehab .read_status = s921_read_status, 5273a509bb1SMauro Carvalho Chehab .read_signal_strength = s921_read_signal_strength, 5283a509bb1SMauro Carvalho Chehab .tune = s921_tune, 5293a509bb1SMauro Carvalho Chehab .get_frontend_algo = s921_get_algo, 5303a509bb1SMauro Carvalho Chehab }; 5313a509bb1SMauro Carvalho Chehab 5323a509bb1SMauro Carvalho Chehab MODULE_DESCRIPTION("DVB Frontend module for Sharp S921 hardware"); 53337e59f87SMauro Carvalho Chehab MODULE_AUTHOR("Mauro Carvalho Chehab"); 5343a509bb1SMauro Carvalho Chehab MODULE_AUTHOR("Douglas Landgraf <dougsland@redhat.com>"); 5353a509bb1SMauro Carvalho Chehab MODULE_LICENSE("GPL"); 536