From 1171e03997659abe8d45148f00c58da9290f5b1a Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Fri, 5 Apr 2013 14:35:18 -0300 Subject: [PATCH] --- yaml --- r: 367118 b: refs/heads/master c: a80abc58feda48f868d748bde8c88592c2892b1d h: refs/heads/master v: v3 --- [refs] | 2 +- trunk/Documentation/video4linux/si476x.txt | 187 ++ trunk/drivers/media/dvb-core/dvb-usb-ids.h | 3 +- trunk/drivers/media/dvb-frontends/dib0090.c | 438 ++-- trunk/drivers/media/dvb-frontends/dib7000p.c | 17 +- trunk/drivers/media/dvb-frontends/dib7000p.h | 7 - trunk/drivers/media/dvb-frontends/dib8000.c | 2237 +++++++---------- trunk/drivers/media/dvb-frontends/dib8000.h | 6 +- .../media/dvb-frontends/dibx000_common.h | 3 +- trunk/drivers/media/dvb-frontends/lg2160.h | 4 +- .../drivers/media/i2c/s5c73m3/s5c73m3-core.c | 21 +- trunk/drivers/media/media-device.c | 9 +- trunk/drivers/media/pci/cx88/cx88-mpeg.c | 10 +- trunk/drivers/media/pci/cx88/cx88-video.c | 10 +- trunk/drivers/media/platform/coda.c | 5 - .../media/platform/exynos-gsc/gsc-m2m.c | 5 - .../media/platform/exynos4-is/fimc-capture.c | 13 +- .../media/platform/exynos4-is/fimc-core.c | 17 +- .../media/platform/exynos4-is/fimc-core.h | 6 +- .../media/platform/exynos4-is/fimc-is-i2c.c | 3 + .../platform/exynos4-is/fimc-is-sensor.c | 35 +- .../platform/exynos4-is/fimc-is-sensor.h | 6 - .../media/platform/exynos4-is/fimc-is.c | 15 +- .../media/platform/exynos4-is/fimc-isp.c | 2 +- .../media/platform/exynos4-is/fimc-lite.c | 3 +- .../media/platform/exynos4-is/fimc-lite.h | 2 +- .../media/platform/exynos4-is/fimc-m2m.c | 26 +- .../media/platform/exynos4-is/media-dev.c | 5 +- .../media/platform/exynos4-is/mipi-csis.c | 14 +- .../drivers/media/platform/m2m-deinterlace.c | 5 - .../drivers/media/platform/mem2mem_testdev.c | 12 +- trunk/drivers/media/platform/mx2_emmaprp.c | 5 - trunk/drivers/media/platform/s5p-g2d/g2d.c | 5 - .../media/platform/s5p-jpeg/jpeg-core.c | 5 - .../drivers/media/platform/s5p-mfc/s5p_mfc.c | 13 +- trunk/drivers/media/radio/Kconfig | 17 + trunk/drivers/media/radio/Makefile | 1 + trunk/drivers/media/radio/radio-si476x.c | 1599 ++++++++++++ trunk/drivers/media/rc/imon.c | 26 +- trunk/drivers/media/tuners/Kconfig | 7 + trunk/drivers/media/tuners/Makefile | 1 + trunk/drivers/media/tuners/r820t.c | 1486 +++++++++++ trunk/drivers/media/tuners/r820t.h | 55 + trunk/drivers/media/usb/dvb-usb-v2/anysee.c | 4 +- .../media/usb/dvb-usb/dib0700_devices.c | 465 ++-- trunk/drivers/media/usb/em28xx/em28xx-cards.c | 5 +- .../media/v4l2-core/videobuf-dma-contig.c | 130 +- .../drivers/media/v4l2-core/videobuf2-core.c | 21 +- .../media/v4l2-core/videobuf2-dma-contig.c | 3 + .../media/v4l2-core/videobuf2-dma-sg.c | 3 +- trunk/drivers/mfd/Kconfig | 13 + trunk/drivers/mfd/Makefile | 4 + trunk/drivers/of/base.c | 1 - .../staging/media/go7007/go7007-priv.h | 4 +- .../media/solo6x10/solo6x10-v4l2-enc.c | 44 +- trunk/include/linux/mfd/si476x-core.h | 525 ++++ trunk/include/media/si476x.h | 426 ++++ trunk/include/media/videobuf-dma-contig.h | 10 + 58 files changed, 5980 insertions(+), 2026 deletions(-) create mode 100644 trunk/Documentation/video4linux/si476x.txt create mode 100644 trunk/drivers/media/radio/radio-si476x.c create mode 100644 trunk/drivers/media/tuners/r820t.c create mode 100644 trunk/drivers/media/tuners/r820t.h create mode 100644 trunk/include/linux/mfd/si476x-core.h create mode 100644 trunk/include/media/si476x.h diff --git a/[refs] b/[refs] index c3963c89eab2..b010705a0bca 100644 --- a/[refs] +++ b/[refs] @@ -1,2 +1,2 @@ --- -refs/heads/master: 542b329f8e0d92ca93d033d13a9db16b89830acd +refs/heads/master: a80abc58feda48f868d748bde8c88592c2892b1d diff --git a/trunk/Documentation/video4linux/si476x.txt b/trunk/Documentation/video4linux/si476x.txt new file mode 100644 index 000000000000..d1a08db2cbd9 --- /dev/null +++ b/trunk/Documentation/video4linux/si476x.txt @@ -0,0 +1,187 @@ +SI476x Driver Readme +------------------------------------------------ + Copyright (C) 2013 Andrey Smirnov + +TODO for the driver +------------------------------ + +- According to the SiLabs' datasheet it is possible to update the + firmware of the radio chip in the run-time, thus bringing it to the + most recent version. Unfortunately I couldn't find any mentioning of + the said firmware update for the old chips that I tested the driver + against, so for chips like that the driver only exposes the old + functionality. + + +Parameters exposed over debugfs +------------------------------- +SI476x allow user to get multiple characteristics that can be very +useful for EoL testing/RF performance estimation, parameters that have +very little to do with V4L2 subsystem. Such parameters are exposed via +debugfs and can be accessed via regular file I/O operations. + +The drivers exposes following files: + +* /sys/kernel/debug//acf + This file contains ACF(Automatically Controlled Features) status + information. The contents of the file is binary data of the + following layout: + + Offset | Name | Description + ==================================================================== + 0x00 | blend_int | Flag, set when stereo separation has + | | crossed below the blend threshold + -------------------------------------------------------------------- + 0x01 | hblend_int | Flag, set when HiBlend cutoff + | | frequency is lower than threshold + -------------------------------------------------------------------- + 0x02 | hicut_int | Flag, set when HiCut cutoff + | | frequency is lower than threshold + -------------------------------------------------------------------- + 0x03 | chbw_int | Flag, set when channel filter + | | bandwidth is less than threshold + -------------------------------------------------------------------- + 0x04 | softmute_int | Flag indicating that softmute + | | attenuation has increased above + | | softmute threshold + -------------------------------------------------------------------- + 0x05 | smute | 0 - Audio is not soft muted + | | 1 - Audio is soft muted + -------------------------------------------------------------------- + 0x06 | smattn | Soft mute attenuation level in dB + -------------------------------------------------------------------- + 0x07 | chbw | Channel filter bandwidth in kHz + -------------------------------------------------------------------- + 0x08 | hicut | HiCut cutoff frequency in units of + | | 100Hz + -------------------------------------------------------------------- + 0x09 | hiblend | HiBlend cutoff frequency in units + | | of 100 Hz + -------------------------------------------------------------------- + 0x10 | pilot | 0 - Stereo pilot is not present + | | 1 - Stereo pilot is present + -------------------------------------------------------------------- + 0x11 | stblend | Stereo blend in % + -------------------------------------------------------------------- + + +* /sys/kernel/debug//rds_blckcnt + This file contains statistics about RDS receptions. It's binary data + has the following layout: + + Offset | Name | Description + ==================================================================== + 0x00 | expected | Number of expected RDS blocks + -------------------------------------------------------------------- + 0x02 | received | Number of received RDS blocks + -------------------------------------------------------------------- + 0x04 | uncorrectable | Number of uncorrectable RDS blocks + -------------------------------------------------------------------- + +* /sys/kernel/debug//agc + This file contains information about parameters pertaining to + AGC(Automatic Gain Control) + + The layout is: + Offset | Name | Description + ==================================================================== + 0x00 | mxhi | 0 - FM Mixer PD high threshold is + | | not tripped + | | 1 - FM Mixer PD high threshold is + | | tripped + -------------------------------------------------------------------- + 0x01 | mxlo | ditto for FM Mixer PD low + -------------------------------------------------------------------- + 0x02 | lnahi | ditto for FM LNA PD high + -------------------------------------------------------------------- + 0x03 | lnalo | ditto for FM LNA PD low + -------------------------------------------------------------------- + 0x04 | fmagc1 | FMAGC1 attenuator resistance + | | (see datasheet for more detail) + -------------------------------------------------------------------- + 0x05 | fmagc2 | ditto for FMAGC2 + -------------------------------------------------------------------- + 0x06 | pgagain | PGA gain in dB + -------------------------------------------------------------------- + 0x07 | fmwblang | FM/WB LNA Gain in dB + -------------------------------------------------------------------- + +* /sys/kernel/debug//rsq + This file contains information about parameters pertaining to + RSQ(Received Signal Quality) + + The layout is: + Offset | Name | Description + ==================================================================== + 0x00 | multhint | 0 - multipath value has not crossed + | | the Multipath high threshold + | | 1 - multipath value has crossed + | | the Multipath high threshold + -------------------------------------------------------------------- + 0x01 | multlint | ditto for Multipath low threshold + -------------------------------------------------------------------- + 0x02 | snrhint | 0 - received signal's SNR has not + | | crossed high threshold + | | 1 - received signal's SNR has + | | crossed high threshold + -------------------------------------------------------------------- + 0x03 | snrlint | ditto for low threshold + -------------------------------------------------------------------- + 0x04 | rssihint | ditto for RSSI high threshold + -------------------------------------------------------------------- + 0x05 | rssilint | ditto for RSSI low threshold + -------------------------------------------------------------------- + 0x06 | bltf | Flag indicating if seek command + | | reached/wrapped seek band limit + -------------------------------------------------------------------- + 0x07 | snr_ready | Indicates that SNR metrics is ready + -------------------------------------------------------------------- + 0x08 | rssiready | ditto for RSSI metrics + -------------------------------------------------------------------- + 0x09 | injside | 0 - Low-side injection is being used + | | 1 - High-side injection is used + -------------------------------------------------------------------- + 0x10 | afcrl | Flag indicating if AFC rails + -------------------------------------------------------------------- + 0x11 | valid | Flag indicating if channel is valid + -------------------------------------------------------------------- + 0x12 | readfreq | Current tuned frequency + -------------------------------------------------------------------- + 0x14 | freqoff | Singed frequency offset in units of + | | 2ppm + -------------------------------------------------------------------- + 0x15 | rssi | Signed value of RSSI in dBuV + -------------------------------------------------------------------- + 0x16 | snr | Signed RF SNR in dB + -------------------------------------------------------------------- + 0x17 | issi | Signed Image Strength Signal + | | indicator + -------------------------------------------------------------------- + 0x18 | lassi | Signed Low side adjacent Channel + | | Strength indicator + -------------------------------------------------------------------- + 0x19 | hassi | ditto fpr High side + -------------------------------------------------------------------- + 0x20 | mult | Multipath indicator + -------------------------------------------------------------------- + 0x21 | dev | Frequency deviation + -------------------------------------------------------------------- + 0x24 | assi | Adjascent channel SSI + -------------------------------------------------------------------- + 0x25 | usn | Ultrasonic noise indicator + -------------------------------------------------------------------- + 0x26 | pilotdev | Pilot deviation in units of 100 Hz + -------------------------------------------------------------------- + 0x27 | rdsdev | ditto for RDS + -------------------------------------------------------------------- + 0x28 | assidev | ditto for ASSI + -------------------------------------------------------------------- + 0x29 | strongdev | Frequency deviation + -------------------------------------------------------------------- + 0x30 | rdspi | RDS PI code + -------------------------------------------------------------------- + +* /sys/kernel/debug//rsq_primary + This file contains information about parameters pertaining to + RSQ(Received Signal Quality) for primary tuner only. Layout is as + the one above. diff --git a/trunk/drivers/media/dvb-core/dvb-usb-ids.h b/trunk/drivers/media/dvb-core/dvb-usb-ids.h index 335a8f4695b4..399e1042d351 100644 --- a/trunk/drivers/media/dvb-core/dvb-usb-ids.h +++ b/trunk/drivers/media/dvb-core/dvb-usb-ids.h @@ -124,7 +124,8 @@ #define USB_PID_DIBCOM_STK7770P 0x1e80 #define USB_PID_DIBCOM_NIM7090 0x1bb2 #define USB_PID_DIBCOM_TFE7090PVR 0x1bb4 -#define USB_PID_DIBCOM_TFE7790P 0x1e6e +#define USB_PID_DIBCOM_TFE7090E 0x1bb7 +#define USB_PID_DIBCOM_TFE7790E 0x1e6e #define USB_PID_DIBCOM_NIM9090M 0x2383 #define USB_PID_DIBCOM_NIM9090MD 0x2384 #define USB_PID_DPOSH_M9206_COLD 0x9206 diff --git a/trunk/drivers/media/dvb-frontends/dib0090.c b/trunk/drivers/media/dvb-frontends/dib0090.c index f9916b8434b7..d9fe60b4be48 100644 --- a/trunk/drivers/media/dvb-frontends/dib0090.c +++ b/trunk/drivers/media/dvb-frontends/dib0090.c @@ -528,19 +528,20 @@ static void dib0090_reset_digital(struct dvb_frontend *fe, const struct dib0090_ u16 PllCfg, i, v; HARD_RESET(state); - dib0090_write_reg(state, 0x24, EN_PLL | EN_CRYSTAL); - if (cfg->in_soc) - return; + dib0090_write_reg(state, 0x24, EN_PLL | EN_CRYSTAL); dib0090_write_reg(state, 0x1b, EN_DIGCLK | EN_PLL | EN_CRYSTAL); /* PLL, DIG_CLK and CRYSTAL remain */ - /* adcClkOutRatio=8->7, release reset */ - dib0090_write_reg(state, 0x20, ((cfg->io.adc_clock_ratio - 1) << 11) | (0 << 10) | (1 << 9) | (1 << 8) | (0 << 4) | 0); - if (cfg->clkoutdrive != 0) - dib0090_write_reg(state, 0x23, (0 << 15) | ((!cfg->analog_output) << 14) | (2 << 10) | (1 << 9) | (0 << 8) - | (cfg->clkoutdrive << 5) | (cfg->clkouttobamse << 4) | (0 << 2) | (0)); - else - dib0090_write_reg(state, 0x23, (0 << 15) | ((!cfg->analog_output) << 14) | (2 << 10) | (1 << 9) | (0 << 8) - | (7 << 5) | (cfg->clkouttobamse << 4) | (0 << 2) | (0)); + + if (!cfg->in_soc) { + /* adcClkOutRatio=8->7, release reset */ + dib0090_write_reg(state, 0x20, ((cfg->io.adc_clock_ratio - 1) << 11) | (0 << 10) | (1 << 9) | (1 << 8) | (0 << 4) | 0); + if (cfg->clkoutdrive != 0) + dib0090_write_reg(state, 0x23, (0 << 15) | ((!cfg->analog_output) << 14) | (2 << 10) | (1 << 9) | (0 << 8) + | (cfg->clkoutdrive << 5) | (cfg->clkouttobamse << 4) | (0 << 2) | (0)); + else + dib0090_write_reg(state, 0x23, (0 << 15) | ((!cfg->analog_output) << 14) | (2 << 10) | (1 << 9) | (0 << 8) + | (7 << 5) | (cfg->clkouttobamse << 4) | (0 << 2) | (0)); + } /* Read Pll current config * */ PllCfg = dib0090_read_reg(state, 0x21); @@ -693,174 +694,192 @@ void dib0090_dcc_freq(struct dvb_frontend *fe, u8 fast) EXPORT_SYMBOL(dib0090_dcc_freq); static const u16 bb_ramp_pwm_normal_socs[] = { - 550, /* max BB gain in 10th of dB */ - (1<<9) | 8, /* ramp_slope = 1dB of gain -> clock_ticks_per_db = clk_khz / ramp_slope -> BB_RAMP2 */ + 550, /* max BB gain in 10th of dB */ + (1 << 9) | 8, /* ramp_slope = 1dB of gain -> clock_ticks_per_db = clk_khz / ramp_slope -> BB_RAMP2 */ 440, - (4 << 9) | 0, /* BB_RAMP3 = 26dB */ - (0 << 9) | 208, /* BB_RAMP4 */ - (4 << 9) | 208, /* BB_RAMP5 = 29dB */ - (0 << 9) | 440, /* BB_RAMP6 */ + (4 << 9) | 0, /* BB_RAMP3 = 26dB */ + (0 << 9) | 208, /* BB_RAMP4 */ + (4 << 9) | 208, /* BB_RAMP5 = 29dB */ + (0 << 9) | 440, /* BB_RAMP6 */ }; -static const u16 rf_ramp_pwm_cband_7090p[] = { - 280, /* max RF gain in 10th of dB */ - 18, /* ramp_slope = 1dB of gain -> clock_ticks_per_db = clk_khz / ramp_slope -> RF_RAMP2 */ - 504, /* ramp_max = maximum X used on the ramp */ - (29 << 10) | 364, /* RF_RAMP5, LNA 1 = 8dB */ - (0 << 10) | 504, /* RF_RAMP6, LNA 1 */ - (60 << 10) | 228, /* RF_RAMP7, LNA 2 = 7.7dB */ - (0 << 10) | 364, /* RF_RAMP8, LNA 2 */ - (34 << 10) | 109, /* GAIN_4_1, LNA 3 = 6.8dB */ - (0 << 10) | 228, /* GAIN_4_2, LNA 3 */ - (37 << 10) | 0, /* RF_RAMP3, LNA 4 = 6.2dB */ - (0 << 10) | 109, /* RF_RAMP4, LNA 4 */ +static const u16 rf_ramp_pwm_cband_7090[] = { + 280, /* max RF gain in 10th of dB */ + 18, /* ramp_slope = 1dB of gain -> clock_ticks_per_db = clk_khz / ramp_slope -> RF_RAMP2 */ + 504, /* ramp_max = maximum X used on the ramp */ + (29 << 10) | 364, /* RF_RAMP5, LNA 1 = 8dB */ + (0 << 10) | 504, /* RF_RAMP6, LNA 1 */ + (60 << 10) | 228, /* RF_RAMP7, LNA 2 = 7.7dB */ + (0 << 10) | 364, /* RF_RAMP8, LNA 2 */ + (34 << 10) | 109, /* GAIN_4_1, LNA 3 = 6.8dB */ + (0 << 10) | 228, /* GAIN_4_2, LNA 3 */ + (37 << 10) | 0, /* RF_RAMP3, LNA 4 = 6.2dB */ + (0 << 10) | 109, /* RF_RAMP4, LNA 4 */ }; -static const u16 rf_ramp_pwm_cband_7090e_sensitivity[] = { - 186, /* max RF gain in 10th of dB */ - 40, /* ramp_slope = 1dB of gain -> clock_ticks_per_db = clk_khz / ramp_slope -> RF_RAMP2 */ - 746, /* ramp_max = maximum X used on the ramp */ - (10 << 10) | 345, /* RF_RAMP5, LNA 1 = 10dB */ - (0 << 10) | 746, /* RF_RAMP6, LNA 1 */ - (0 << 10) | 0, /* RF_RAMP7, LNA 2 = 0 dB */ - (0 << 10) | 0, /* RF_RAMP8, LNA 2 */ - (28 << 10) | 200, /* GAIN_4_1, LNA 3 = 6.8dB */ /* 3.61 dB */ - (0 << 10) | 345, /* GAIN_4_2, LNA 3 */ - (20 << 10) | 0, /* RF_RAMP3, LNA 4 = 6.2dB */ /* 4.96 dB */ - (0 << 10) | 200, /* RF_RAMP4, LNA 4 */ +static const uint16_t rf_ramp_pwm_cband_7090e_sensitivity[] = { + 186, + 40, + 746, + (10 << 10) | 345, + (0 << 10) | 746, + (0 << 10) | 0, + (0 << 10) | 0, + (28 << 10) | 200, + (0 << 10) | 345, + (20 << 10) | 0, + (0 << 10) | 200, }; -static const u16 rf_ramp_pwm_cband_7090e_aci[] = { - 86, /* max RF gain in 10th of dB */ - 40, /* ramp_slope = 1dB of gain -> clock_ticks_per_db = clk_khz / ramp_slope -> RF_RAMP2 */ - 345, /* ramp_max = maximum X used on the ramp */ - (0 << 10) | 0, /* RF_RAMP5, LNA 1 = 8dB */ /* 7.47 dB */ - (0 << 10) | 0, /* RF_RAMP6, LNA 1 */ - (0 << 10) | 0, /* RF_RAMP7, LNA 2 = 0 dB */ - (0 << 10) | 0, /* RF_RAMP8, LNA 2 */ - (28 << 10) | 200, /* GAIN_4_1, LNA 3 = 6.8dB */ /* 3.61 dB */ - (0 << 10) | 345, /* GAIN_4_2, LNA 3 */ - (20 << 10) | 0, /* RF_RAMP3, LNA 4 = 6.2dB */ /* 4.96 dB */ - (0 << 10) | 200, /* RF_RAMP4, LNA 4 */ +static const uint16_t rf_ramp_pwm_cband_7090e_aci[] = { + 86, + 40, + 345, + (0 << 10) | 0, + (0 << 10) | 0, + (0 << 10) | 0, + (0 << 10) | 0, + (28 << 10) | 200, + (0 << 10) | 345, + (20 << 10) | 0, + (0 << 10) | 200, }; static const u16 rf_ramp_pwm_cband_8090[] = { - 345, /* max RF gain in 10th of dB */ - 29, /* ramp_slope = 1dB of gain -> clock_ticks_per_db = clk_khz / ramp_slope -> RF_RAMP2 */ - 1000, /* ramp_max = maximum X used on the ramp */ - (35 << 10) | 772, /* RF_RAMP3, LNA 1 = 8dB */ - (0 << 10) | 1000, /* RF_RAMP4, LNA 1 */ - (58 << 10) | 496, /* RF_RAMP5, LNA 2 = 9.5dB */ - (0 << 10) | 772, /* RF_RAMP6, LNA 2 */ - (27 << 10) | 200, /* RF_RAMP7, LNA 3 = 10.5dB */ - (0 << 10) | 496, /* RF_RAMP8, LNA 3 */ - (40 << 10) | 0, /* GAIN_4_1, LNA 4 = 7dB */ - (0 << 10) | 200, /* GAIN_4_2, LNA 4 */ + 345, /* max RF gain in 10th of dB */ + 29, /* ramp_slope = 1dB of gain -> clock_ticks_per_db = clk_khz / ramp_slope -> RF_RAMP2 */ + 1000, /* ramp_max = maximum X used on the ramp */ + (35 << 10) | 772, /* RF_RAMP3, LNA 1 = 8dB */ + (0 << 10) | 1000, /* RF_RAMP4, LNA 1 */ + (58 << 10) | 496, /* RF_RAMP5, LNA 2 = 9.5dB */ + (0 << 10) | 772, /* RF_RAMP6, LNA 2 */ + (27 << 10) | 200, /* RF_RAMP7, LNA 3 = 10.5dB */ + (0 << 10) | 496, /* RF_RAMP8, LNA 3 */ + (40 << 10) | 0, /* GAIN_4_1, LNA 4 = 7dB */ + (0 << 10) | 200, /* GAIN_4_2, LNA 4 */ }; static const u16 rf_ramp_pwm_uhf_7090[] = { - 407, /* max RF gain in 10th of dB */ - 13, /* ramp_slope = 1dB of gain -> clock_ticks_per_db = clk_khz / ramp_slope -> RF_RAMP2 */ - 529, /* ramp_max = maximum X used on the ramp */ - (23 << 10) | 0, /* RF_RAMP3, LNA 1 = 14.7dB */ - (0 << 10) | 176, /* RF_RAMP4, LNA 1 */ - (63 << 10) | 400, /* RF_RAMP5, LNA 2 = 8dB */ - (0 << 10) | 529, /* RF_RAMP6, LNA 2 */ - (48 << 10) | 316, /* RF_RAMP7, LNA 3 = 6.8dB */ - (0 << 10) | 400, /* RF_RAMP8, LNA 3 */ - (29 << 10) | 176, /* GAIN_4_1, LNA 4 = 11.5dB */ - (0 << 10) | 316, /* GAIN_4_2, LNA 4 */ + 407, /* max RF gain in 10th of dB */ + 13, /* ramp_slope = 1dB of gain -> clock_ticks_per_db = clk_khz / ramp_slope -> RF_RAMP2 */ + 529, /* ramp_max = maximum X used on the ramp */ + (23 << 10) | 0, /* RF_RAMP3, LNA 1 = 14.7dB */ + (0 << 10) | 176, /* RF_RAMP4, LNA 1 */ + (63 << 10) | 400, /* RF_RAMP5, LNA 2 = 8dB */ + (0 << 10) | 529, /* RF_RAMP6, LNA 2 */ + (48 << 10) | 316, /* RF_RAMP7, LNA 3 = 6.8dB */ + (0 << 10) | 400, /* RF_RAMP8, LNA 3 */ + (29 << 10) | 176, /* GAIN_4_1, LNA 4 = 11.5dB */ + (0 << 10) | 316, /* GAIN_4_2, LNA 4 */ }; static const u16 rf_ramp_pwm_uhf_8090[] = { - 388, /* max RF gain in 10th of dB */ - 26, /* ramp_slope = 1dB of gain -> clock_ticks_per_db = clk_khz / ramp_slope -> RF_RAMP2 */ - 1008, /* ramp_max = maximum X used on the ramp */ - (11 << 10) | 0, /* RF_RAMP3, LNA 1 = 14.7dB */ - (0 << 10) | 369, /* RF_RAMP4, LNA 1 */ - (41 << 10) | 809, /* RF_RAMP5, LNA 2 = 8dB */ - (0 << 10) | 1008, /* RF_RAMP6, LNA 2 */ - (27 << 10) | 659, /* RF_RAMP7, LNA 3 = 6dB */ - (0 << 10) | 809, /* RF_RAMP8, LNA 3 */ - (14 << 10) | 369, /* GAIN_4_1, LNA 4 = 11.5dB */ - (0 << 10) | 659, /* GAIN_4_2, LNA 4 */ + 388, /* max RF gain in 10th of dB */ + 26, /* ramp_slope = 1dB of gain -> clock_ticks_per_db = clk_khz / ramp_slope -> RF_RAMP2 */ + 1008, /* ramp_max = maximum X used on the ramp */ + (11 << 10) | 0, /* RF_RAMP3, LNA 1 = 14.7dB */ + (0 << 10) | 369, /* RF_RAMP4, LNA 1 */ + (41 << 10) | 809, /* RF_RAMP5, LNA 2 = 8dB */ + (0 << 10) | 1008, /* RF_RAMP6, LNA 2 */ + (27 << 10) | 659, /* RF_RAMP7, LNA 3 = 6dB */ + (0 << 10) | 809, /* RF_RAMP8, LNA 3 */ + (14 << 10) | 369, /* GAIN_4_1, LNA 4 = 11.5dB */ + (0 << 10) | 659, /* GAIN_4_2, LNA 4 */ }; -/* GENERAL PWM ramp definition for all other Krosus */ -static const u16 bb_ramp_pwm_normal[] = { - 500, /* max BB gain in 10th of dB */ - 8, /* ramp_slope = 1dB of gain -> clock_ticks_per_db = clk_khz / ramp_slope -> BB_RAMP2 */ - 400, - (2 << 9) | 0, /* BB_RAMP3 = 21dB */ - (0 << 9) | 168, /* BB_RAMP4 */ - (2 << 9) | 168, /* BB_RAMP5 = 29dB */ - (0 << 9) | 400, /* BB_RAMP6 */ +static const u16 rf_ramp_pwm_cband[] = { + 0, /* max RF gain in 10th of dB */ + 0, /* ramp_slope = 1dB of gain -> clock_ticks_per_db = clk_khz / ramp_slope -> 0x2b */ + 0, /* ramp_max = maximum X used on the ramp */ + (0 << 10) | 0, /* 0x2c, LNA 1 = 0dB */ + (0 << 10) | 0, /* 0x2d, LNA 1 */ + (0 << 10) | 0, /* 0x2e, LNA 2 = 0dB */ + (0 << 10) | 0, /* 0x2f, LNA 2 */ + (0 << 10) | 0, /* 0x30, LNA 3 = 0dB */ + (0 << 10) | 0, /* 0x31, LNA 3 */ + (0 << 10) | 0, /* GAIN_4_1, LNA 4 = 0dB */ + (0 << 10) | 0, /* GAIN_4_2, LNA 4 */ }; -static const u16 bb_ramp_pwm_boost[] = { - 550, /* max BB gain in 10th of dB */ - 8, /* ramp_slope = 1dB of gain -> clock_ticks_per_db = clk_khz / ramp_slope -> BB_RAMP2 */ - 440, - (2 << 9) | 0, /* BB_RAMP3 = 26dB */ - (0 << 9) | 208, /* BB_RAMP4 */ - (2 << 9) | 208, /* BB_RAMP5 = 29dB */ - (0 << 9) | 440, /* BB_RAMP6 */ +static const u16 rf_ramp_vhf[] = { + 412, /* max RF gain in 10th of dB */ + 132, 307, 127, /* LNA1, 13.2dB */ + 105, 412, 255, /* LNA2, 10.5dB */ + 50, 50, 127, /* LNA3, 5dB */ + 125, 175, 127, /* LNA4, 12.5dB */ + 0, 0, 127, /* CBAND, 0dB */ }; -static const u16 rf_ramp_pwm_cband[] = { - 314, /* max RF gain in 10th of dB */ - 33, /* ramp_slope = 1dB of gain -> clock_ticks_per_db = clk_khz / ramp_slope -> RF_RAMP2 */ - 1023, /* ramp_max = maximum X used on the ramp */ - (8 << 10) | 743, /* RF_RAMP3, LNA 1 = 0dB */ - (0 << 10) | 1023, /* RF_RAMP4, LNA 1 */ - (15 << 10) | 469, /* RF_RAMP5, LNA 2 = 0dB */ - (0 << 10) | 742, /* RF_RAMP6, LNA 2 */ - (9 << 10) | 234, /* RF_RAMP7, LNA 3 = 0dB */ - (0 << 10) | 468, /* RF_RAMP8, LNA 3 */ - (9 << 10) | 0, /* GAIN_4_1, LNA 4 = 0dB */ - (0 << 10) | 233, /* GAIN_4_2, LNA 4 */ +static const u16 rf_ramp_uhf[] = { + 412, /* max RF gain in 10th of dB */ + 132, 307, 127, /* LNA1 : total gain = 13.2dB, point on the ramp where this amp is full gain, value to write to get full gain */ + 105, 412, 255, /* LNA2 : 10.5 dB */ + 50, 50, 127, /* LNA3 : 5.0 dB */ + 125, 175, 127, /* LNA4 : 12.5 dB */ + 0, 0, 127, /* CBAND : 0.0 dB */ +}; + +static const u16 rf_ramp_cband_broadmatching[] = /* for p1G only */ +{ + 314, /* Calibrated at 200MHz order has been changed g4-g3-g2-g1 */ + 84, 314, 127, /* LNA1 */ + 80, 230, 255, /* LNA2 */ + 80, 150, 127, /* LNA3 It was measured 12dB, do not lock if 120 */ + 70, 70, 127, /* LNA4 */ + 0, 0, 127, /* CBAND */ +}; + +static const u16 rf_ramp_cband[] = { + 332, /* max RF gain in 10th of dB */ + 132, 252, 127, /* LNA1, dB */ + 80, 332, 255, /* LNA2, dB */ + 0, 0, 127, /* LNA3, dB */ + 0, 0, 127, /* LNA4, dB */ + 120, 120, 127, /* LT1 CBAND */ }; static const u16 rf_ramp_pwm_vhf[] = { - 398, /* max RF gain in 10th of dB */ - 24, /* ramp_slope = 1dB of gain -> clock_ticks_per_db = clk_khz / ramp_slope -> RF_RAMP2 */ - 954, /* ramp_max = maximum X used on the ramp */ - (7 << 10) | 0, /* RF_RAMP3, LNA 1 = 13.2dB */ - (0 << 10) | 290, /* RF_RAMP4, LNA 1 */ - (16 << 10) | 699, /* RF_RAMP5, LNA 2 = 10.5dB */ - (0 << 10) | 954, /* RF_RAMP6, LNA 2 */ - (17 << 10) | 580, /* RF_RAMP7, LNA 3 = 5dB */ - (0 << 10) | 699, /* RF_RAMP8, LNA 3 */ - (7 << 10) | 290, /* GAIN_4_1, LNA 4 = 12.5dB */ - (0 << 10) | 580, /* GAIN_4_2, LNA 4 */ + 404, /* max RF gain in 10th of dB */ + 25, /* ramp_slope = 1dB of gain -> clock_ticks_per_db = clk_khz / ramp_slope -> 0x2b */ + 1011, /* ramp_max = maximum X used on the ramp */ + (6 << 10) | 417, /* 0x2c, LNA 1 = 13.2dB */ + (0 << 10) | 756, /* 0x2d, LNA 1 */ + (16 << 10) | 756, /* 0x2e, LNA 2 = 10.5dB */ + (0 << 10) | 1011, /* 0x2f, LNA 2 */ + (16 << 10) | 290, /* 0x30, LNA 3 = 5dB */ + (0 << 10) | 417, /* 0x31, LNA 3 */ + (7 << 10) | 0, /* GAIN_4_1, LNA 4 = 12.5dB */ + (0 << 10) | 290, /* GAIN_4_2, LNA 4 */ }; static const u16 rf_ramp_pwm_uhf[] = { - 398, /* max RF gain in 10th of dB */ - 24, /* ramp_slope = 1dB of gain -> clock_ticks_per_db = clk_khz / ramp_slope -> RF_RAMP2 */ - 954, /* ramp_max = maximum X used on the ramp */ - (7 << 10) | 0, /* RF_RAMP3, LNA 1 = 13.2dB */ - (0 << 10) | 290, /* RF_RAMP4, LNA 1 */ - (16 << 10) | 699, /* RF_RAMP5, LNA 2 = 10.5dB */ - (0 << 10) | 954, /* RF_RAMP6, LNA 2 */ - (17 << 10) | 580, /* RF_RAMP7, LNA 3 = 5dB */ - (0 << 10) | 699, /* RF_RAMP8, LNA 3 */ - (7 << 10) | 290, /* GAIN_4_1, LNA 4 = 12.5dB */ - (0 << 10) | 580, /* GAIN_4_2, LNA 4 */ + 404, /* max RF gain in 10th of dB */ + 25, /* ramp_slope = 1dB of gain -> clock_ticks_per_db = clk_khz / ramp_slope -> 0x2b */ + 1011, /* ramp_max = maximum X used on the ramp */ + (6 << 10) | 417, /* 0x2c, LNA 1 = 13.2dB */ + (0 << 10) | 756, /* 0x2d, LNA 1 */ + (16 << 10) | 756, /* 0x2e, LNA 2 = 10.5dB */ + (0 << 10) | 1011, /* 0x2f, LNA 2 */ + (16 << 10) | 0, /* 0x30, LNA 3 = 5dB */ + (0 << 10) | 127, /* 0x31, LNA 3 */ + (7 << 10) | 127, /* GAIN_4_1, LNA 4 = 12.5dB */ + (0 << 10) | 417, /* GAIN_4_2, LNA 4 */ }; -static const u16 rf_ramp_pwm_sband[] = { - 253, /* max RF gain in 10th of dB */ - 38, /* ramp_slope = 1dB of gain -> clock_ticks_per_db = clk_khz / ramp_slope -> RF_RAMP2 */ - 961, - (4 << 10) | 0, /* RF_RAMP3, LNA 1 = 14.1dB */ - (0 << 10) | 508, /* RF_RAMP4, LNA 1 */ - (9 << 10) | 508, /* RF_RAMP5, LNA 2 = 11.2dB */ - (0 << 10) | 961, /* RF_RAMP6, LNA 2 */ - (0 << 10) | 0, /* RF_RAMP7, LNA 3 = 0dB */ - (0 << 10) | 0, /* RF_RAMP8, LNA 3 */ - (0 << 10) | 0, /* GAIN_4_1, LNA 4 = 0dB */ - (0 << 10) | 0, /* GAIN_4_2, LNA 4 */ +static const u16 bb_ramp_boost[] = { + 550, /* max BB gain in 10th of dB */ + 260, 260, 26, /* BB1, 26dB */ + 290, 550, 29, /* BB2, 29dB */ +}; + +static const u16 bb_ramp_pwm_normal[] = { + 500, /* max RF gain in 10th of dB */ + 8, /* ramp_slope = 1dB of gain -> clock_ticks_per_db = clk_khz / ramp_slope -> 0x34 */ + 400, + (2 << 9) | 0, /* 0x35 = 21dB */ + (0 << 9) | 168, /* 0x36 */ + (2 << 9) | 168, /* 0x37 = 29dB */ + (0 << 9) | 400, /* 0x38 */ }; struct slope { @@ -1070,69 +1089,70 @@ static void dib0090_set_bbramp_pwm(struct dib0090_state *state, const u16 * cfg) void dib0090_pwm_gain_reset(struct dvb_frontend *fe) { struct dib0090_state *state = fe->tuner_priv; - u16 *bb_ramp = (u16 *)&bb_ramp_pwm_normal; /* default baseband config */ - u16 *rf_ramp = NULL; - u8 en_pwm_rf_mux = 1; - /* reset the AGC */ + if (state->config->use_pwm_agc) { +#ifdef CONFIG_BAND_SBAND + if (state->current_band == BAND_SBAND) { + dib0090_set_rframp_pwm(state, rf_ramp_pwm_sband); + dib0090_set_bbramp_pwm(state, bb_ramp_pwm_boost); + } else +#endif +#ifdef CONFIG_BAND_CBAND if (state->current_band == BAND_CBAND) { if (state->identity.in_soc) { - bb_ramp = (u16 *)&bb_ramp_pwm_normal_socs; + dib0090_set_bbramp_pwm(state, bb_ramp_pwm_normal_socs); if (state->identity.version == SOC_8090_P1G_11R1 || state->identity.version == SOC_8090_P1G_21R1) - rf_ramp = (u16 *)&rf_ramp_pwm_cband_8090; - else if (state->identity.version == SOC_7090_P1G_11R1 || state->identity.version == SOC_7090_P1G_21R1) { + dib0090_set_rframp_pwm(state, rf_ramp_pwm_cband_8090); + else if (state->identity.version == SOC_7090_P1G_11R1 + || state->identity.version == SOC_7090_P1G_21R1) { if (state->config->is_dib7090e) { if (state->rf_ramp == NULL) - rf_ramp = (u16 *)&rf_ramp_pwm_cband_7090e_sensitivity; + dib0090_set_rframp_pwm(state, rf_ramp_pwm_cband_7090e_sensitivity); else - rf_ramp = (u16 *)state->rf_ramp; + dib0090_set_rframp_pwm(state, state->rf_ramp); } else - rf_ramp = (u16 *)&rf_ramp_pwm_cband_7090p; + dib0090_set_rframp_pwm(state, rf_ramp_pwm_cband_7090); } - } else - rf_ramp = (u16 *)&rf_ramp_pwm_cband; + } else { + dib0090_set_rframp_pwm(state, rf_ramp_pwm_cband); + dib0090_set_bbramp_pwm(state, bb_ramp_pwm_normal); + } } else - - if (state->current_band == BAND_VHF) { - if (state->identity.in_soc) { - bb_ramp = (u16 *)&bb_ramp_pwm_normal_socs; - /* rf_ramp = &rf_ramp_pwm_vhf_socs; */ /* TODO */ - } else - rf_ramp = (u16 *)&rf_ramp_pwm_vhf; - } else if (state->current_band == BAND_UHF) { - if (state->identity.in_soc) { - bb_ramp = (u16 *)&bb_ramp_pwm_normal_socs; - if (state->identity.version == SOC_8090_P1G_11R1 || state->identity.version == SOC_8090_P1G_21R1) - rf_ramp = (u16 *)&rf_ramp_pwm_uhf_8090; - else if (state->identity.version == SOC_7090_P1G_11R1 || state->identity.version == SOC_7090_P1G_21R1) - rf_ramp = (u16 *)&rf_ramp_pwm_uhf_7090; - } else - rf_ramp = (u16 *)&rf_ramp_pwm_uhf; +#endif +#ifdef CONFIG_BAND_VHF + if (state->current_band == BAND_VHF) { + if (state->identity.in_soc) { + dib0090_set_bbramp_pwm(state, bb_ramp_pwm_normal_socs); + } else { + dib0090_set_rframp_pwm(state, rf_ramp_pwm_vhf); + dib0090_set_bbramp_pwm(state, bb_ramp_pwm_normal); } - if (rf_ramp) - dib0090_set_rframp_pwm(state, rf_ramp); - dib0090_set_bbramp_pwm(state, bb_ramp); - - /* activate the ramp generator using PWM control */ - dprintk("ramp RF gain = %d BAND = %s version = %d", state->rf_ramp[0], (state->current_band == BAND_CBAND) ? "CBAND" : "NOT CBAND", state->identity.version & 0x1f); - - if ((state->rf_ramp[0] == 0) || (state->current_band == BAND_CBAND && (state->identity.version & 0x1f) <= P1D_E_F)) { - dprintk("DE-Engage mux for direct gain reg control"); - en_pwm_rf_mux = 0; } else - dprintk("Engage mux for PWM control"); - - dib0090_write_reg(state, 0x32, (en_pwm_rf_mux << 12) | (en_pwm_rf_mux << 11)); +#endif + { + if (state->identity.in_soc) { + if (state->identity.version == SOC_8090_P1G_11R1 || state->identity.version == SOC_8090_P1G_21R1) + dib0090_set_rframp_pwm(state, rf_ramp_pwm_uhf_8090); + else if (state->identity.version == SOC_7090_P1G_11R1 || state->identity.version == SOC_7090_P1G_21R1) + dib0090_set_rframp_pwm(state, rf_ramp_pwm_uhf_7090); + dib0090_set_bbramp_pwm(state, bb_ramp_pwm_normal_socs); + } else { + dib0090_set_rframp_pwm(state, rf_ramp_pwm_uhf); + dib0090_set_bbramp_pwm(state, bb_ramp_pwm_normal); + } + } - /* Set fast servo cutoff to start AGC; 0 = 1KHz ; 1 = 50Hz ; 2 = 150Hz ; 3 = 50KHz ; 4 = servo fast*/ - if (state->identity.version == SOC_7090_P1G_11R1 || state->identity.version == SOC_7090_P1G_21R1) - dib0090_write_reg(state, 0x04, 3); + if (state->rf_ramp[0] != 0) + dib0090_write_reg(state, 0x32, (3 << 11)); else - dib0090_write_reg(state, 0x04, 1); - dib0090_write_reg(state, 0x39, (1 << 10)); /* 0 gain by default */ + dib0090_write_reg(state, 0x32, (0 << 11)); + + dib0090_write_reg(state, 0x04, 0x03); + dib0090_write_reg(state, 0x39, (1 << 10)); } } + EXPORT_SYMBOL(dib0090_pwm_gain_reset); void dib0090_set_dc_servo(struct dvb_frontend *fe, u8 DC_servo_cutoff) @@ -1173,22 +1193,22 @@ int dib0090_gain_control(struct dvb_frontend *fe) #endif #ifdef CONFIG_BAND_VHF if (state->current_band == BAND_VHF && !state->identity.p1g) { - dib0090_set_rframp(state, rf_ramp_pwm_vhf); - dib0090_set_bbramp(state, bb_ramp_pwm_normal); + dib0090_set_rframp(state, rf_ramp_vhf); + dib0090_set_bbramp(state, bb_ramp_boost); } else #endif #ifdef CONFIG_BAND_CBAND if (state->current_band == BAND_CBAND && !state->identity.p1g) { - dib0090_set_rframp(state, rf_ramp_pwm_cband); - dib0090_set_bbramp(state, bb_ramp_pwm_normal); + dib0090_set_rframp(state, rf_ramp_cband); + dib0090_set_bbramp(state, bb_ramp_boost); } else #endif if ((state->current_band == BAND_CBAND || state->current_band == BAND_VHF) && state->identity.p1g) { - dib0090_set_rframp(state, rf_ramp_pwm_cband_7090p); - dib0090_set_bbramp(state, bb_ramp_pwm_normal_socs); + dib0090_set_rframp(state, rf_ramp_cband_broadmatching); + dib0090_set_bbramp(state, bb_ramp_boost); } else { - dib0090_set_rframp(state, rf_ramp_pwm_uhf); - dib0090_set_bbramp(state, bb_ramp_pwm_normal); + dib0090_set_rframp(state, rf_ramp_uhf); + dib0090_set_bbramp(state, bb_ramp_boost); } dib0090_write_reg(state, 0x32, 0); @@ -1533,20 +1553,14 @@ static void dib0090_set_EFUSE(struct dib0090_state *state) if ((c >= CAP_VALUE_MAX) || (c <= CAP_VALUE_MIN)) c = 32; - else - c += 14; if ((h >= HR_MAX) || (h <= HR_MIN)) h = 34; if ((n >= POLY_MAX) || (n <= POLY_MIN)) n = 3; - if ((c >= CAP_VALUE_MIN) && (c <= CAP_VALUE_MAX) - && (h >= HR_MIN) && (h <= HR_MAX) - && (n >= POLY_MIN) && (n <= POLY_MAX)) { - dib0090_write_reg(state, 0x13, (h << 10)); - e2 = (n << 11) | ((h >> 2)<<6) | c; - dib0090_write_reg(state, 0x2, e2); /* Load the BB_2 */ - } + dib0090_write_reg(state, 0x13, (h << 10)) ; + e2 = (n<<11) | ((h>>2)<<6) | (c); + dib0090_write_reg(state, 0x2, e2) ; /* Load the BB_2 */ } } diff --git a/trunk/drivers/media/dvb-frontends/dib7000p.c b/trunk/drivers/media/dvb-frontends/dib7000p.c index effb87f773b0..3e1eefada0e8 100644 --- a/trunk/drivers/media/dvb-frontends/dib7000p.c +++ b/trunk/drivers/media/dvb-frontends/dib7000p.c @@ -429,13 +429,6 @@ int dib7000p_get_agc_values(struct dvb_frontend *fe, } EXPORT_SYMBOL(dib7000p_get_agc_values); -int dib7000p_set_agc1_min(struct dvb_frontend *fe, u16 v) -{ - struct dib7000p_state *state = fe->demodulator_priv; - return dib7000p_write_word(state, 108, v); -} -EXPORT_SYMBOL(dib7000p_set_agc1_min); - static void dib7000p_reset_pll(struct dib7000p_state *state) { struct dibx000_bandwidth_config *bw = &state->cfg.bw[0]; @@ -828,7 +821,6 @@ static int dib7000p_agc_startup(struct dvb_frontend *demod) u8 agc_split; u16 reg; u32 upd_demod_gain_period = 0x1000; - s32 frequency_offset = 0; switch (state->agc_state) { case 0: @@ -849,14 +841,7 @@ static int dib7000p_agc_startup(struct dvb_frontend *demod) if (dib7000p_set_agc_config(state, BAND_OF_FREQUENCY(ch->frequency / 1000)) != 0) return -1; - if (demod->ops.tuner_ops.get_frequency) { - u32 frequency_tuner; - - demod->ops.tuner_ops.get_frequency(demod, &frequency_tuner); - frequency_offset = (s32)frequency_tuner / 1000 - ch->frequency / 1000; - } - - dib7000p_set_dds(state, frequency_offset); + dib7000p_set_dds(state, 0); ret = 7; (*agc_state)++; break; diff --git a/trunk/drivers/media/dvb-frontends/dib7000p.h b/trunk/drivers/media/dvb-frontends/dib7000p.h index d08cdff59bdf..cf5e77956a1f 100644 --- a/trunk/drivers/media/dvb-frontends/dib7000p.h +++ b/trunk/drivers/media/dvb-frontends/dib7000p.h @@ -63,7 +63,6 @@ extern struct i2c_adapter *dib7090_get_i2c_tuner(struct dvb_frontend *fe); extern int dib7090_slave_reset(struct dvb_frontend *fe); extern int dib7000p_get_agc_values(struct dvb_frontend *fe, u16 *agc_global, u16 *agc1, u16 *agc2, u16 *wbd); -extern int dib7000p_set_agc1_min(struct dvb_frontend *fe, u16 v); #else static inline struct dvb_frontend *dib7000p_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr, struct dib7000p_config *cfg) { @@ -155,12 +154,6 @@ static inline int dib7000p_get_agc_values(struct dvb_frontend *fe, printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); return -ENODEV; } - -static inline int dib7000p_set_agc1_min(struct dvb_frontend *fe, u16 v) -{ - printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); - return -ENODEV; -} #endif #endif diff --git a/trunk/drivers/media/dvb-frontends/dib8000.c b/trunk/drivers/media/dvb-frontends/dib8000.c index 1d719cce8995..1f3bcb5a1de8 100644 --- a/trunk/drivers/media/dvb-frontends/dib8000.c +++ b/trunk/drivers/media/dvb-frontends/dib8000.c @@ -23,8 +23,8 @@ #define LAYER_B 2 #define LAYER_C 3 +#define FE_CALLBACK_TIME_NEVER 0xffffffff #define MAX_NUMBER_OF_FRONTENDS 6 -/* #define DIB8000_AGC_FREEZE */ static int debug; module_param(debug, int, 0644); @@ -32,6 +32,8 @@ MODULE_PARM_DESC(debug, "turn on debugging (default: 0)"); #define dprintk(args...) do { if (debug) { printk(KERN_DEBUG "DiB8000: "); printk(args); printk("\n"); } } while (0) +#define FE_STATUS_TUNE_FAILED 0 + struct i2c_device { struct i2c_adapter *adap; u8 addr; @@ -40,23 +42,6 @@ struct i2c_device { struct mutex *i2c_buffer_lock; }; -enum param_loop_step { - LOOP_TUNE_1, - LOOP_TUNE_2 -}; - -enum dib8000_autosearch_step { - AS_START = 0, - AS_SEARCHING_FFT, - AS_SEARCHING_GUARD, - AS_DONE = 100, -}; - -enum timeout_mode { - SYMBOL_DEPENDENT_OFF = 0, - SYMBOL_DEPENDENT_ON, -}; - struct dib8000_state { struct dib8000_config cfg; @@ -87,7 +72,7 @@ struct dib8000_state { u16 revision; u8 isdbt_cfg_loaded; enum frontend_tune_state tune_state; - s32 status; + u32 status; struct dvb_frontend *fe[MAX_NUMBER_OF_FRONTENDS]; @@ -100,30 +85,6 @@ struct dib8000_state { u16 tuner_enable; struct i2c_adapter dib8096p_tuner_adap; - u16 current_demod_bw; - - u16 seg_mask; - u16 seg_diff_mask; - u16 mode; - u8 layer_b_nb_seg; - u8 layer_c_nb_seg; - - u8 channel_parameters_set; - u16 autosearch_state; - u16 found_nfft; - u16 found_guard; - u8 subchannel; - u8 symbol_duration; - u32 timeout; - u8 longest_intlv_layer; - u16 output_mode; - -#ifdef DIB8000_AGC_FREEZE - u16 agc1_max; - u16 agc1_min; - u16 agc2_max; - u16 agc2_min; -#endif }; enum dib8000_power_mode { @@ -377,9 +338,9 @@ static void dib8000_set_acquisition_mode(struct dib8000_state *state) static int dib8000_set_output_mode(struct dvb_frontend *fe, int mode) { struct dib8000_state *state = fe->demodulator_priv; + u16 outreg, fifo_threshold, smo_mode, sram = 0x0205; /* by default SDRAM deintlv is enabled */ - state->output_mode = mode; outreg = 0; fifo_threshold = 1792; smo_mode = (dib8000_read_word(state, 299) & 0x0050) | (1 << 1); @@ -438,9 +399,8 @@ static int dib8000_set_output_mode(struct dvb_frontend *fe, int mode) static int dib8000_set_diversity_in(struct dvb_frontend *fe, int onoff) { struct dib8000_state *state = fe->demodulator_priv; - u16 tmp, sync_wait = dib8000_read_word(state, 273) & 0xfff0; + u16 sync_wait = dib8000_read_word(state, 273) & 0xfff0; - dprintk("set diversity input to %i", onoff); if (!state->differential_constellation) { dib8000_write_word(state, 272, 1 << 9); //dvsy_off_lmod4 = 1 dib8000_write_word(state, 273, sync_wait | (1 << 2) | 2); // sync_enable = 1; comb_mode = 2 @@ -464,13 +424,6 @@ static int dib8000_set_diversity_in(struct dvb_frontend *fe, int onoff) dib8000_write_word(state, 271, 1); break; } - - if (state->revision == 0x8002) { - tmp = dib8000_read_word(state, 903); - dib8000_write_word(state, 903, tmp & ~(1 << 3)); - msleep(30); - dib8000_write_word(state, 903, tmp | (1 << 3)); - } return 0; } @@ -515,6 +468,27 @@ static void dib8000_set_power_mode(struct dib8000_state *state, enum dib8000_pow dib8000_write_word(state, 1280, reg_1280); } +static int dib8000_init_sdram(struct dib8000_state *state) +{ + u16 reg = 0; + dprintk("Init sdram"); + + reg = dib8000_read_word(state, 274)&0xfff0; + /* P_dintlv_delay_ram = 7 because of MobileSdram */ + dib8000_write_word(state, 274, reg | 0x7); + + dib8000_write_word(state, 1803, (7<<2)); + + reg = dib8000_read_word(state, 1280); + /* force restart P_restart_sdram */ + dib8000_write_word(state, 1280, reg | (1<<2)); + + /* release restart P_restart_sdram */ + dib8000_write_word(state, 1280, reg); + + return 0; +} + static int dib8000_set_adc_state(struct dib8000_state *state, enum dibx000_adc_states no) { int ret = 0; @@ -610,23 +584,18 @@ static int dib8000_set_bandwidth(struct dvb_frontend *fe, u32 bw) static int dib8000_sad_calib(struct dib8000_state *state) { - u8 sad_sel = 3; - if (state->revision == 0x8090) { - dib8000_write_word(state, 922, (sad_sel << 2)); - dib8000_write_word(state, 923, 2048); - - dib8000_write_word(state, 922, (sad_sel << 2) | 0x1); - dib8000_write_word(state, 922, (sad_sel << 2)); - } else { - /* internal */ - dib8000_write_word(state, 923, (0 << 1) | (0 << 0)); - dib8000_write_word(state, 924, 776); - - /* do the calibration */ - dib8000_write_word(state, 923, (1 << 0)); - dib8000_write_word(state, 923, (0 << 0)); + dprintk("%s: the sad calibration is not needed for the dib8096P", + __func__); + return 0; } + /* internal */ + dib8000_write_word(state, 923, (0 << 1) | (0 << 0)); + dib8000_write_word(state, 924, 776); // 0.625*3.3 / 4096 + + /* do the calibration */ + dib8000_write_word(state, 923, (1 << 0)); + dib8000_write_word(state, 923, (0 << 0)); msleep(1); return 0; @@ -640,8 +609,8 @@ int dib8000_set_wbd_ref(struct dvb_frontend *fe, u16 value) state->wbd_ref = value; return dib8000_write_word(state, 106, value); } -EXPORT_SYMBOL(dib8000_set_wbd_ref); +EXPORT_SYMBOL(dib8000_set_wbd_ref); static void dib8000_reset_pll_common(struct dib8000_state *state, const struct dibx000_bandwidth_config *bw) { dprintk("ifreq: %d %x, inversion: %d", bw->ifreq, bw->ifreq, bw->ifreq >> 25); @@ -716,23 +685,20 @@ static void dib8000_reset_pll(struct dib8000_state *state) } int dib8000_update_pll(struct dvb_frontend *fe, - struct dibx000_bandwidth_config *pll, u32 bw, u8 ratio) + struct dibx000_bandwidth_config *pll) { struct dib8000_state *state = fe->demodulator_priv; u16 reg_1857, reg_1856 = dib8000_read_word(state, 1856); - u8 loopdiv, prediv, oldprediv = state->cfg.pll->pll_prediv ; + u8 loopdiv, prediv; u32 internal, xtal; /* get back old values */ prediv = reg_1856 & 0x3f; loopdiv = (reg_1856 >> 6) & 0x3f; - if ((pll == NULL) || (pll->pll_prediv == prediv && - pll->pll_ratio == loopdiv)) - return -EINVAL; - - dprintk("Updating pll (prediv: old = %d new = %d ; loopdiv : old = %d new = %d)", prediv, pll->pll_prediv, loopdiv, pll->pll_ratio); - if (state->revision == 0x8090) { + if ((pll != NULL) && (pll->pll_prediv != prediv || + pll->pll_ratio != loopdiv)) { + dprintk("Updating pll (prediv: old = %d new = %d ; loopdiv : old = %d new = %d)", prediv, pll->pll_prediv, loopdiv, pll->pll_ratio); reg_1856 &= 0xf000; reg_1857 = dib8000_read_word(state, 1857); /* disable PLL */ @@ -763,33 +729,10 @@ int dib8000_update_pll(struct dvb_frontend *fe, reg_1856 = dib8000_read_word(state, 1856); dprintk("PLL Updated with prediv = %d and loopdiv = %d", reg_1856&0x3f, (reg_1856>>6)&0x3f); - } else { - if (bw != state->current_demod_bw) { - /** Bandwidth change => force PLL update **/ - dprintk("PLL: Bandwidth Change %d MHz -> %d MHz (prediv: %d->%d)", state->current_demod_bw / 1000, bw / 1000, oldprediv, state->cfg.pll->pll_prediv); - - if (state->cfg.pll->pll_prediv != oldprediv) { - /** Full PLL change only if prediv is changed **/ - - /** full update => bypass and reconfigure **/ - dprintk("PLL: New Setting for %d MHz Bandwidth (prediv: %d, ratio: %d)", bw/1000, state->cfg.pll->pll_prediv, state->cfg.pll->pll_ratio); - dib8000_write_word(state, 902, dib8000_read_word(state, 902) | (1<<3)); /* bypass PLL */ - dib8000_reset_pll(state); - dib8000_write_word(state, 898, 0x0004); /* sad */ - } else - ratio = state->cfg.pll->pll_ratio; - - state->current_demod_bw = bw; - } - - if (ratio != 0) { - /** ratio update => only change ratio **/ - dprintk("PLL: Update ratio (prediv: %d, ratio: %d)", state->cfg.pll->pll_prediv, ratio); - dib8000_write_word(state, 901, (state->cfg.pll->pll_prediv << 8) | (ratio << 0)); /* only the PLL ratio is updated. */ - } -} - return 0; + return 0; + } + return -EINVAL; } EXPORT_SYMBOL(dib8000_update_pll); @@ -985,7 +928,7 @@ static int dib8000_reset(struct dvb_frontend *fe) dib8000_set_power_mode(state, DIB8000_POWER_ALL); /* always leave the VBG voltage on - it consumes almost nothing but takes a long time to start */ - dib8000_set_adc_state(state, DIBX000_ADC_OFF); + dib8000_set_adc_state(state, DIBX000_VBG_ENABLE); /* restart all parts */ dib8000_write_word(state, 770, 0xffff); @@ -1049,11 +992,12 @@ static int dib8000_reset(struct dvb_frontend *fe) l = *n++; } } - + if (state->revision != 0x8090) + dib8000_write_word(state, 903, (0 << 4) | 2); state->isdbt_cfg_loaded = 0; //div_cfg override for special configs - if ((state->revision != 8090) && (state->cfg.div_cfg != 0)) + if (state->cfg.div_cfg != 0) dib8000_write_word(state, 903, state->cfg.div_cfg); /* unforce divstr regardless whether i2c enumeration was done or not */ @@ -1062,12 +1006,10 @@ static int dib8000_reset(struct dvb_frontend *fe) dib8000_set_bandwidth(fe, 6000); dib8000_set_adc_state(state, DIBX000_SLOW_ADC_ON); - dib8000_sad_calib(state); - if (state->revision != 0x8090) + if (state->revision != 0x8090) { + dib8000_sad_calib(state); dib8000_set_adc_state(state, DIBX000_SLOW_ADC_OFF); - - /* ber_rs_len = 3 */ - dib8000_write_word(state, 285, (dib8000_read_word(state, 285) & ~0x60) | (3 << 5)); + } dib8000_set_power_mode(state, DIB8000_POWER_INTERFACE_ONLY); @@ -1499,7 +1441,6 @@ static int dib8096p_set_output_mode(struct dvb_frontend *fe, int mode) u8 prefer_mpeg_mux_use = 1; int ret = 0; - state->output_mode = mode; dib8096p_host_bus_drive(state, 1); fifo_threshold = 1792; @@ -1938,637 +1879,782 @@ static const u16 adc_target_16dB[11] = { }; static const u8 permu_seg[] = { 6, 5, 7, 4, 8, 3, 9, 2, 10, 1, 11, 0, 12 }; -static u16 dib8000_set_layer(struct dib8000_state *state, u8 layer_index, u16 max_constellation) +static void dib8000_set_channel(struct dib8000_state *state, u8 seq, u8 autosearching) { - u8 cr, constellation, time_intlv; + u16 mode, max_constellation, seg_diff_mask = 0, nbseg_diff = 0; + u8 guard, crate, constellation, timeI; + u16 i, coeff[4], P_cfr_left_edge = 0, P_cfr_right_edge = 0, seg_mask13 = 0x1fff; // All 13 segments enabled + const s16 *ncoeff = NULL, *ana_fe; + u16 tmcc_pow = 0; + u16 coff_pow = 0x2800; + u16 init_prbs = 0xfff; + u16 ana_gain = 0; - switch (state->fe[0]->dtv_property_cache.layer[layer_index].modulation) { - case DQPSK: - constellation = 0; - break; - case QPSK: - constellation = 1; + if (state->revision == 0x8090) + dib8000_init_sdram(state); + + if (state->ber_monitored_layer != LAYER_ALL) + dib8000_write_word(state, 285, (dib8000_read_word(state, 285) & 0x60) | state->ber_monitored_layer); + else + dib8000_write_word(state, 285, dib8000_read_word(state, 285) & 0x60); + + i = dib8000_read_word(state, 26) & 1; // P_dds_invspec + dib8000_write_word(state, 26, state->fe[0]->dtv_property_cache.inversion^i); + + if (state->fe[0]->dtv_property_cache.isdbt_sb_mode) { + //compute new dds_freq for the seg and adjust prbs + int seg_offset = + state->fe[0]->dtv_property_cache.isdbt_sb_segment_idx - + (state->fe[0]->dtv_property_cache.isdbt_sb_segment_count / 2) - + (state->fe[0]->dtv_property_cache.isdbt_sb_segment_count % 2); + int clk = state->cfg.pll->internal; + u32 segtodds = ((u32) (430 << 23) / clk) << 3; // segtodds = SegBW / Fclk * pow(2,26) + int dds_offset = seg_offset * segtodds; + int new_dds, sub_channel; + if ((state->fe[0]->dtv_property_cache.isdbt_sb_segment_count % 2) == 0) + dds_offset -= (int)(segtodds / 2); + + if (state->cfg.pll->ifreq == 0) { + if ((state->fe[0]->dtv_property_cache.inversion ^ i) == 0) { + dib8000_write_word(state, 26, dib8000_read_word(state, 26) | 1); + new_dds = dds_offset; + } else + new_dds = dds_offset; + + // We shift tuning frequency if the wanted segment is : + // - the segment of center frequency with an odd total number of segments + // - the segment to the left of center frequency with an even total number of segments + // - the segment to the right of center frequency with an even total number of segments + if ((state->fe[0]->dtv_property_cache.delivery_system == SYS_ISDBT) + && (state->fe[0]->dtv_property_cache.isdbt_sb_mode == 1) + && (((state->fe[0]->dtv_property_cache.isdbt_sb_segment_count % 2) + && (state->fe[0]->dtv_property_cache.isdbt_sb_segment_idx == + ((state->fe[0]->dtv_property_cache.isdbt_sb_segment_count / 2) + 1))) + || (((state->fe[0]->dtv_property_cache.isdbt_sb_segment_count % 2) == 0) + && (state->fe[0]->dtv_property_cache.isdbt_sb_segment_idx == (state->fe[0]->dtv_property_cache.isdbt_sb_segment_count / 2))) + || (((state->fe[0]->dtv_property_cache.isdbt_sb_segment_count % 2) == 0) + && (state->fe[0]->dtv_property_cache.isdbt_sb_segment_idx == + ((state->fe[0]->dtv_property_cache.isdbt_sb_segment_count / 2) + 1))) + )) { + new_dds -= ((u32) (850 << 22) / clk) << 4; // new_dds = 850 (freq shift in KHz) / Fclk * pow(2,26) + } + } else { + if ((state->fe[0]->dtv_property_cache.inversion ^ i) == 0) + new_dds = state->cfg.pll->ifreq - dds_offset; + else + new_dds = state->cfg.pll->ifreq + dds_offset; + } + dib8000_write_word(state, 27, (u16) ((new_dds >> 16) & 0x01ff)); + dib8000_write_word(state, 28, (u16) (new_dds & 0xffff)); + if (state->fe[0]->dtv_property_cache.isdbt_sb_segment_count % 2) + sub_channel = ((state->fe[0]->dtv_property_cache.isdbt_sb_subchannel + (3 * seg_offset) + 1) % 41) / 3; + else + sub_channel = ((state->fe[0]->dtv_property_cache.isdbt_sb_subchannel + (3 * seg_offset)) % 41) / 3; + sub_channel -= 6; + + if (state->fe[0]->dtv_property_cache.transmission_mode == TRANSMISSION_MODE_2K + || state->fe[0]->dtv_property_cache.transmission_mode == TRANSMISSION_MODE_4K) { + dib8000_write_word(state, 219, dib8000_read_word(state, 219) | 0x1); //adp_pass =1 + dib8000_write_word(state, 190, dib8000_read_word(state, 190) | (0x1 << 14)); //pha3_force_pha_shift = 1 + } else { + dib8000_write_word(state, 219, dib8000_read_word(state, 219) & 0xfffe); //adp_pass =0 + dib8000_write_word(state, 190, dib8000_read_word(state, 190) & 0xbfff); //pha3_force_pha_shift = 0 + } + + switch (state->fe[0]->dtv_property_cache.transmission_mode) { + case TRANSMISSION_MODE_2K: + switch (sub_channel) { + case -6: + init_prbs = 0x0; + break; // 41, 0, 1 + case -5: + init_prbs = 0x423; + break; // 02~04 + case -4: + init_prbs = 0x9; + break; // 05~07 + case -3: + init_prbs = 0x5C7; + break; // 08~10 + case -2: + init_prbs = 0x7A6; + break; // 11~13 + case -1: + init_prbs = 0x3D8; + break; // 14~16 + case 0: + init_prbs = 0x527; + break; // 17~19 + case 1: + init_prbs = 0x7FF; + break; // 20~22 + case 2: + init_prbs = 0x79B; + break; // 23~25 + case 3: + init_prbs = 0x3D6; + break; // 26~28 + case 4: + init_prbs = 0x3A2; + break; // 29~31 + case 5: + init_prbs = 0x53B; + break; // 32~34 + case 6: + init_prbs = 0x2F4; + break; // 35~37 + default: + case 7: + init_prbs = 0x213; + break; // 38~40 + } break; - case QAM_16: - constellation = 2; + + case TRANSMISSION_MODE_4K: + switch (sub_channel) { + case -6: + init_prbs = 0x0; + break; // 41, 0, 1 + case -5: + init_prbs = 0x208; + break; // 02~04 + case -4: + init_prbs = 0xC3; + break; // 05~07 + case -3: + init_prbs = 0x7B9; + break; // 08~10 + case -2: + init_prbs = 0x423; + break; // 11~13 + case -1: + init_prbs = 0x5C7; + break; // 14~16 + case 0: + init_prbs = 0x3D8; + break; // 17~19 + case 1: + init_prbs = 0x7FF; + break; // 20~22 + case 2: + init_prbs = 0x3D6; + break; // 23~25 + case 3: + init_prbs = 0x53B; + break; // 26~28 + case 4: + init_prbs = 0x213; + break; // 29~31 + case 5: + init_prbs = 0x29; + break; // 32~34 + case 6: + init_prbs = 0xD0; + break; // 35~37 + default: + case 7: + init_prbs = 0x48E; + break; // 38~40 + } break; - case QAM_64: - default: - constellation = 3; + + default: + case TRANSMISSION_MODE_8K: + switch (sub_channel) { + case -6: + init_prbs = 0x0; + break; // 41, 0, 1 + case -5: + init_prbs = 0x740; + break; // 02~04 + case -4: + init_prbs = 0x069; + break; // 05~07 + case -3: + init_prbs = 0x7DD; + break; // 08~10 + case -2: + init_prbs = 0x208; + break; // 11~13 + case -1: + init_prbs = 0x7B9; + break; // 14~16 + case 0: + init_prbs = 0x5C7; + break; // 17~19 + case 1: + init_prbs = 0x7FF; + break; // 20~22 + case 2: + init_prbs = 0x53B; + break; // 23~25 + case 3: + init_prbs = 0x29; + break; // 26~28 + case 4: + init_prbs = 0x48E; + break; // 29~31 + case 5: + init_prbs = 0x4C4; + break; // 32~34 + case 6: + init_prbs = 0x367; + break; // 33~37 + default: + case 7: + init_prbs = 0x684; + break; // 38~40 + } break; + } + } else { + dib8000_write_word(state, 27, (u16) ((state->cfg.pll->ifreq >> 16) & 0x01ff)); + dib8000_write_word(state, 28, (u16) (state->cfg.pll->ifreq & 0xffff)); + dib8000_write_word(state, 26, (u16) ((state->cfg.pll->ifreq >> 25) & 0x0003)); } + /*P_mode == ?? */ + dib8000_write_word(state, 10, (seq << 4)); + // dib8000_write_word(state, 287, (dib8000_read_word(state, 287) & 0xe000) | 0x1000); - switch (state->fe[0]->dtv_property_cache.layer[layer_index].fec) { - case FEC_1_2: - cr = 1; - break; - case FEC_2_3: - cr = 2; - break; - case FEC_3_4: - cr = 3; - break; - case FEC_5_6: - cr = 5; - break; - case FEC_7_8: + switch (state->fe[0]->dtv_property_cache.guard_interval) { + case GUARD_INTERVAL_1_32: + guard = 0; + break; + case GUARD_INTERVAL_1_16: + guard = 1; + break; + case GUARD_INTERVAL_1_8: + guard = 2; + break; + case GUARD_INTERVAL_1_4: default: - cr = 7; - break; + guard = 3; + break; } - if ((state->fe[0]->dtv_property_cache.layer[layer_index].interleaving > 0) && ((state->fe[0]->dtv_property_cache.layer[layer_index].interleaving <= 3) || (state->fe[0]->dtv_property_cache.layer[layer_index].interleaving == 4 && state->fe[0]->dtv_property_cache.isdbt_sb_mode == 1))) - time_intlv = state->fe[0]->dtv_property_cache.layer[layer_index].interleaving; - else - time_intlv = 0; + dib8000_write_word(state, 1, (init_prbs << 2) | (guard & 0x3)); // ADDR 1 - dib8000_write_word(state, 2 + layer_index, (constellation << 10) | ((state->fe[0]->dtv_property_cache.layer[layer_index].segment_count & 0xf) << 6) | (cr << 3) | time_intlv); - if (state->fe[0]->dtv_property_cache.layer[layer_index].segment_count > 0) { - switch (max_constellation) { + max_constellation = DQPSK; + for (i = 0; i < 3; i++) { + switch (state->fe[0]->dtv_property_cache.layer[i].modulation) { case DQPSK: + constellation = 0; + break; case QPSK: - if (state->fe[0]->dtv_property_cache.layer[layer_index].modulation == QAM_16 || state->fe[0]->dtv_property_cache.layer[layer_index].modulation == QAM_64) - max_constellation = state->fe[0]->dtv_property_cache.layer[layer_index].modulation; - break; + constellation = 1; + break; case QAM_16: - if (state->fe[0]->dtv_property_cache.layer[layer_index].modulation == QAM_64) - max_constellation = state->fe[0]->dtv_property_cache.layer[layer_index].modulation; - break; + constellation = 2; + break; + case QAM_64: + default: + constellation = 3; + break; } - } - - return max_constellation; -} -static const u16 adp_Q64[4] = {0x0148, 0xfff0, 0x00a4, 0xfff8}; /* P_adp_regul_cnt 0.04, P_adp_noise_cnt -0.002, P_adp_regul_ext 0.02, P_adp_noise_ext -0.001 */ -static const u16 adp_Q16[4] = {0x023d, 0xffdf, 0x00a4, 0xfff0}; /* P_adp_regul_cnt 0.07, P_adp_noise_cnt -0.004, P_adp_regul_ext 0.02, P_adp_noise_ext -0.002 */ -static const u16 adp_Qdefault[4] = {0x099a, 0xffae, 0x0333, 0xfff8}; /* P_adp_regul_cnt 0.3, P_adp_noise_cnt -0.01, P_adp_regul_ext 0.1, P_adp_noise_ext -0.002 */ -static u16 dib8000_adp_fine_tune(struct dib8000_state *state, u16 max_constellation) -{ - u16 i, ana_gain = 0; - const u16 *adp; - - /* channel estimation fine configuration */ - switch (max_constellation) { - case QAM_64: - ana_gain = 0x7; - adp = &adp_Q64[0]; + switch (state->fe[0]->dtv_property_cache.layer[i].fec) { + case FEC_1_2: + crate = 1; break; - case QAM_16: - ana_gain = 0x7; - adp = &adp_Q16[0]; + case FEC_2_3: + crate = 2; break; - default: - ana_gain = 0; - adp = &adp_Qdefault[0]; + case FEC_3_4: + crate = 3; break; - } - - for (i = 0; i < 4; i++) - dib8000_write_word(state, 215 + i, adp[i]); - - return ana_gain; -} - -static void dib8000_update_ana_gain(struct dib8000_state *state, u16 ana_gain) -{ - u16 i; - - dib8000_write_word(state, 116, ana_gain); - - /* update ADC target depending on ana_gain */ - if (ana_gain) { /* set -16dB ADC target for ana_gain=-1 */ - for (i = 0; i < 10; i++) - dib8000_write_word(state, 80 + i, adc_target_16dB[i]); - } else { /* set -22dB ADC target for ana_gain=0 */ - for (i = 0; i < 10; i++) - dib8000_write_word(state, 80 + i, adc_target_16dB[i] - 355); - } -} - -static void dib8000_load_ana_fe_coefs(struct dib8000_state *state, const s16 *ana_fe) -{ - u16 mode = 0; - - if (state->isdbt_cfg_loaded == 0) - for (mode = 0; mode < 24; mode++) - dib8000_write_word(state, 117 + mode, ana_fe[mode]); -} - -static const u16 lut_prbs_2k[14] = { - 0, 0x423, 0x009, 0x5C7, 0x7A6, 0x3D8, 0x527, 0x7FF, 0x79B, 0x3D6, 0x3A2, 0x53B, 0x2F4, 0x213 -}; -static const u16 lut_prbs_4k[14] = { - 0, 0x208, 0x0C3, 0x7B9, 0x423, 0x5C7, 0x3D8, 0x7FF, 0x3D6, 0x53B, 0x213, 0x029, 0x0D0, 0x48E -}; -static const u16 lut_prbs_8k[14] = { - 0, 0x740, 0x069, 0x7DD, 0x208, 0x7B9, 0x5C7, 0x7FF, 0x53B, 0x029, 0x48E, 0x4C4, 0x367, 0x684 -}; - -static u16 dib8000_get_init_prbs(struct dib8000_state *state, u16 subchannel) -{ - int sub_channel_prbs_group = 0; - - sub_channel_prbs_group = (subchannel / 3) + 1; - dprintk("sub_channel_prbs_group = %d , subchannel =%d prbs = 0x%04x", sub_channel_prbs_group, subchannel, lut_prbs_8k[sub_channel_prbs_group]); + case FEC_5_6: + crate = 5; + break; + case FEC_7_8: + default: + crate = 7; + break; + } - switch (state->fe[0]->dtv_property_cache.transmission_mode) { - case TRANSMISSION_MODE_2K: - return lut_prbs_2k[sub_channel_prbs_group]; - case TRANSMISSION_MODE_4K: - return lut_prbs_4k[sub_channel_prbs_group]; - default: - case TRANSMISSION_MODE_8K: - return lut_prbs_8k[sub_channel_prbs_group]; + if ((state->fe[0]->dtv_property_cache.layer[i].interleaving > 0) && + ((state->fe[0]->dtv_property_cache.layer[i].interleaving <= 3) || + (state->fe[0]->dtv_property_cache.layer[i].interleaving == 4 && state->fe[0]->dtv_property_cache.isdbt_sb_mode == 1)) + ) + timeI = state->fe[0]->dtv_property_cache.layer[i].interleaving; + else + timeI = 0; + dib8000_write_word(state, 2 + i, (constellation << 10) | ((state->fe[0]->dtv_property_cache.layer[i].segment_count & 0xf) << 6) | + (crate << 3) | timeI); + if (state->fe[0]->dtv_property_cache.layer[i].segment_count > 0) { + switch (max_constellation) { + case DQPSK: + case QPSK: + if (state->fe[0]->dtv_property_cache.layer[i].modulation == QAM_16 || + state->fe[0]->dtv_property_cache.layer[i].modulation == QAM_64) + max_constellation = state->fe[0]->dtv_property_cache.layer[i].modulation; + break; + case QAM_16: + if (state->fe[0]->dtv_property_cache.layer[i].modulation == QAM_64) + max_constellation = state->fe[0]->dtv_property_cache.layer[i].modulation; + break; + } + } } -} -static void dib8000_set_13seg_channel(struct dib8000_state *state) -{ - u16 i; - u16 coff_pow = 0x2800; + mode = fft_to_mode(state); - state->seg_mask = 0x1fff; /* All 13 segments enabled */ + //dib8000_write_word(state, 5, 13); /*p_last_seg = 13*/ - /* ---- COFF ---- Carloff, the most robust --- */ - if (state->isdbt_cfg_loaded == 0) { /* if not Sound Broadcasting mode : put default values for 13 segments */ - dib8000_write_word(state, 180, (16 << 6) | 9); - dib8000_write_word(state, 187, (4 << 12) | (8 << 5) | 0x2); - coff_pow = 0x2800; - for (i = 0; i < 6; i++) - dib8000_write_word(state, 181+i, coff_pow); + dib8000_write_word(state, 274, (dib8000_read_word(state, 274) & 0xffcf) | + ((state->fe[0]->dtv_property_cache.isdbt_partial_reception & 1) << 5) | ((state->fe[0]->dtv_property_cache. + isdbt_sb_mode & 1) << 4)); - /* P_ctrl_corm_thres4pre_freq_inh=1, P_ctrl_pre_freq_mode_sat=1 */ - /* P_ctrl_pre_freq_mode_sat=1, P_ctrl_pre_freq_inh=0, P_ctrl_pre_freq_step = 3, P_pre_freq_win_len=1 */ - dib8000_write_word(state, 338, (1 << 12) | (1 << 10) | (0 << 9) | (3 << 5) | 1); + dprintk("mode = %d ; guard = %d", mode, state->fe[0]->dtv_property_cache.guard_interval); - /* P_ctrl_pre_freq_win_len=8, P_ctrl_pre_freq_thres_lockin=6 */ - dib8000_write_word(state, 340, (8 << 6) | (6 << 0)); - /* P_ctrl_pre_freq_thres_lockout=4, P_small_use_tmcc/ac/cp=1 */ - dib8000_write_word(state, 341, (4 << 3) | (1 << 2) | (1 << 1) | (1 << 0)); + /* signal optimization parameter */ - dib8000_write_word(state, 228, 0); /* default value */ - dib8000_write_word(state, 265, 31); /* default value */ - dib8000_write_word(state, 205, 0x200f); /* init value */ + if (state->fe[0]->dtv_property_cache.isdbt_partial_reception) { + seg_diff_mask = (state->fe[0]->dtv_property_cache.layer[0].modulation == DQPSK) << permu_seg[0]; + for (i = 1; i < 3; i++) + nbseg_diff += + (state->fe[0]->dtv_property_cache.layer[i].modulation == DQPSK) * state->fe[0]->dtv_property_cache.layer[i].segment_count; + for (i = 0; i < nbseg_diff; i++) + seg_diff_mask |= 1 << permu_seg[i + 1]; + } else { + for (i = 0; i < 3; i++) + nbseg_diff += + (state->fe[0]->dtv_property_cache.layer[i].modulation == DQPSK) * state->fe[0]->dtv_property_cache.layer[i].segment_count; + for (i = 0; i < nbseg_diff; i++) + seg_diff_mask |= 1 << permu_seg[i]; } + dprintk("nbseg_diff = %X (%d)", seg_diff_mask, seg_diff_mask); - /* - * make the cpil_coff_lock more robust but slower p_coff_winlen - * 6bits; p_coff_thres_lock 6bits (for coff lock if needed) - */ - - if (state->cfg.pll->ifreq == 0) - dib8000_write_word(state, 266, ~state->seg_mask | state->seg_diff_mask | 0x40); /* P_equal_noise_seg_inh */ + state->differential_constellation = (seg_diff_mask != 0); + if (state->revision != 0x8090) + dib8000_set_diversity_in(state->fe[0], state->diversity_onoff); + else + dib8096p_set_diversity_in(state->fe[0], state->diversity_onoff); - dib8000_load_ana_fe_coefs(state, ana_fe_coeff_13seg); -} + if (state->fe[0]->dtv_property_cache.isdbt_sb_mode == 1) { + if (state->fe[0]->dtv_property_cache.isdbt_partial_reception == 1) + seg_mask13 = 0x00E0; + else // 1-segment + seg_mask13 = 0x0040; + } else + seg_mask13 = 0x1fff; -static void dib8000_set_subchannel_prbs(struct dib8000_state *state, u16 init_prbs) -{ - u16 reg_1; + // WRITE: Mode & Diff mask + dib8000_write_word(state, 0, (mode << 13) | seg_diff_mask); - reg_1 = dib8000_read_word(state, 1); - dib8000_write_word(state, 1, (init_prbs << 2) | (reg_1 & 0x3)); /* ADDR 1 */ -} + if ((seg_diff_mask) || (state->fe[0]->dtv_property_cache.isdbt_sb_mode)) + dib8000_write_word(state, 268, (dib8000_read_word(state, 268) & 0xF9FF) | 0x0200); + else + dib8000_write_word(state, 268, (2 << 9) | 39); //init value -static void dib8000_small_fine_tune(struct dib8000_state *state) -{ - u16 i; - const s16 *ncoeff; + // ---- SMALL ---- + // P_small_seg_diff + dib8000_write_word(state, 352, seg_diff_mask); // ADDR 352 - dib8000_write_word(state, 352, state->seg_diff_mask); - dib8000_write_word(state, 353, state->seg_mask); + dib8000_write_word(state, 353, seg_mask13); // ADDR 353 - /* P_small_coef_ext_enable=ISDB-Tsb, P_small_narrow_band=ISDB-Tsb, P_small_last_seg=13, P_small_offset_num_car=5 */ - dib8000_write_word(state, 351, (state->fe[0]->dtv_property_cache.isdbt_sb_mode << 9) | (state->fe[0]->dtv_property_cache.isdbt_sb_mode << 8) | (13 << 4) | 5); +/* // P_small_narrow_band=0, P_small_last_seg=13, P_small_offset_num_car=5 */ - if (state->fe[0]->dtv_property_cache.isdbt_sb_mode) { - /* ---- SMALL ---- */ + // ---- SMALL ---- + if (state->fe[0]->dtv_property_cache.isdbt_sb_mode == 1) { switch (state->fe[0]->dtv_property_cache.transmission_mode) { case TRANSMISSION_MODE_2K: - if (state->fe[0]->dtv_property_cache.isdbt_partial_reception == 0) { /* 1-seg */ - if (state->fe[0]->dtv_property_cache.layer[0].modulation == DQPSK) /* DQPSK */ - ncoeff = coeff_2k_sb_1seg_dqpsk; - else /* QPSK or QAM */ - ncoeff = coeff_2k_sb_1seg; - } else { /* 3-segments */ - if (state->fe[0]->dtv_property_cache.layer[0].modulation == DQPSK) { /* DQPSK on central segment */ - if (state->fe[0]->dtv_property_cache.layer[1].modulation == DQPSK) /* DQPSK on external segments */ - ncoeff = coeff_2k_sb_3seg_0dqpsk_1dqpsk; - else /* QPSK or QAM on external segments */ - ncoeff = coeff_2k_sb_3seg_0dqpsk; - } else { /* QPSK or QAM on central segment */ - if (state->fe[0]->dtv_property_cache.layer[1].modulation == DQPSK) /* DQPSK on external segments */ - ncoeff = coeff_2k_sb_3seg_1dqpsk; - else /* QPSK or QAM on external segments */ - ncoeff = coeff_2k_sb_3seg; - } + if (state->fe[0]->dtv_property_cache.isdbt_partial_reception == 0) { + if (state->fe[0]->dtv_property_cache.layer[0].modulation == DQPSK) + ncoeff = coeff_2k_sb_1seg_dqpsk; + else // QPSK or QAM + ncoeff = coeff_2k_sb_1seg; + } else { // 3-segments + if (state->fe[0]->dtv_property_cache.layer[0].modulation == DQPSK) { + if (state->fe[0]->dtv_property_cache.layer[1].modulation == DQPSK) + ncoeff = coeff_2k_sb_3seg_0dqpsk_1dqpsk; + else // QPSK or QAM on external segments + ncoeff = coeff_2k_sb_3seg_0dqpsk; + } else { // QPSK or QAM on central segment + if (state->fe[0]->dtv_property_cache.layer[1].modulation == DQPSK) + ncoeff = coeff_2k_sb_3seg_1dqpsk; + else // QPSK or QAM on external segments + ncoeff = coeff_2k_sb_3seg; } - break; + } + break; + case TRANSMISSION_MODE_4K: - if (state->fe[0]->dtv_property_cache.isdbt_partial_reception == 0) { /* 1-seg */ - if (state->fe[0]->dtv_property_cache.layer[0].modulation == DQPSK) /* DQPSK */ - ncoeff = coeff_4k_sb_1seg_dqpsk; - else /* QPSK or QAM */ - ncoeff = coeff_4k_sb_1seg; - } else { /* 3-segments */ - if (state->fe[0]->dtv_property_cache.layer[0].modulation == DQPSK) { /* DQPSK on central segment */ - if (state->fe[0]->dtv_property_cache.layer[1].modulation == DQPSK) /* DQPSK on external segments */ - ncoeff = coeff_4k_sb_3seg_0dqpsk_1dqpsk; - else /* QPSK or QAM on external segments */ - ncoeff = coeff_4k_sb_3seg_0dqpsk; - } else { /* QPSK or QAM on central segment */ - if (state->fe[0]->dtv_property_cache.layer[1].modulation == DQPSK) /* DQPSK on external segments */ - ncoeff = coeff_4k_sb_3seg_1dqpsk; - else /* QPSK or QAM on external segments */ - ncoeff = coeff_4k_sb_3seg; + if (state->fe[0]->dtv_property_cache.isdbt_partial_reception == 0) { + if (state->fe[0]->dtv_property_cache.layer[0].modulation == DQPSK) + ncoeff = coeff_4k_sb_1seg_dqpsk; + else // QPSK or QAM + ncoeff = coeff_4k_sb_1seg; + } else { // 3-segments + if (state->fe[0]->dtv_property_cache.layer[0].modulation == DQPSK) { + if (state->fe[0]->dtv_property_cache.layer[1].modulation == DQPSK) { + ncoeff = coeff_4k_sb_3seg_0dqpsk_1dqpsk; + } else { // QPSK or QAM on external segments + ncoeff = coeff_4k_sb_3seg_0dqpsk; } + } else { // QPSK or QAM on central segment + if (state->fe[0]->dtv_property_cache.layer[1].modulation == DQPSK) { + ncoeff = coeff_4k_sb_3seg_1dqpsk; + } else // QPSK or QAM on external segments + ncoeff = coeff_4k_sb_3seg; } - break; + } + break; + case TRANSMISSION_MODE_AUTO: case TRANSMISSION_MODE_8K: default: - if (state->fe[0]->dtv_property_cache.isdbt_partial_reception == 0) { /* 1-seg */ - if (state->fe[0]->dtv_property_cache.layer[0].modulation == DQPSK) /* DQPSK */ - ncoeff = coeff_8k_sb_1seg_dqpsk; - else /* QPSK or QAM */ - ncoeff = coeff_8k_sb_1seg; - } else { /* 3-segments */ - if (state->fe[0]->dtv_property_cache.layer[0].modulation == DQPSK) { /* DQPSK on central segment */ - if (state->fe[0]->dtv_property_cache.layer[1].modulation == DQPSK) /* DQPSK on external segments */ - ncoeff = coeff_8k_sb_3seg_0dqpsk_1dqpsk; - else /* QPSK or QAM on external segments */ - ncoeff = coeff_8k_sb_3seg_0dqpsk; - } else { /* QPSK or QAM on central segment */ - if (state->fe[0]->dtv_property_cache.layer[1].modulation == DQPSK) /* DQPSK on external segments */ - ncoeff = coeff_8k_sb_3seg_1dqpsk; - else /* QPSK or QAM on external segments */ - ncoeff = coeff_8k_sb_3seg; + if (state->fe[0]->dtv_property_cache.isdbt_partial_reception == 0) { + if (state->fe[0]->dtv_property_cache.layer[0].modulation == DQPSK) + ncoeff = coeff_8k_sb_1seg_dqpsk; + else // QPSK or QAM + ncoeff = coeff_8k_sb_1seg; + } else { // 3-segments + if (state->fe[0]->dtv_property_cache.layer[0].modulation == DQPSK) { + if (state->fe[0]->dtv_property_cache.layer[1].modulation == DQPSK) { + ncoeff = coeff_8k_sb_3seg_0dqpsk_1dqpsk; + } else { // QPSK or QAM on external segments + ncoeff = coeff_8k_sb_3seg_0dqpsk; } + } else { // QPSK or QAM on central segment + if (state->fe[0]->dtv_property_cache.layer[1].modulation == DQPSK) { + ncoeff = coeff_8k_sb_3seg_1dqpsk; + } else // QPSK or QAM on external segments + ncoeff = coeff_8k_sb_3seg; } - break; + } + break; } - for (i = 0; i < 8; i++) dib8000_write_word(state, 343 + i, ncoeff[i]); } -} - -static const u16 coff_thres_1seg[3] = {300, 150, 80}; -static const u16 coff_thres_3seg[3] = {350, 300, 250}; -static void dib8000_set_sb_channel(struct dib8000_state *state) -{ - const u16 *coff; - u16 i; - - if (state->fe[0]->dtv_property_cache.transmission_mode == TRANSMISSION_MODE_2K || state->fe[0]->dtv_property_cache.transmission_mode == TRANSMISSION_MODE_4K) { - dib8000_write_word(state, 219, dib8000_read_word(state, 219) | 0x1); /* adp_pass =1 */ - dib8000_write_word(state, 190, dib8000_read_word(state, 190) | (0x1 << 14)); /* pha3_force_pha_shift = 1 */ - } else { - dib8000_write_word(state, 219, dib8000_read_word(state, 219) & 0xfffe); /* adp_pass =0 */ - dib8000_write_word(state, 190, dib8000_read_word(state, 190) & 0xbfff); /* pha3_force_pha_shift = 0 */ - } - if (state->fe[0]->dtv_property_cache.isdbt_partial_reception == 1) /* 3-segments */ - state->seg_mask = 0x00E0; - else /* 1-segment */ - state->seg_mask = 0x0040; + // P_small_coef_ext_enable=ISDB-Tsb, P_small_narrow_band=ISDB-Tsb, P_small_last_seg=13, P_small_offset_num_car=5 + dib8000_write_word(state, 351, + (state->fe[0]->dtv_property_cache.isdbt_sb_mode << 9) | (state->fe[0]->dtv_property_cache.isdbt_sb_mode << 8) | (13 << 4) | 5); - dib8000_write_word(state, 268, (dib8000_read_word(state, 268) & 0xF9FF) | 0x0200); + // ---- COFF ---- + // Carloff, the most robust + if (state->fe[0]->dtv_property_cache.isdbt_sb_mode == 1) { - /* ---- COFF ---- Carloff, the most robust --- */ - /* P_coff_cpil_alpha=4, P_coff_inh=0, P_coff_cpil_winlen=64, P_coff_narrow_band=1, P_coff_square_val=1, P_coff_one_seg=~partial_rcpt, P_coff_use_tmcc=1, P_coff_use_ac=1 */ - dib8000_write_word(state, 187, (4 << 12) | (0 << 11) | (63 << 5) | (0x3 << 3) | ((~state->fe[0]->dtv_property_cache.isdbt_partial_reception & 1) << 2) | 0x3); + // P_coff_cpil_alpha=4, P_coff_inh=0, P_coff_cpil_winlen=64 + // P_coff_narrow_band=1, P_coff_square_val=1, P_coff_one_seg=~partial_rcpt, P_coff_use_tmcc=1, P_coff_use_ac=1 + dib8000_write_word(state, 187, + (4 << 12) | (0 << 11) | (63 << 5) | (0x3 << 3) | ((~state->fe[0]->dtv_property_cache.isdbt_partial_reception & 1) << 2) + | 0x3); - dib8000_write_word(state, 340, (16 << 6) | (8 << 0)); /* P_ctrl_pre_freq_win_len=16, P_ctrl_pre_freq_thres_lockin=8 */ - dib8000_write_word(state, 341, (6 << 3) | (1 << 2) | (1 << 1) | (1 << 0));/* P_ctrl_pre_freq_thres_lockout=6, P_small_use_tmcc/ac/cp=1 */ +/* // P_small_coef_ext_enable = 1 */ +/* dib8000_write_word(state, 351, dib8000_read_word(state, 351) | 0x200); */ - /* Sound Broadcasting mode 1 seg */ - if (state->fe[0]->dtv_property_cache.isdbt_partial_reception == 0) { - /* P_coff_winlen=63, P_coff_thres_lock=15, P_coff_one_seg_width = (P_mode == 3) , P_coff_one_seg_sym = (P_mode-1) */ - if (state->mode == 3) - dib8000_write_word(state, 180, 0x1fcf | ((state->mode - 1) << 14)); - else - dib8000_write_word(state, 180, 0x0fcf | ((state->mode - 1) << 14)); + if (state->fe[0]->dtv_property_cache.isdbt_partial_reception == 0) { - /* P_ctrl_corm_thres4pre_freq_inh=1,P_ctrl_pre_freq_mode_sat=1, P_ctrl_pre_freq_inh=0, P_ctrl_pre_freq_step = 5, P_pre_freq_win_len=4 */ - dib8000_write_word(state, 338, (1 << 12) | (1 << 10) | (0 << 9) | (5 << 5) | 4); - coff = &coff_thres_1seg[0]; - } else { /* Sound Broadcasting mode 3 seg */ - dib8000_write_word(state, 180, 0x1fcf | (1 << 14)); - /* P_ctrl_corm_thres4pre_freq_inh = 1, P_ctrl_pre_freq_mode_sat=1, P_ctrl_pre_freq_inh=0, P_ctrl_pre_freq_step = 4, P_pre_freq_win_len=4 */ - dib8000_write_word(state, 338, (1 << 12) | (1 << 10) | (0 << 9) | (4 << 5) | 4); - coff = &coff_thres_3seg[0]; - } + // P_coff_winlen=63, P_coff_thres_lock=15, P_coff_one_seg_width= (P_mode == 3) , P_coff_one_seg_sym= (P_mode-1) + if (mode == 3) + dib8000_write_word(state, 180, 0x1fcf | ((mode - 1) << 14)); + else + dib8000_write_word(state, 180, 0x0fcf | ((mode - 1) << 14)); + // P_ctrl_corm_thres4pre_freq_inh=1,P_ctrl_pre_freq_mode_sat=1, + // P_ctrl_pre_freq_inh=0, P_ctrl_pre_freq_step = 5, P_pre_freq_win_len=4 + dib8000_write_word(state, 338, (1 << 12) | (1 << 10) | (0 << 9) | (5 << 5) | 4); + // P_ctrl_pre_freq_win_len=16, P_ctrl_pre_freq_thres_lockin=8 + dib8000_write_word(state, 340, (16 << 6) | (8 << 0)); + // P_ctrl_pre_freq_thres_lockout=6, P_small_use_tmcc/ac/cp=1 + dib8000_write_word(state, 341, (6 << 3) | (1 << 2) | (1 << 1) | (1 << 0)); + + // P_coff_corthres_8k, 4k, 2k and P_coff_cpilthres_8k, 4k, 2k + dib8000_write_word(state, 181, 300); + dib8000_write_word(state, 182, 150); + dib8000_write_word(state, 183, 80); + dib8000_write_word(state, 184, 300); + dib8000_write_word(state, 185, 150); + dib8000_write_word(state, 186, 80); + } else { // Sound Broadcasting mode 3 seg + // P_coff_one_seg_sym= 1, P_coff_one_seg_width= 1, P_coff_winlen=63, P_coff_thres_lock=15 + /* if (mode == 3) */ + /* dib8000_write_word(state, 180, 0x2fca | ((0) << 14)); */ + /* else */ + /* dib8000_write_word(state, 180, 0x2fca | ((1) << 14)); */ + dib8000_write_word(state, 180, 0x1fcf | (1 << 14)); + + // P_ctrl_corm_thres4pre_freq_inh = 1, P_ctrl_pre_freq_mode_sat=1, + // P_ctrl_pre_freq_inh=0, P_ctrl_pre_freq_step = 4, P_pre_freq_win_len=4 + dib8000_write_word(state, 338, (1 << 12) | (1 << 10) | (0 << 9) | (4 << 5) | 4); + // P_ctrl_pre_freq_win_len=16, P_ctrl_pre_freq_thres_lockin=8 + dib8000_write_word(state, 340, (16 << 6) | (8 << 0)); + //P_ctrl_pre_freq_thres_lockout=6, P_small_use_tmcc/ac/cp=1 + dib8000_write_word(state, 341, (6 << 3) | (1 << 2) | (1 << 1) | (1 << 0)); + + // P_coff_corthres_8k, 4k, 2k and P_coff_cpilthres_8k, 4k, 2k + dib8000_write_word(state, 181, 350); + dib8000_write_word(state, 182, 300); + dib8000_write_word(state, 183, 250); + dib8000_write_word(state, 184, 350); + dib8000_write_word(state, 185, 300); + dib8000_write_word(state, 186, 250); + } - dib8000_write_word(state, 228, 1); /* P_2d_mode_byp=1 */ - dib8000_write_word(state, 205, dib8000_read_word(state, 205) & 0xfff0); /* P_cspu_win_cut = 0 */ + } else if (state->isdbt_cfg_loaded == 0) { // if not Sound Broadcasting mode : put default values for 13 segments + dib8000_write_word(state, 180, (16 << 6) | 9); + dib8000_write_word(state, 187, (4 << 12) | (8 << 5) | 0x2); + coff_pow = 0x2800; + for (i = 0; i < 6; i++) + dib8000_write_word(state, 181 + i, coff_pow); - if (state->fe[0]->dtv_property_cache.isdbt_partial_reception == 0 && state->fe[0]->dtv_property_cache.transmission_mode == TRANSMISSION_MODE_2K) - dib8000_write_word(state, 265, 15); /* P_equal_noise_sel = 15 */ + // P_ctrl_corm_thres4pre_freq_inh=1, P_ctrl_pre_freq_mode_sat=1, + // P_ctrl_pre_freq_mode_sat=1, P_ctrl_pre_freq_inh=0, P_ctrl_pre_freq_step = 3, P_pre_freq_win_len=1 + dib8000_write_word(state, 338, (1 << 12) | (1 << 10) | (0 << 9) | (3 << 5) | 1); - /* Write COFF thres */ - for (i = 0 ; i < 3; i++) { - dib8000_write_word(state, 181+i, coff[i]); - dib8000_write_word(state, 184+i, coff[i]); + // P_ctrl_pre_freq_win_len=8, P_ctrl_pre_freq_thres_lockin=6 + dib8000_write_word(state, 340, (8 << 6) | (6 << 0)); + // P_ctrl_pre_freq_thres_lockout=4, P_small_use_tmcc/ac/cp=1 + dib8000_write_word(state, 341, (4 << 3) | (1 << 2) | (1 << 1) | (1 << 0)); } + // ---- FFT ---- + if (state->fe[0]->dtv_property_cache.isdbt_sb_mode == 1 && state->fe[0]->dtv_property_cache.isdbt_partial_reception == 0) + dib8000_write_word(state, 178, 64); // P_fft_powrange=64 + else + dib8000_write_word(state, 178, 32); // P_fft_powrange=32 - /* - * make the cpil_coff_lock more robust but slower p_coff_winlen + /* make the cpil_coff_lock more robust but slower p_coff_winlen * 6bits; p_coff_thres_lock 6bits (for coff lock if needed) */ - - dib8000_write_word(state, 266, ~state->seg_mask | state->seg_diff_mask); /* P_equal_noise_seg_inh */ - - if (state->fe[0]->dtv_property_cache.isdbt_partial_reception == 0) - dib8000_write_word(state, 178, 64); /* P_fft_powrange = 64 */ + /* if ( ( nbseg_diff>0)&&(nbseg_diff<13)) + dib8000_write_word(state, 187, (dib8000_read_word(state, 187) & 0xfffb) | (1 << 3)); */ + + dib8000_write_word(state, 189, ~seg_mask13 | seg_diff_mask); /* P_lmod4_seg_inh */ + dib8000_write_word(state, 192, ~seg_mask13 | seg_diff_mask); /* P_pha3_seg_inh */ + dib8000_write_word(state, 225, ~seg_mask13 | seg_diff_mask); /* P_tac_seg_inh */ + if ((!state->fe[0]->dtv_property_cache.isdbt_sb_mode) && (state->cfg.pll->ifreq == 0)) + dib8000_write_word(state, 266, ~seg_mask13 | seg_diff_mask | 0x40); /* P_equal_noise_seg_inh */ else - dib8000_write_word(state, 178, 32); /* P_fft_powrange = 32 */ -} - -static void dib8000_set_isdbt_common_channel(struct dib8000_state *state, u8 seq, u8 autosearching) -{ - u16 p_cfr_left_edge = 0, p_cfr_right_edge = 0; - u16 tmcc_pow = 0, ana_gain = 0, tmp = 0, i = 0, nbseg_diff = 0 ; - u16 max_constellation = DQPSK; - int init_prbs; - - /* P_mode */ - dib8000_write_word(state, 10, (seq << 4)); - - /* init mode */ - state->mode = fft_to_mode(state); - - /* set guard */ - tmp = dib8000_read_word(state, 1); - dib8000_write_word(state, 1, (tmp&0xfffc) | (state->fe[0]->dtv_property_cache.guard_interval & 0x3)); - - dib8000_write_word(state, 274, (dib8000_read_word(state, 274) & 0xffcf) | ((state->fe[0]->dtv_property_cache.isdbt_partial_reception & 1) << 5) | ((state->fe[0]->dtv_property_cache.isdbt_sb_mode & 1) << 4)); - - /* signal optimization parameter */ - if (state->fe[0]->dtv_property_cache.isdbt_partial_reception) { - state->seg_diff_mask = (state->fe[0]->dtv_property_cache.layer[0].modulation == DQPSK) << permu_seg[0]; - for (i = 1; i < 3; i++) - nbseg_diff += (state->fe[0]->dtv_property_cache.layer[i].modulation == DQPSK) * state->fe[0]->dtv_property_cache.layer[i].segment_count; - for (i = 0; i < nbseg_diff; i++) - state->seg_diff_mask |= 1 << permu_seg[i+1]; - } else { - for (i = 0; i < 3; i++) - nbseg_diff += (state->fe[0]->dtv_property_cache.layer[i].modulation == DQPSK) * state->fe[0]->dtv_property_cache.layer[i].segment_count; - for (i = 0; i < nbseg_diff; i++) - state->seg_diff_mask |= 1 << permu_seg[i]; - } - - if (state->seg_diff_mask) - dib8000_write_word(state, 268, (dib8000_read_word(state, 268) & 0xF9FF) | 0x0200); + dib8000_write_word(state, 266, ~seg_mask13 | seg_diff_mask); /* P_equal_noise_seg_inh */ + dib8000_write_word(state, 287, ~seg_mask13 | 0x1000); /* P_tmcc_seg_inh */ + //dib8000_write_word(state, 288, ~seg_mask13 | seg_diff_mask); /* P_tmcc_seg_eq_inh */ + if (!autosearching) + dib8000_write_word(state, 288, (~seg_mask13 | seg_diff_mask) & 0x1fff); /* P_tmcc_seg_eq_inh */ else - dib8000_write_word(state, 268, (2 << 9) | 39); /*init value */ - - for (i = 0; i < 3; i++) - max_constellation = dib8000_set_layer(state, i, max_constellation); - if (autosearching == 0) { - state->layer_b_nb_seg = state->fe[0]->dtv_property_cache.layer[1].segment_count; - state->layer_c_nb_seg = state->fe[0]->dtv_property_cache.layer[2].segment_count; - } + dib8000_write_word(state, 288, 0x1fff); //disable equalisation of the tmcc when autosearch to be able to find the DQPSK channels. + dprintk("287 = %X (%d)", ~seg_mask13 | 0x1000, ~seg_mask13 | 0x1000); + + dib8000_write_word(state, 211, seg_mask13 & (~seg_diff_mask)); /* P_des_seg_enabled */ + + /* offset loop parameters */ + if (state->fe[0]->dtv_property_cache.isdbt_sb_mode == 1) { + if (state->fe[0]->dtv_property_cache.isdbt_partial_reception == 0) + /* P_timf_alpha = (11-P_mode), P_corm_alpha=6, P_corm_thres=0x80 */ + dib8000_write_word(state, 32, ((11 - mode) << 12) | (6 << 8) | 0x40); + + else // Sound Broadcasting mode 3 seg + /* P_timf_alpha = (10-P_mode), P_corm_alpha=6, P_corm_thres=0x80 */ + dib8000_write_word(state, 32, ((10 - mode) << 12) | (6 << 8) | 0x60); + } else + // TODO in 13 seg, timf_alpha can always be the same or not ? + /* P_timf_alpha = (9-P_mode, P_corm_alpha=6, P_corm_thres=0x80 */ + dib8000_write_word(state, 32, ((9 - mode) << 12) | (6 << 8) | 0x80); + + if (state->fe[0]->dtv_property_cache.isdbt_sb_mode == 1) { + if (state->fe[0]->dtv_property_cache.isdbt_partial_reception == 0) + /* P_ctrl_pha_off_max=3 P_ctrl_sfreq_inh =0 P_ctrl_sfreq_step = (11-P_mode) */ + dib8000_write_word(state, 37, (3 << 5) | (0 << 4) | (10 - mode)); + + else // Sound Broadcasting mode 3 seg + /* P_ctrl_pha_off_max=3 P_ctrl_sfreq_inh =0 P_ctrl_sfreq_step = (10-P_mode) */ + dib8000_write_word(state, 37, (3 << 5) | (0 << 4) | (9 - mode)); + } else + /* P_ctrl_pha_off_max=3 P_ctrl_sfreq_inh =0 P_ctrl_sfreq_step = 9 */ + dib8000_write_word(state, 37, (3 << 5) | (0 << 4) | (8 - mode)); - /* WRITE: Mode & Diff mask */ - dib8000_write_word(state, 0, (state->mode << 13) | state->seg_diff_mask); - - state->differential_constellation = (state->seg_diff_mask != 0); - - /* channel estimation fine configuration */ - ana_gain = dib8000_adp_fine_tune(state, max_constellation); - - /* update ana_gain depending on max constellation */ - dib8000_update_ana_gain(state, ana_gain); - - /* ---- ANA_FE ---- */ - if (state->fe[0]->dtv_property_cache.isdbt_partial_reception) /* 3-segments */ - dib8000_load_ana_fe_coefs(state, ana_fe_coeff_3seg); + /* P_dvsy_sync_wait - reuse mode */ + switch (state->fe[0]->dtv_property_cache.transmission_mode) { + case TRANSMISSION_MODE_8K: + mode = 256; + break; + case TRANSMISSION_MODE_4K: + mode = 128; + break; + default: + case TRANSMISSION_MODE_2K: + mode = 64; + break; + } + if (state->cfg.diversity_delay == 0) + mode = (mode * (1 << (guard)) * 3) / 2 + 48; // add 50% SFN margin + compensate for one DVSY-fifo else - dib8000_load_ana_fe_coefs(state, ana_fe_coeff_1seg); /* 1-segment */ + mode = (mode * (1 << (guard)) * 3) / 2 + state->cfg.diversity_delay; // add 50% SFN margin + compensate for DVSY-fifo + mode <<= 4; + dib8000_write_word(state, 273, (dib8000_read_word(state, 273) & 0x000f) | mode); - /* TSB or ISDBT ? apply it now */ - if (state->fe[0]->dtv_property_cache.isdbt_sb_mode) { - dib8000_set_sb_channel(state); - if (state->fe[0]->dtv_property_cache.isdbt_sb_subchannel != -1) - init_prbs = dib8000_get_init_prbs(state, state->fe[0]->dtv_property_cache.isdbt_sb_subchannel); - else - init_prbs = 0; - } else { - dib8000_set_13seg_channel(state); - init_prbs = 0xfff; + /* channel estimation fine configuration */ + switch (max_constellation) { + case QAM_64: + ana_gain = 0x7; // -1 : avoid def_est saturation when ADC target is -16dB + coeff[0] = 0x0148; /* P_adp_regul_cnt 0.04 */ + coeff[1] = 0xfff0; /* P_adp_noise_cnt -0.002 */ + coeff[2] = 0x00a4; /* P_adp_regul_ext 0.02 */ + coeff[3] = 0xfff8; /* P_adp_noise_ext -0.001 */ + //if (!state->cfg.hostbus_diversity) //if diversity, we should prehaps use the configuration of the max_constallation -1 + break; + case QAM_16: + ana_gain = 0x7; // -1 : avoid def_est saturation when ADC target is -16dB + coeff[0] = 0x023d; /* P_adp_regul_cnt 0.07 */ + coeff[1] = 0xffdf; /* P_adp_noise_cnt -0.004 */ + coeff[2] = 0x00a4; /* P_adp_regul_ext 0.02 */ + coeff[3] = 0xfff0; /* P_adp_noise_ext -0.002 */ + //if (!((state->cfg.hostbus_diversity) && (max_constellation == QAM_16))) + break; + default: + ana_gain = 0; // 0 : goes along with ADC target at -22dB to keep good mobile performance and lock at sensitivity level + coeff[0] = 0x099a; /* P_adp_regul_cnt 0.3 */ + coeff[1] = 0xffae; /* P_adp_noise_cnt -0.01 */ + coeff[2] = 0x0333; /* P_adp_regul_ext 0.1 */ + coeff[3] = 0xfff8; /* P_adp_noise_ext -0.002 */ + break; } + for (mode = 0; mode < 4; mode++) + dib8000_write_word(state, 215 + mode, coeff[mode]); - /* SMALL */ - dib8000_small_fine_tune(state); + // update ana_gain depending on max constellation + dib8000_write_word(state, 116, ana_gain); + // update ADC target depending on ana_gain + if (ana_gain) { // set -16dB ADC target for ana_gain=-1 + for (i = 0; i < 10; i++) + dib8000_write_word(state, 80 + i, adc_target_16dB[i]); + } else { // set -22dB ADC target for ana_gain=0 + for (i = 0; i < 10; i++) + dib8000_write_word(state, 80 + i, adc_target_16dB[i] - 355); + } - dib8000_set_subchannel_prbs(state, init_prbs); + // ---- ANA_FE ---- + if (state->fe[0]->dtv_property_cache.isdbt_sb_mode) { + if (state->fe[0]->dtv_property_cache.isdbt_partial_reception == 1) + ana_fe = ana_fe_coeff_3seg; + else // 1-segment + ana_fe = ana_fe_coeff_1seg; + } else + ana_fe = ana_fe_coeff_13seg; + + if (state->fe[0]->dtv_property_cache.isdbt_sb_mode == 1 || state->isdbt_cfg_loaded == 0) + for (mode = 0; mode < 24; mode++) + dib8000_write_word(state, 117 + mode, ana_fe[mode]); - /* ---- CHAN_BLK ---- */ + // ---- CHAN_BLK ---- for (i = 0; i < 13; i++) { - if ((((~state->seg_diff_mask) >> i) & 1) == 1) { - p_cfr_left_edge += (1 << i) * ((i == 0) || ((((state->seg_mask & (~state->seg_diff_mask)) >> (i - 1)) & 1) == 0)); - p_cfr_right_edge += (1 << i) * ((i == 12) || ((((state->seg_mask & (~state->seg_diff_mask)) >> (i + 1)) & 1) == 0)); + if ((((~seg_diff_mask) >> i) & 1) == 1) { + P_cfr_left_edge += (1 << i) * ((i == 0) || ((((seg_mask13 & (~seg_diff_mask)) >> (i - 1)) & 1) == 0)); + P_cfr_right_edge += (1 << i) * ((i == 12) || ((((seg_mask13 & (~seg_diff_mask)) >> (i + 1)) & 1) == 0)); } } - dib8000_write_word(state, 222, p_cfr_left_edge); /* p_cfr_left_edge */ - dib8000_write_word(state, 223, p_cfr_right_edge); /* p_cfr_right_edge */ - /* "P_cspu_left_edge" & "P_cspu_right_edge" not used => do not care */ - - dib8000_write_word(state, 189, ~state->seg_mask | state->seg_diff_mask); /* P_lmod4_seg_inh */ - dib8000_write_word(state, 192, ~state->seg_mask | state->seg_diff_mask); /* P_pha3_seg_inh */ - dib8000_write_word(state, 225, ~state->seg_mask | state->seg_diff_mask); /* P_tac_seg_inh */ - - if (!autosearching) - dib8000_write_word(state, 288, (~state->seg_mask | state->seg_diff_mask) & 0x1fff); /* P_tmcc_seg_eq_inh */ - else - dib8000_write_word(state, 288, 0x1fff); /*disable equalisation of the tmcc when autosearch to be able to find the DQPSK channels. */ - - dib8000_write_word(state, 211, state->seg_mask & (~state->seg_diff_mask)); /* P_des_seg_enabled */ - dib8000_write_word(state, 287, ~state->seg_mask | 0x1000); /* P_tmcc_seg_inh */ - - dib8000_write_word(state, 178, 32); /* P_fft_powrange = 32 */ - - /* ---- TMCC ---- */ + dib8000_write_word(state, 222, P_cfr_left_edge); // P_cfr_left_edge + dib8000_write_word(state, 223, P_cfr_right_edge); // P_cfr_right_edge + // "P_cspu_left_edge" not used => do not care + // "P_cspu_right_edge" not used => do not care + + if (state->fe[0]->dtv_property_cache.isdbt_sb_mode == 1) { + dib8000_write_word(state, 228, 1); // P_2d_mode_byp=1 + dib8000_write_word(state, 205, dib8000_read_word(state, 205) & 0xfff0); // P_cspu_win_cut = 0 + if (state->fe[0]->dtv_property_cache.isdbt_partial_reception == 0 + && state->fe[0]->dtv_property_cache.transmission_mode == TRANSMISSION_MODE_2K) { + //dib8000_write_word(state, 219, dib8000_read_word(state, 219) & 0xfffe); // P_adp_pass = 0 + dib8000_write_word(state, 265, 15); // P_equal_noise_sel = 15 + } + } else if (state->isdbt_cfg_loaded == 0) { + dib8000_write_word(state, 228, 0); // default value + dib8000_write_word(state, 265, 31); // default value + dib8000_write_word(state, 205, 0x200f); // init value + } + // ---- TMCC ---- for (i = 0; i < 3; i++) - tmcc_pow += (((state->fe[0]->dtv_property_cache.layer[i].modulation == DQPSK) * 4 + 1) * state->fe[0]->dtv_property_cache.layer[i].segment_count) ; + tmcc_pow += + (((state->fe[0]->dtv_property_cache.layer[i].modulation == DQPSK) * 4 + 1) * state->fe[0]->dtv_property_cache.layer[i].segment_count); + // Quantif of "P_tmcc_dec_thres_?k" is (0, 5+mode, 9); + // Threshold is set at 1/4 of max power. + tmcc_pow *= (1 << (9 - 2)); + + dib8000_write_word(state, 290, tmcc_pow); // P_tmcc_dec_thres_2k + dib8000_write_word(state, 291, tmcc_pow); // P_tmcc_dec_thres_4k + dib8000_write_word(state, 292, tmcc_pow); // P_tmcc_dec_thres_8k + //dib8000_write_word(state, 287, (1 << 13) | 0x1000 ); + // ---- PHA3 ---- - /* Quantif of "P_tmcc_dec_thres_?k" is (0, 5+mode, 9); */ - /* Threshold is set at 1/4 of max power. */ - tmcc_pow *= (1 << (9-2)); - dib8000_write_word(state, 290, tmcc_pow); /* P_tmcc_dec_thres_2k */ - dib8000_write_word(state, 291, tmcc_pow); /* P_tmcc_dec_thres_4k */ - dib8000_write_word(state, 292, tmcc_pow); /* P_tmcc_dec_thres_8k */ - /*dib8000_write_word(state, 287, (1 << 13) | 0x1000 ); */ - - /* ---- PHA3 ---- */ if (state->isdbt_cfg_loaded == 0) - dib8000_write_word(state, 250, 3285); /* p_2d_hspeed_thr0 */ - - state->isdbt_cfg_loaded = 0; -} + dib8000_write_word(state, 250, 3285); /*p_2d_hspeed_thr0 */ -u32 dib8000_wait_lock(struct dib8000_state *state, u32 internal, u32 wait0_ms, u32 wait1_ms, u32 wait2_ms) -{ - u32 value; - u16 reg = 11; /* P_search_end0 start addr */ + if (state->fe[0]->dtv_property_cache.isdbt_sb_mode == 1) + state->isdbt_cfg_loaded = 0; + else + state->isdbt_cfg_loaded = 1; - for (reg = 11; reg < 16; reg += 2) { - if (reg == 11) { - if (state->revision == 0x8090) - value = internal * wait1_ms; /* P_search_end0 wait time */ - else - value = internal * wait0_ms; /* P_search_end0 wait time */ - } else if (reg == 13) - value = internal * wait1_ms; /* P_search_end0 wait time */ - else if (reg == 15) - value = internal * wait2_ms; /* P_search_end0 wait time */ - dib8000_write_word(state, reg, (u16)((value >> 16) & 0xffff)); - dib8000_write_word(state, (reg + 1), (u16)(value & 0xffff)); - } - return value; } static int dib8000_autosearch_start(struct dvb_frontend *fe) { + u8 factor; + u32 value; struct dib8000_state *state = fe->demodulator_priv; - u8 slist = 0; - u32 value, internal = state->cfg.pll->internal; - if (state->revision == 0x8090) - internal = dib8000_read32(state, 23) / 1000; - - if (state->autosearch_state == AS_SEARCHING_FFT) { - dib8000_write_word(state, 37, 0x0065); /* P_ctrl_pha_off_max default values */ - dib8000_write_word(state, 116, 0x0000); /* P_ana_gain to 0 */ - - dib8000_write_word(state, 0, (dib8000_read_word(state, 0) & 0x1fff) | (0 << 13) | (1 << 15)); /* P_mode = 0, P_restart_search=1 */ - dib8000_write_word(state, 1, (dib8000_read_word(state, 1) & 0xfffc) | 0); /* P_guard = 0 */ - dib8000_write_word(state, 6, 0); /* P_lock0_mask = 0 */ - dib8000_write_word(state, 7, 0); /* P_lock1_mask = 0 */ - dib8000_write_word(state, 8, 0); /* P_lock2_mask = 0 */ - dib8000_write_word(state, 10, (dib8000_read_word(state, 10) & 0x200) | (16 << 4) | (0 << 0)); /* P_search_list=16, P_search_maxtrial=0 */ - - if (state->revision == 0x8090) - value = dib8000_wait_lock(state, internal, 10, 10, 10); /* time in ms configure P_search_end0 P_search_end1 P_search_end2 */ - else - value = dib8000_wait_lock(state, internal, 20, 20, 20); /* time in ms configure P_search_end0 P_search_end1 P_search_end2 */ - - dib8000_write_word(state, 17, 0); - dib8000_write_word(state, 18, 200); /* P_search_rstst = 200 */ - dib8000_write_word(state, 19, 0); - dib8000_write_word(state, 20, 400); /* P_search_rstend = 400 */ - dib8000_write_word(state, 21, (value >> 16) & 0xffff); /* P_search_checkst */ - dib8000_write_word(state, 22, value & 0xffff); + int slist = 0; - if (state->revision == 0x8090) - dib8000_write_word(state, 32, (dib8000_read_word(state, 32) & 0xf0ff) | (0 << 8)); /* P_corm_alpha = 0 */ - else - dib8000_write_word(state, 32, (dib8000_read_word(state, 32) & 0xf0ff) | (9 << 8)); /* P_corm_alpha = 3 */ - dib8000_write_word(state, 355, 2); /* P_search_param_max = 2 */ - - /* P_search_param_select = (1 | 1<<4 | 1 << 8) */ - dib8000_write_word(state, 356, 0); - dib8000_write_word(state, 357, 0x111); + state->fe[0]->dtv_property_cache.inversion = 0; + if (!state->fe[0]->dtv_property_cache.isdbt_sb_mode) + state->fe[0]->dtv_property_cache.layer[0].segment_count = 13; + state->fe[0]->dtv_property_cache.layer[0].modulation = QAM_64; + state->fe[0]->dtv_property_cache.layer[0].fec = FEC_2_3; + state->fe[0]->dtv_property_cache.layer[0].interleaving = 0; - dib8000_write_word(state, 770, (dib8000_read_word(state, 770) & 0xdfff) | (1 << 13)); /* P_restart_ccg = 1 */ - dib8000_write_word(state, 770, (dib8000_read_word(state, 770) & 0xdfff) | (0 << 13)); /* P_restart_ccg = 0 */ - dib8000_write_word(state, 0, (dib8000_read_word(state, 0) & 0x7ff) | (0 << 15) | (1 << 13)); /* P_restart_search = 0; */ - } else if (state->autosearch_state == AS_SEARCHING_GUARD) { + //choose the right list, in sb, always do everything + if (state->fe[0]->dtv_property_cache.isdbt_sb_mode) { state->fe[0]->dtv_property_cache.transmission_mode = TRANSMISSION_MODE_8K; state->fe[0]->dtv_property_cache.guard_interval = GUARD_INTERVAL_1_8; - state->fe[0]->dtv_property_cache.inversion = 0; - state->fe[0]->dtv_property_cache.layer[0].modulation = QAM_64; - state->fe[0]->dtv_property_cache.layer[0].fec = FEC_2_3; - state->fe[0]->dtv_property_cache.layer[0].interleaving = 0; - state->fe[0]->dtv_property_cache.layer[0].segment_count = 13; - - slist = 16; - state->fe[0]->dtv_property_cache.transmission_mode = state->found_nfft; - - dib8000_set_isdbt_common_channel(state, slist, 1); - - /* set lock_mask values */ - dib8000_write_word(state, 6, 0x4); - if (state->revision == 0x8090) - dib8000_write_word(state, 7, ((1 << 12) | (1 << 11) | (1 << 10)));/* tmcc_dec_lock, tmcc_sync_lock, tmcc_data_lock, tmcc_bch_uncor */ - else - dib8000_write_word(state, 7, 0x8); - dib8000_write_word(state, 8, 0x1000); - - /* set lock_mask wait time values */ - if (state->revision == 0x8090) - dib8000_wait_lock(state, internal, 50, 100, 1000); /* time in ms configure P_search_end0 P_search_end1 P_search_end2 */ - else - dib8000_wait_lock(state, internal, 50, 200, 1000); /* time in ms configure P_search_end0 P_search_end1 P_search_end2 */ - - dib8000_write_word(state, 355, 3); /* P_search_param_max = 3 */ - - /* P_search_param_select = 0xf; look for the 4 different guard intervals */ - dib8000_write_word(state, 356, 0); - dib8000_write_word(state, 357, 0xf); - - value = dib8000_read_word(state, 0); - dib8000_write_word(state, 0, (u16)((1 << 15) | value)); - dib8000_read_word(state, 1284); /* reset the INT. n_irq_pending */ - dib8000_write_word(state, 0, (u16)value); + slist = 7; + dib8000_write_word(state, 0, (dib8000_read_word(state, 0) & 0x9fff) | (1 << 13)); } else { - state->fe[0]->dtv_property_cache.inversion = 0; - state->fe[0]->dtv_property_cache.layer[0].modulation = QAM_64; - state->fe[0]->dtv_property_cache.layer[0].fec = FEC_2_3; - state->fe[0]->dtv_property_cache.layer[0].interleaving = 0; - state->fe[0]->dtv_property_cache.layer[0].segment_count = 13; - if (!state->fe[0]->dtv_property_cache.isdbt_sb_mode) - state->fe[0]->dtv_property_cache.layer[0].segment_count = 13; - - /* choose the right list, in sb, always do everything */ - if (state->fe[0]->dtv_property_cache.isdbt_sb_mode) { - slist = 7; - dib8000_write_word(state, 0, (dib8000_read_word(state, 0) & 0x9fff) | (1 << 13)); + if (state->fe[0]->dtv_property_cache.guard_interval == GUARD_INTERVAL_AUTO) { + if (state->fe[0]->dtv_property_cache.transmission_mode == TRANSMISSION_MODE_AUTO) { + slist = 7; + dib8000_write_word(state, 0, (dib8000_read_word(state, 0) & 0x9fff) | (1 << 13)); // P_mode = 1 to have autosearch start ok with mode2 + } else + slist = 3; } else { - if (state->fe[0]->dtv_property_cache.guard_interval == GUARD_INTERVAL_AUTO) { - if (state->fe[0]->dtv_property_cache.transmission_mode == TRANSMISSION_MODE_AUTO) { - state->fe[0]->dtv_property_cache.transmission_mode = TRANSMISSION_MODE_8K; - state->fe[0]->dtv_property_cache.guard_interval = GUARD_INTERVAL_1_8; - slist = 7; - dib8000_write_word(state, 0, (dib8000_read_word(state, 0) & 0x9fff) | (1 << 13)); /* P_mode = 1 to have autosearch start ok with mode2 */ - } else { - state->fe[0]->dtv_property_cache.guard_interval = GUARD_INTERVAL_1_8; - slist = 3; - } - } else { - if (state->fe[0]->dtv_property_cache.transmission_mode == TRANSMISSION_MODE_AUTO) { - state->fe[0]->dtv_property_cache.transmission_mode = TRANSMISSION_MODE_8K; - slist = 2; - dib8000_write_word(state, 0, (dib8000_read_word(state, 0) & 0x9fff) | (1 << 13)); /* P_mode = 1 */ - } else - slist = 0; - } + if (state->fe[0]->dtv_property_cache.transmission_mode == TRANSMISSION_MODE_AUTO) { + slist = 2; + dib8000_write_word(state, 0, (dib8000_read_word(state, 0) & 0x9fff) | (1 << 13)); // P_mode = 1 + } else + slist = 0; } - dprintk("Using list for autosearch : %d", slist); - dib8000_set_isdbt_common_channel(state, slist, 1); + if (state->fe[0]->dtv_property_cache.transmission_mode == TRANSMISSION_MODE_AUTO) + state->fe[0]->dtv_property_cache.transmission_mode = TRANSMISSION_MODE_8K; + if (state->fe[0]->dtv_property_cache.guard_interval == GUARD_INTERVAL_AUTO) + state->fe[0]->dtv_property_cache.guard_interval = GUARD_INTERVAL_1_8; + + dprintk("using list for autosearch : %d", slist); + dib8000_set_channel(state, (unsigned char)slist, 1); + //dib8000_write_word(state, 0, (dib8000_read_word(state, 0) & 0x9fff) | (1 << 13)); // P_mode = 1 + + factor = 1; - /* set lock_mask values */ + //set lock_mask values dib8000_write_word(state, 6, 0x4); - if (state->revision == 0x8090) - dib8000_write_word(state, 7, (1 << 12) | (1 << 11) | (1 << 10)); - else - dib8000_write_word(state, 7, 0x8); + dib8000_write_word(state, 7, 0x8); dib8000_write_word(state, 8, 0x1000); - /* set lock_mask wait time values */ - if (state->revision == 0x8090) - dib8000_wait_lock(state, internal, 50, 200, 1000); /* time in ms configure P_search_end0 P_search_end1 P_search_end2 */ - else - dib8000_wait_lock(state, internal, 50, 100, 1000); /* time in ms configure P_search_end0 P_search_end1 P_search_end2 */ + //set lock_mask wait time values + value = 50 * state->cfg.pll->internal * factor; + dib8000_write_word(state, 11, (u16) ((value >> 16) & 0xffff)); // lock0 wait time + dib8000_write_word(state, 12, (u16) (value & 0xffff)); // lock0 wait time + value = 100 * state->cfg.pll->internal * factor; + dib8000_write_word(state, 13, (u16) ((value >> 16) & 0xffff)); // lock1 wait time + dib8000_write_word(state, 14, (u16) (value & 0xffff)); // lock1 wait time + value = 1000 * state->cfg.pll->internal * factor; + dib8000_write_word(state, 15, (u16) ((value >> 16) & 0xffff)); // lock2 wait time + dib8000_write_word(state, 16, (u16) (value & 0xffff)); // lock2 wait time value = dib8000_read_word(state, 0); - dib8000_write_word(state, 0, (u16)((1 << 15) | value)); - dib8000_read_word(state, 1284); /* reset the INT. n_irq_pending */ - dib8000_write_word(state, 0, (u16)value); + dib8000_write_word(state, 0, (u16) ((1 << 15) | value)); + dib8000_read_word(state, 1284); // reset the INT. n_irq_pending + dib8000_write_word(state, 0, (u16) value); + } + return 0; } @@ -2577,623 +2663,96 @@ static int dib8000_autosearch_irq(struct dvb_frontend *fe) struct dib8000_state *state = fe->demodulator_priv; u16 irq_pending = dib8000_read_word(state, 1284); - if (state->autosearch_state == AS_SEARCHING_FFT) { - if (irq_pending & 0x1) { - dprintk("dib8000_autosearch_irq: max correlation result available"); - return 3; - } - } else { - if (irq_pending & 0x1) { /* failed */ - dprintk("dib8000_autosearch_irq failed"); - return 1; - } + if (irq_pending & 0x1) { // failed + dprintk("dib8000_autosearch_irq failed"); + return 1; + } - if (irq_pending & 0x2) { /* succeeded */ - dprintk("dib8000_autosearch_irq succeeded"); - return 2; - } + if (irq_pending & 0x2) { // succeeded + dprintk("dib8000_autosearch_irq succeeded"); + return 2; } return 0; // still pending } -static void dib8000_viterbi_state(struct dib8000_state *state, u8 onoff) +static int dib8000_tune(struct dvb_frontend *fe) { - u16 tmp; + struct dib8000_state *state = fe->demodulator_priv; + int ret = 0; + u16 lock, value, mode; - tmp = dib8000_read_word(state, 771); - if (onoff) /* start P_restart_chd : channel_decoder */ - dib8000_write_word(state, 771, tmp & 0xfffd); - else /* stop P_restart_chd : channel_decoder */ - dib8000_write_word(state, 771, tmp | (1<<1)); -} + // we are already tuned - just resuming from suspend + if (state == NULL) + return -EINVAL; -static void dib8000_set_dds(struct dib8000_state *state, s32 offset_khz) -{ - s16 unit_khz_dds_val; - u32 abs_offset_khz = ABS(offset_khz); - u32 dds = state->cfg.pll->ifreq & 0x1ffffff; - u8 invert = !!(state->cfg.pll->ifreq & (1 << 25)); - u8 ratio; + mode = fft_to_mode(state); - if (state->revision == 0x8090) { - ratio = 4; - unit_khz_dds_val = (1<<26) / (dib8000_read32(state, 23) / 1000); - if (offset_khz < 0) - dds = (1 << 26) - (abs_offset_khz * unit_khz_dds_val); - else - dds = (abs_offset_khz * unit_khz_dds_val); + dib8000_set_bandwidth(fe, state->fe[0]->dtv_property_cache.bandwidth_hz / 1000); + dib8000_set_channel(state, 0, 0); - if (invert) - dds = (1<<26) - dds; - } else { - ratio = 2; - unit_khz_dds_val = (u16) (67108864 / state->cfg.pll->internal); + // restart demod + ret |= dib8000_write_word(state, 770, 0x4000); + ret |= dib8000_write_word(state, 770, 0x0000); + msleep(45); - if (offset_khz < 0) - unit_khz_dds_val *= -1; + /* P_ctrl_inh_cor=0, P_ctrl_alpha_cor=4, P_ctrl_inh_isi=0, P_ctrl_alpha_isi=3 */ + /* ret |= dib8000_write_word(state, 29, (0 << 9) | (4 << 5) | (0 << 4) | (3 << 0) ); workaround inh_isi stays at 1 */ - /* IF tuner */ - if (invert) - dds -= abs_offset_khz * unit_khz_dds_val; - else - dds += abs_offset_khz * unit_khz_dds_val; + // never achieved a lock before - wait for timfreq to update + if (state->timf == 0) { + if (state->fe[0]->dtv_property_cache.isdbt_sb_mode == 1) { + if (state->fe[0]->dtv_property_cache.isdbt_partial_reception == 0) + msleep(300); + else // Sound Broadcasting mode 3 seg + msleep(500); + } else // 13 seg + msleep(200); } + if (state->fe[0]->dtv_property_cache.isdbt_sb_mode == 1) { + if (state->fe[0]->dtv_property_cache.isdbt_partial_reception == 0) { - dprintk("setting a DDS frequency offset of %c%dkHz", invert ? '-' : ' ', dds / unit_khz_dds_val); + /* P_timf_alpha = (13-P_mode) , P_corm_alpha=6, P_corm_thres=0x40 alpha to check on board */ + dib8000_write_word(state, 32, ((13 - mode) << 12) | (6 << 8) | 0x40); + //dib8000_write_word(state, 32, (8 << 12) | (6 << 8) | 0x80); - if (abs_offset_khz <= (state->cfg.pll->internal / ratio)) { - /* Max dds offset is the half of the demod freq */ - dib8000_write_word(state, 26, invert); - dib8000_write_word(state, 27, (u16)(dds >> 16) & 0x1ff); - dib8000_write_word(state, 28, (u16)(dds & 0xffff)); - } -} + /* P_ctrl_sfreq_step= (12-P_mode) P_ctrl_sfreq_inh =0 P_ctrl_pha_off_max */ + ret |= dib8000_write_word(state, 37, (12 - mode) | ((5 + mode) << 5)); -static void dib8000_set_frequency_offset(struct dib8000_state *state) -{ - int i; - u32 current_rf; - int total_dds_offset_khz; - - if (state->fe[0]->ops.tuner_ops.get_frequency) - state->fe[0]->ops.tuner_ops.get_frequency(state->fe[0], ¤t_rf); - else - current_rf = state->fe[0]->dtv_property_cache.frequency; - current_rf /= 1000; - total_dds_offset_khz = (int)current_rf - (int)state->fe[0]->dtv_property_cache.frequency / 1000; + } else { // Sound Broadcasting mode 3 seg - if (state->fe[0]->dtv_property_cache.isdbt_sb_mode) { - state->subchannel = state->fe[0]->dtv_property_cache.isdbt_sb_subchannel; - - i = dib8000_read_word(state, 26) & 1; /* P_dds_invspec */ - dib8000_write_word(state, 26, state->fe[0]->dtv_property_cache.inversion ^ i); + /* P_timf_alpha = (12-P_mode) , P_corm_alpha=6, P_corm_thres=0x60 alpha to check on board */ + dib8000_write_word(state, 32, ((12 - mode) << 12) | (6 << 8) | 0x60); - if (state->cfg.pll->ifreq == 0) { /* low if tuner */ - if ((state->fe[0]->dtv_property_cache.inversion ^ i) == 0) - dib8000_write_word(state, 26, dib8000_read_word(state, 26) | 1); - } else { - if ((state->fe[0]->dtv_property_cache.inversion ^ i) == 0) - total_dds_offset_khz *= -1; + ret |= dib8000_write_word(state, 37, (11 - mode) | ((5 + mode) << 5)); } - } - - dprintk("%dkhz tuner offset (frequency = %dHz & current_rf = %dHz) total_dds_offset_hz = %d", state->fe[0]->dtv_property_cache.frequency - current_rf, state->fe[0]->dtv_property_cache.frequency, current_rf, total_dds_offset_khz); - - /* apply dds offset now */ - dib8000_set_dds(state, total_dds_offset_khz); -} - -static u16 LUT_isdbt_symbol_duration[4] = { 26, 101, 63 }; -u32 dib8000_get_symbol_duration(struct dib8000_state *state) -{ - u16 i; - - switch (state->fe[0]->dtv_property_cache.transmission_mode) { - case TRANSMISSION_MODE_2K: - i = 0; - break; - case TRANSMISSION_MODE_4K: - i = 2; - break; - default: - case TRANSMISSION_MODE_AUTO: - case TRANSMISSION_MODE_8K: - i = 1; - break; - } - return (LUT_isdbt_symbol_duration[i] / (state->fe[0]->dtv_property_cache.bandwidth_hz / 1000)) + 1; -} + } else { // 13 seg + /* P_timf_alpha = 8 , P_corm_alpha=6, P_corm_thres=0x80 alpha to check on board */ + dib8000_write_word(state, 32, ((11 - mode) << 12) | (6 << 8) | 0x80); -static void dib8000_set_isdbt_loop_params(struct dib8000_state *state, enum param_loop_step loop_step) -{ - u16 reg_32 = 0, reg_37 = 0; + ret |= dib8000_write_word(state, 37, (10 - mode) | ((5 + mode) << 5)); - switch (loop_step) { - case LOOP_TUNE_1: - if (state->fe[0]->dtv_property_cache.isdbt_sb_mode) { - if (state->fe[0]->dtv_property_cache.isdbt_partial_reception == 0) { - reg_32 = ((11 - state->mode) << 12) | (6 << 8) | 0x40; /* P_timf_alpha = (11-P_mode), P_corm_alpha=6, P_corm_thres=0x40 */ - reg_37 = (3 << 5) | (0 << 4) | (10 - state->mode); /* P_ctrl_pha_off_max=3 P_ctrl_sfreq_inh =0 P_ctrl_sfreq_step = (10-P_mode) */ - } else { /* Sound Broadcasting mode 3 seg */ - reg_32 = ((10 - state->mode) << 12) | (6 << 8) | 0x60; /* P_timf_alpha = (10-P_mode), P_corm_alpha=6, P_corm_thres=0x60 */ - reg_37 = (3 << 5) | (0 << 4) | (9 - state->mode); /* P_ctrl_pha_off_max=3 P_ctrl_sfreq_inh =0 P_ctrl_sfreq_step = (9-P_mode) */ - } - } else { /* 13-seg start conf offset loop parameters */ - reg_32 = ((9 - state->mode) << 12) | (6 << 8) | 0x80; /* P_timf_alpha = (9-P_mode, P_corm_alpha=6, P_corm_thres=0x80 */ - reg_37 = (3 << 5) | (0 << 4) | (8 - state->mode); /* P_ctrl_pha_off_max=3 P_ctrl_sfreq_inh =0 P_ctrl_sfreq_step = 9 */ - } - break; - case LOOP_TUNE_2: - if (state->fe[0]->dtv_property_cache.isdbt_sb_mode) { - if (state->fe[0]->dtv_property_cache.isdbt_partial_reception == 0) { /* Sound Broadcasting mode 1 seg */ - reg_32 = ((13-state->mode) << 12) | (6 << 8) | 0x40; /* P_timf_alpha = (13-P_mode) , P_corm_alpha=6, P_corm_thres=0x40*/ - reg_37 = (12-state->mode) | ((5 + state->mode) << 5); - } else { /* Sound Broadcasting mode 3 seg */ - reg_32 = ((12-state->mode) << 12) | (6 << 8) | 0x60; /* P_timf_alpha = (12-P_mode) , P_corm_alpha=6, P_corm_thres=0x60 */ - reg_37 = (11-state->mode) | ((5 + state->mode) << 5); - } - } else { /* 13 seg */ - reg_32 = ((11-state->mode) << 12) | (6 << 8) | 0x80; /* P_timf_alpha = 8 , P_corm_alpha=6, P_corm_thres=0x80 */ - reg_37 = ((5+state->mode) << 5) | (10 - state->mode); - } - break; } - dib8000_write_word(state, 32, reg_32); - dib8000_write_word(state, 37, reg_37); -} - -static void dib8000_demod_restart(struct dib8000_state *state) -{ - dib8000_write_word(state, 770, 0x4000); - dib8000_write_word(state, 770, 0x0000); - return; -} -static void dib8000_set_sync_wait(struct dib8000_state *state) -{ - u16 sync_wait = 64; - - /* P_dvsy_sync_wait - reuse mode */ - switch (state->fe[0]->dtv_property_cache.transmission_mode) { - case TRANSMISSION_MODE_8K: - sync_wait = 256; - break; - case TRANSMISSION_MODE_4K: - sync_wait = 128; - break; - default: - case TRANSMISSION_MODE_2K: - sync_wait = 64; - break; - } - - if (state->cfg.diversity_delay == 0) - sync_wait = (sync_wait * (1 << (state->fe[0]->dtv_property_cache.guard_interval)) * 3) / 2 + 48; /* add 50% SFN margin + compensate for one DVSY-fifo */ - else - sync_wait = (sync_wait * (1 << (state->fe[0]->dtv_property_cache.guard_interval)) * 3) / 2 + state->cfg.diversity_delay; /* add 50% SFN margin + compensate for DVSY-fifo */ - - dib8000_write_word(state, 273, (dib8000_read_word(state, 273) & 0x000f) | (sync_wait << 4)); -} - -static u32 dib8000_get_timeout(struct dib8000_state *state, u32 delay, enum timeout_mode mode) -{ - if (mode == SYMBOL_DEPENDENT_ON) - return systime() + (delay * state->symbol_duration); + // we achieved a coff_cpil_lock - it's time to update the timf + if (state->revision != 0x8090) + lock = dib8000_read_word(state, 568); else - return systime() + delay; -} + lock = dib8000_read_word(state, 570); + if ((lock >> 11) & 0x1) + dib8000_update_timf(state); -static s32 dib8000_get_status(struct dvb_frontend *fe) -{ - struct dib8000_state *state = fe->demodulator_priv; - return state->status; -} + //now that tune is finished, lock0 should lock on fec_mpeg to output this lock on MP_LOCK. It's changed in autosearch start + dib8000_write_word(state, 6, 0x200); -enum frontend_tune_state dib8000_get_tune_state(struct dvb_frontend *fe) -{ - struct dib8000_state *state = fe->demodulator_priv; - return state->tune_state; -} -EXPORT_SYMBOL(dib8000_get_tune_state); - -int dib8000_set_tune_state(struct dvb_frontend *fe, enum frontend_tune_state tune_state) -{ - struct dib8000_state *state = fe->demodulator_priv; - - state->tune_state = tune_state; - return 0; -} -EXPORT_SYMBOL(dib8000_set_tune_state); - -static int dib8000_tune_restart_from_demod(struct dvb_frontend *fe) -{ - struct dib8000_state *state = fe->demodulator_priv; - - state->status = FE_STATUS_TUNE_PENDING; - state->tune_state = CT_DEMOD_START; - return 0; -} - -static u16 dib8000_read_lock(struct dvb_frontend *fe) -{ - struct dib8000_state *state = fe->demodulator_priv; - - if (state->revision == 0x8090) - return dib8000_read_word(state, 570); - return dib8000_read_word(state, 568); -} - -static int dib8090p_init_sdram(struct dib8000_state *state) -{ - u16 reg = 0; - dprintk("init sdram"); - - reg = dib8000_read_word(state, 274) & 0xfff0; - dib8000_write_word(state, 274, reg | 0x7); /* P_dintlv_delay_ram = 7 because of MobileSdram */ - - dib8000_write_word(state, 1803, (7 << 2)); - - reg = dib8000_read_word(state, 1280); - dib8000_write_word(state, 1280, reg | (1 << 2)); /* force restart P_restart_sdram */ - dib8000_write_word(state, 1280, reg); /* release restart P_restart_sdram */ - - return 0; -} - -static int dib8000_tune(struct dvb_frontend *fe) -{ - struct dib8000_state *state = fe->demodulator_priv; - enum frontend_tune_state *tune_state = &state->tune_state; - - u16 locks, deeper_interleaver = 0, i; - int ret = 1; /* 1 symbol duration (in 100us unit) delay most of the time */ - - u32 *timeout = &state->timeout; - u32 now = systime(); -#ifdef DIB8000_AGC_FREEZE - u16 agc1, agc2; -#endif - - u32 corm[4] = {0, 0, 0, 0}; - u8 find_index, max_value; - -#if 0 - if (*tune_state < CT_DEMOD_STOP) - dprintk("IN: context status = %d, TUNE_STATE %d autosearch step = %u systime = %u", state->channel_parameters_set, *tune_state, state->autosearch_state, now); -#endif - - switch (*tune_state) { - case CT_DEMOD_START: /* 30 */ - if (state->revision == 0x8090) - dib8090p_init_sdram(state); - state->status = FE_STATUS_TUNE_PENDING; - if ((state->fe[0]->dtv_property_cache.delivery_system != SYS_ISDBT) || - (state->fe[0]->dtv_property_cache.inversion == INVERSION_AUTO) || - (state->fe[0]->dtv_property_cache.transmission_mode == TRANSMISSION_MODE_AUTO) || - (state->fe[0]->dtv_property_cache.guard_interval == GUARD_INTERVAL_AUTO) || - (((state->fe[0]->dtv_property_cache.isdbt_layer_enabled & (1 << 0)) != 0) && - (state->fe[0]->dtv_property_cache.layer[0].segment_count != 0xff) && - (state->fe[0]->dtv_property_cache.layer[0].segment_count != 0) && - ((state->fe[0]->dtv_property_cache.layer[0].modulation == QAM_AUTO) || - (state->fe[0]->dtv_property_cache.layer[0].fec == FEC_AUTO))) || - (((state->fe[0]->dtv_property_cache.isdbt_layer_enabled & (1 << 1)) != 0) && - (state->fe[0]->dtv_property_cache.layer[1].segment_count != 0xff) && - (state->fe[0]->dtv_property_cache.layer[1].segment_count != 0) && - ((state->fe[0]->dtv_property_cache.layer[1].modulation == QAM_AUTO) || - (state->fe[0]->dtv_property_cache.layer[1].fec == FEC_AUTO))) || - (((state->fe[0]->dtv_property_cache.isdbt_layer_enabled & (1 << 2)) != 0) && - (state->fe[0]->dtv_property_cache.layer[2].segment_count != 0xff) && - (state->fe[0]->dtv_property_cache.layer[2].segment_count != 0) && - ((state->fe[0]->dtv_property_cache.layer[2].modulation == QAM_AUTO) || - (state->fe[0]->dtv_property_cache.layer[2].fec == FEC_AUTO))) || - (((state->fe[0]->dtv_property_cache.layer[0].segment_count == 0) || - ((state->fe[0]->dtv_property_cache.isdbt_layer_enabled & (1 << 0)) == 0)) && - ((state->fe[0]->dtv_property_cache.layer[1].segment_count == 0) || - ((state->fe[0]->dtv_property_cache.isdbt_layer_enabled & (2 << 0)) == 0)) && - ((state->fe[0]->dtv_property_cache.layer[2].segment_count == 0) || ((state->fe[0]->dtv_property_cache.isdbt_layer_enabled & (3 << 0)) == 0)))) - state->channel_parameters_set = 0; /* auto search */ - else - state->channel_parameters_set = 1; /* channel parameters are known */ - - dib8000_viterbi_state(state, 0); /* force chan dec in restart */ - - /* Layer monit */ - dib8000_write_word(state, 285, dib8000_read_word(state, 285) & 0x60); - - dib8000_set_frequency_offset(state); - dib8000_set_bandwidth(fe, state->fe[0]->dtv_property_cache.bandwidth_hz / 1000); - - if (state->channel_parameters_set == 0) { /* The channel struct is unknown, search it ! */ -#ifdef DIB8000_AGC_FREEZE - if (state->revision != 0x8090) { - state->agc1_max = dib8000_read_word(state, 108); - state->agc1_min = dib8000_read_word(state, 109); - state->agc2_max = dib8000_read_word(state, 110); - state->agc2_min = dib8000_read_word(state, 111); - agc1 = dib8000_read_word(state, 388); - agc2 = dib8000_read_word(state, 389); - dib8000_write_word(state, 108, agc1); - dib8000_write_word(state, 109, agc1); - dib8000_write_word(state, 110, agc2); - dib8000_write_word(state, 111, agc2); - } -#endif - state->autosearch_state = AS_SEARCHING_FFT; - state->found_nfft = TRANSMISSION_MODE_AUTO; - state->found_guard = GUARD_INTERVAL_AUTO; - *tune_state = CT_DEMOD_SEARCH_NEXT; - } else { /* we already know the channel struct so TUNE only ! */ - state->autosearch_state = AS_DONE; - *tune_state = CT_DEMOD_STEP_3; - } - state->symbol_duration = dib8000_get_symbol_duration(state); - break; - - case CT_DEMOD_SEARCH_NEXT: /* 51 */ - dib8000_autosearch_start(fe); - if (state->revision == 0x8090) - ret = 50; - else - ret = 15; - *tune_state = CT_DEMOD_STEP_1; - break; - - case CT_DEMOD_STEP_1: /* 31 */ - switch (dib8000_autosearch_irq(fe)) { - case 1: /* fail */ - state->status = FE_STATUS_TUNE_FAILED; - state->autosearch_state = AS_DONE; - *tune_state = CT_DEMOD_STOP; /* else we are done here */ - break; - case 2: /* Succes */ - state->status = FE_STATUS_FFT_SUCCESS; /* signal to the upper layer, that there was a channel found and the parameters can be read */ - *tune_state = CT_DEMOD_STEP_3; - if (state->autosearch_state == AS_SEARCHING_GUARD) - *tune_state = CT_DEMOD_STEP_2; - else - state->autosearch_state = AS_DONE; - break; - case 3: /* Autosearch FFT max correlation endded */ - *tune_state = CT_DEMOD_STEP_2; - break; - } - break; - - case CT_DEMOD_STEP_2: - switch (state->autosearch_state) { - case AS_SEARCHING_FFT: - /* searching for the correct FFT */ - if (state->revision == 0x8090) { - corm[2] = (dib8000_read_word(state, 596) << 16) | (dib8000_read_word(state, 597)); - corm[1] = (dib8000_read_word(state, 598) << 16) | (dib8000_read_word(state, 599)); - corm[0] = (dib8000_read_word(state, 600) << 16) | (dib8000_read_word(state, 601)); - } else { - corm[2] = (dib8000_read_word(state, 594) << 16) | (dib8000_read_word(state, 595)); - corm[1] = (dib8000_read_word(state, 596) << 16) | (dib8000_read_word(state, 597)); - corm[0] = (dib8000_read_word(state, 598) << 16) | (dib8000_read_word(state, 599)); - } - /* dprintk("corm fft: %u %u %u", corm[0], corm[1], corm[2]); */ - - max_value = 0; - for (find_index = 1 ; find_index < 3 ; find_index++) { - if (corm[max_value] < corm[find_index]) - max_value = find_index ; - } - - switch (max_value) { - case 0: - state->found_nfft = TRANSMISSION_MODE_2K; - break; - case 1: - state->found_nfft = TRANSMISSION_MODE_4K; - break; - case 2: - default: - state->found_nfft = TRANSMISSION_MODE_8K; - break; - } - /* dprintk("Autosearch FFT has found Mode %d", max_value + 1); */ - - *tune_state = CT_DEMOD_SEARCH_NEXT; - state->autosearch_state = AS_SEARCHING_GUARD; - if (state->revision == 0x8090) - ret = 50; - else - ret = 10; - break; - case AS_SEARCHING_GUARD: - /* searching for the correct guard interval */ - if (state->revision == 0x8090) - state->found_guard = dib8000_read_word(state, 572) & 0x3; - else - state->found_guard = dib8000_read_word(state, 570) & 0x3; - /* dprintk("guard interval found=%i", state->found_guard); */ - - *tune_state = CT_DEMOD_STEP_3; - break; - default: - /* the demod should never be in this state */ - state->status = FE_STATUS_TUNE_FAILED; - state->autosearch_state = AS_DONE; - *tune_state = CT_DEMOD_STOP; /* else we are done here */ - break; - } - break; - - case CT_DEMOD_STEP_3: /* 33 */ - state->symbol_duration = dib8000_get_symbol_duration(state); - dib8000_set_isdbt_loop_params(state, LOOP_TUNE_1); - dib8000_set_isdbt_common_channel(state, 0, 0);/* setting the known channel parameters here */ - *tune_state = CT_DEMOD_STEP_4; - break; - - case CT_DEMOD_STEP_4: /* (34) */ - dib8000_demod_restart(state); - - dib8000_set_sync_wait(state); - dib8000_set_diversity_in(state->fe[0], state->diversity_onoff); - - locks = (dib8000_read_word(state, 180) >> 6) & 0x3f; /* P_coff_winlen ? */ - /* coff should lock over P_coff_winlen ofdm symbols : give 3 times this lenght to lock */ - *timeout = dib8000_get_timeout(state, 2 * locks, SYMBOL_DEPENDENT_ON); - *tune_state = CT_DEMOD_STEP_5; - break; - - case CT_DEMOD_STEP_5: /* (35) */ - locks = dib8000_read_lock(fe); - if (locks & (0x3 << 11)) { /* coff-lock and off_cpil_lock achieved */ - dib8000_update_timf(state); /* we achieved a coff_cpil_lock - it's time to update the timf */ - if (!state->differential_constellation) { - /* 2 times lmod4_win_len + 10 symbols (pipe delay after coff + nb to compute a 1st correlation) */ - *timeout = dib8000_get_timeout(state, (20 * ((dib8000_read_word(state, 188)>>5)&0x1f)), SYMBOL_DEPENDENT_ON); - *tune_state = CT_DEMOD_STEP_7; - } else { - *tune_state = CT_DEMOD_STEP_8; - } - } else if (now > *timeout) { - *tune_state = CT_DEMOD_STEP_6; /* goto check for diversity input connection */ - } - break; - - case CT_DEMOD_STEP_6: /* (36) if there is an input (diversity) */ - if ((state->fe[1] != NULL) && (state->output_mode != OUTMODE_DIVERSITY)) { - /* if there is a diversity fe in input and this fe is has not already failled : wait here until this this fe has succedeed or failled */ - if (dib8000_get_status(state->fe[1]) <= FE_STATUS_STD_SUCCESS) /* Something is locked on the input fe */ - *tune_state = CT_DEMOD_STEP_8; /* go for mpeg */ - else if (dib8000_get_status(state->fe[1]) >= FE_STATUS_TUNE_TIME_TOO_SHORT) { /* fe in input failled also, break the current one */ - *tune_state = CT_DEMOD_STOP; /* else we are done here ; step 8 will close the loops and exit */ - dib8000_viterbi_state(state, 1); /* start viterbi chandec */ - dib8000_set_isdbt_loop_params(state, LOOP_TUNE_2); - state->status = FE_STATUS_TUNE_FAILED; - } - } else { - dib8000_viterbi_state(state, 1); /* start viterbi chandec */ - dib8000_set_isdbt_loop_params(state, LOOP_TUNE_2); - *tune_state = CT_DEMOD_STOP; /* else we are done here ; step 8 will close the loops and exit */ - state->status = FE_STATUS_TUNE_FAILED; - } - break; - - case CT_DEMOD_STEP_7: /* 37 */ - locks = dib8000_read_lock(fe); - if (locks & (1<<10)) { /* lmod4_lock */ - ret = 14; /* wait for 14 symbols */ - *tune_state = CT_DEMOD_STEP_8; - } else if (now > *timeout) - *tune_state = CT_DEMOD_STEP_6; /* goto check for diversity input connection */ - break; - - case CT_DEMOD_STEP_8: /* 38 */ - dib8000_viterbi_state(state, 1); /* start viterbi chandec */ - dib8000_set_isdbt_loop_params(state, LOOP_TUNE_2); - - /* mpeg will never lock on this condition because init_prbs is not set : search for it !*/ - if (state->fe[0]->dtv_property_cache.isdbt_sb_mode && state->fe[0]->dtv_property_cache.isdbt_sb_subchannel == -1 && !state->differential_constellation) { - state->subchannel = 0; - *tune_state = CT_DEMOD_STEP_11; - } else { - *tune_state = CT_DEMOD_STEP_9; - state->status = FE_STATUS_LOCKED; - } - break; - - case CT_DEMOD_STEP_9: /* 39 */ - if ((state->revision == 0x8090) || ((dib8000_read_word(state, 1291) >> 9) & 0x1)) { /* fe capable of deinterleaving : esram */ - /* defines timeout for mpeg lock depending on interleaver lenght of longest layer */ - for (i = 0; i < 3; i++) { - if (state->fe[0]->dtv_property_cache.layer[i].interleaving >= deeper_interleaver) { - dprintk("layer%i: time interleaver = %d ", i, state->fe[0]->dtv_property_cache.layer[i].interleaving); - if (state->fe[0]->dtv_property_cache.layer[i].segment_count > 0) { /* valid layer */ - deeper_interleaver = state->fe[0]->dtv_property_cache.layer[0].interleaving; - state->longest_intlv_layer = i; - } - } - } - - if (deeper_interleaver == 0) - locks = 2; /* locks is the tmp local variable name */ - else if (deeper_interleaver == 3) - locks = 8; - else - locks = 2 * deeper_interleaver; - - if (state->diversity_onoff != 0) /* because of diversity sync */ - locks *= 2; - - *timeout = now + (2000 * locks); /* give the mpeg lock 800ms if sram is present */ - dprintk("Deeper interleaver mode = %d on layer %d : timeout mult factor = %d => will use timeout = %d", deeper_interleaver, state->longest_intlv_layer, locks, *timeout); - - *tune_state = CT_DEMOD_STEP_10; - } else - *tune_state = CT_DEMOD_STOP; - break; - - case CT_DEMOD_STEP_10: /* 40 */ - locks = dib8000_read_lock(fe); - if (locks&(1<<(7-state->longest_intlv_layer))) { /* mpeg lock : check the longest one */ - dprintk("Mpeg locks [ L0 : %d | L1 : %d | L2 : %d ]", (locks>>7)&0x1, (locks>>6)&0x1, (locks>>5)&0x1); - if (state->fe[0]->dtv_property_cache.isdbt_sb_mode && state->fe[0]->dtv_property_cache.isdbt_sb_subchannel == -1 && !state->differential_constellation) - /* signal to the upper layer, that there was a channel found and the parameters can be read */ - state->status = FE_STATUS_DEMOD_SUCCESS; - else - state->status = FE_STATUS_DATA_LOCKED; - *tune_state = CT_DEMOD_STOP; - } else if (now > *timeout) { - if (state->fe[0]->dtv_property_cache.isdbt_sb_mode && state->fe[0]->dtv_property_cache.isdbt_sb_subchannel == -1 && !state->differential_constellation) { /* continue to try init prbs autosearch */ - state->subchannel += 3; - *tune_state = CT_DEMOD_STEP_11; - } else { /* we are done mpeg of the longest interleaver xas not locking but let's try if an other layer has locked in the same time */ - if (locks & (0x7<<5)) { - dprintk("Mpeg locks [ L0 : %d | L1 : %d | L2 : %d ]", (locks>>7)&0x1, (locks>>6)&0x1, (locks>>5)&0x1); - state->status = FE_STATUS_DATA_LOCKED; - } else - state->status = FE_STATUS_TUNE_FAILED; - *tune_state = CT_DEMOD_STOP; - } - } - break; - - case CT_DEMOD_STEP_11: /* 41 : init prbs autosearch */ - if (state->subchannel <= 41) { - dib8000_set_subchannel_prbs(state, dib8000_get_init_prbs(state, state->subchannel)); - *tune_state = CT_DEMOD_STEP_9; - } else { - *tune_state = CT_DEMOD_STOP; - state->status = FE_STATUS_TUNE_FAILED; - } - break; - - default: - break; - } - - /* tuning is finished - cleanup the demod */ - switch (*tune_state) { - case CT_DEMOD_STOP: /* (42) */ -#ifdef DIB8000_AGC_FREEZE - if ((state->revision != 0x8090) && (state->agc1_max != 0)) { - dib8000_write_word(state, 108, state->agc1_max); - dib8000_write_word(state, 109, state->agc1_min); - dib8000_write_word(state, 110, state->agc2_max); - dib8000_write_word(state, 111, state->agc2_min); - state->agc1_max = 0; - state->agc1_min = 0; - state->agc2_max = 0; - state->agc2_min = 0; - } -#endif - ret = FE_CALLBACK_TIME_NEVER; - break; - default: - break; + if (state->revision == 0x8002) { + value = dib8000_read_word(state, 903); + dib8000_write_word(state, 903, value & ~(1 << 3)); + msleep(1); + dib8000_write_word(state, 903, value | (1 << 3)); } - if ((ret > 0) && (*tune_state > CT_DEMOD_STEP_3)) - return ret * state->symbol_duration; - if ((ret > 0) && (ret < state->symbol_duration)) - return state->symbol_duration; /* at least one symbol */ return ret; } @@ -3208,7 +2767,7 @@ static int dib8000_wakeup(struct dvb_frontend *fe) if (dib8000_set_adc_state(state, DIBX000_SLOW_ADC_ON) != 0) dprintk("could not start Slow ADC"); - if (state->revision == 0x8090) + if (state->revision != 0x8090) dib8000_sad_calib(state); for (index_frontend = 1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) { @@ -3238,6 +2797,21 @@ static int dib8000_sleep(struct dvb_frontend *fe) return dib8000_set_adc_state(state, DIBX000_SLOW_ADC_OFF) | dib8000_set_adc_state(state, DIBX000_ADC_OFF); } +enum frontend_tune_state dib8000_get_tune_state(struct dvb_frontend *fe) +{ + struct dib8000_state *state = fe->demodulator_priv; + return state->tune_state; +} +EXPORT_SYMBOL(dib8000_get_tune_state); + +int dib8000_set_tune_state(struct dvb_frontend *fe, enum frontend_tune_state tune_state) +{ + struct dib8000_state *state = fe->demodulator_priv; + state->tune_state = tune_state; + return 0; +} +EXPORT_SYMBOL(dib8000_set_tune_state); + static int dib8000_get_frontend(struct dvb_frontend *fe) { struct dib8000_state *state = fe->demodulator_priv; @@ -3387,9 +2961,10 @@ static int dib8000_get_frontend(struct dvb_frontend *fe) static int dib8000_set_frontend(struct dvb_frontend *fe) { struct dib8000_state *state = fe->demodulator_priv; - int l, i, active, time, ret, time_slave = FE_CALLBACK_TIME_NEVER; - u8 exit_condition, index_frontend; - u32 delay, callback_time; + u8 nbr_pending, exit_condition, index_frontend; + s8 index_frontend_success = -1; + int time, ret; + int time_slave = FE_CALLBACK_TIME_NEVER; if (state->fe[0]->dtv_property_cache.frequency == 0) { dprintk("dib8000: must at least specify frequency "); @@ -3406,36 +2981,18 @@ static int dib8000_set_frontend(struct dvb_frontend *fe) state->fe[index_frontend]->dtv_property_cache.delivery_system = SYS_ISDBT; memcpy(&state->fe[index_frontend]->dtv_property_cache, &fe->dtv_property_cache, sizeof(struct dtv_frontend_properties)); - /* set output mode and diversity input */ - if (state->revision != 0x8090) { - dib8000_set_diversity_in(state->fe[index_frontend], 1); - if (index_frontend != 0) - dib8000_set_output_mode(state->fe[index_frontend], - OUTMODE_DIVERSITY); - else - dib8000_set_output_mode(state->fe[0], OUTMODE_HIGH_Z); - } else { - dib8096p_set_diversity_in(state->fe[index_frontend], 1); - if (index_frontend != 0) - dib8096p_set_output_mode(state->fe[index_frontend], - OUTMODE_DIVERSITY); - else - dib8096p_set_output_mode(state->fe[0], OUTMODE_HIGH_Z); - } - - /* tune the tuner */ + if (state->revision != 0x8090) + dib8000_set_output_mode(state->fe[index_frontend], + OUTMODE_HIGH_Z); + else + dib8096p_set_output_mode(state->fe[index_frontend], + OUTMODE_HIGH_Z); if (state->fe[index_frontend]->ops.tuner_ops.set_params) state->fe[index_frontend]->ops.tuner_ops.set_params(state->fe[index_frontend]); dib8000_set_tune_state(state->fe[index_frontend], CT_AGC_START); } - /* turn off the diversity of the last chip */ - if (state->revision != 0x8090) - dib8000_set_diversity_in(state->fe[index_frontend - 1], 0); - else - dib8096p_set_diversity_in(state->fe[index_frontend - 1], 0); - /* start up the AGC */ do { time = dib8000_agc_startup(state->fe[0]); @@ -3462,88 +3019,139 @@ static int dib8000_set_frontend(struct dvb_frontend *fe) for (index_frontend = 0; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) dib8000_set_tune_state(state->fe[index_frontend], CT_DEMOD_START); - active = 1; - do { - callback_time = FE_CALLBACK_TIME_NEVER; + if ((state->fe[0]->dtv_property_cache.delivery_system != SYS_ISDBT) || + (state->fe[0]->dtv_property_cache.inversion == INVERSION_AUTO) || + (state->fe[0]->dtv_property_cache.transmission_mode == TRANSMISSION_MODE_AUTO) || + (state->fe[0]->dtv_property_cache.guard_interval == GUARD_INTERVAL_AUTO) || + (((state->fe[0]->dtv_property_cache.isdbt_layer_enabled & (1 << 0)) != 0) && + (state->fe[0]->dtv_property_cache.layer[0].segment_count != 0xff) && + (state->fe[0]->dtv_property_cache.layer[0].segment_count != 0) && + ((state->fe[0]->dtv_property_cache.layer[0].modulation == QAM_AUTO) || + (state->fe[0]->dtv_property_cache.layer[0].fec == FEC_AUTO))) || + (((state->fe[0]->dtv_property_cache.isdbt_layer_enabled & (1 << 1)) != 0) && + (state->fe[0]->dtv_property_cache.layer[1].segment_count != 0xff) && + (state->fe[0]->dtv_property_cache.layer[1].segment_count != 0) && + ((state->fe[0]->dtv_property_cache.layer[1].modulation == QAM_AUTO) || + (state->fe[0]->dtv_property_cache.layer[1].fec == FEC_AUTO))) || + (((state->fe[0]->dtv_property_cache.isdbt_layer_enabled & (1 << 2)) != 0) && + (state->fe[0]->dtv_property_cache.layer[2].segment_count != 0xff) && + (state->fe[0]->dtv_property_cache.layer[2].segment_count != 0) && + ((state->fe[0]->dtv_property_cache.layer[2].modulation == QAM_AUTO) || + (state->fe[0]->dtv_property_cache.layer[2].fec == FEC_AUTO))) || + (((state->fe[0]->dtv_property_cache.layer[0].segment_count == 0) || + ((state->fe[0]->dtv_property_cache.isdbt_layer_enabled & (1 << 0)) == 0)) && + ((state->fe[0]->dtv_property_cache.layer[1].segment_count == 0) || + ((state->fe[0]->dtv_property_cache.isdbt_layer_enabled & (2 << 0)) == 0)) && + ((state->fe[0]->dtv_property_cache.layer[2].segment_count == 0) || ((state->fe[0]->dtv_property_cache.isdbt_layer_enabled & (3 << 0)) == 0)))) { + int i = 100; + u8 found = 0; + u8 tune_failed = 0; + for (index_frontend = 0; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) { - delay = dib8000_tune(state->fe[index_frontend]); - if (delay != FE_CALLBACK_TIME_NEVER) - delay += systime(); - - /* we are in autosearch */ - if (state->channel_parameters_set == 0) { /* searching */ - if ((dib8000_get_status(state->fe[index_frontend]) == FE_STATUS_DEMOD_SUCCESS) || (dib8000_get_status(state->fe[index_frontend]) == FE_STATUS_FFT_SUCCESS)) { - dprintk("autosearch succeeded on fe%i", index_frontend); - dib8000_get_frontend(state->fe[index_frontend]); /* we read the channel parameters from the frontend which was successful */ - state->channel_parameters_set = 1; - - for (l = 0; (l < MAX_NUMBER_OF_FRONTENDS) && (state->fe[l] != NULL); l++) { - if (l != index_frontend) { /* and for all frontend except the successful one */ - dib8000_tune_restart_from_demod(state->fe[l]); - - state->fe[l]->dtv_property_cache.isdbt_sb_mode = state->fe[index_frontend]->dtv_property_cache.isdbt_sb_mode; - state->fe[l]->dtv_property_cache.inversion = state->fe[index_frontend]->dtv_property_cache.inversion; - state->fe[l]->dtv_property_cache.transmission_mode = state->fe[index_frontend]->dtv_property_cache.transmission_mode; - state->fe[l]->dtv_property_cache.guard_interval = state->fe[index_frontend]->dtv_property_cache.guard_interval; - state->fe[l]->dtv_property_cache.isdbt_partial_reception = state->fe[index_frontend]->dtv_property_cache.isdbt_partial_reception; - for (i = 0; i < 3; i++) { - state->fe[l]->dtv_property_cache.layer[i].segment_count = state->fe[index_frontend]->dtv_property_cache.layer[i].segment_count; - state->fe[l]->dtv_property_cache.layer[i].interleaving = state->fe[index_frontend]->dtv_property_cache.layer[i].interleaving; - state->fe[l]->dtv_property_cache.layer[i].fec = state->fe[index_frontend]->dtv_property_cache.layer[i].fec; - state->fe[l]->dtv_property_cache.layer[i].modulation = state->fe[index_frontend]->dtv_property_cache.layer[i].modulation; - } - - } - } - } - } - if (delay < callback_time) - callback_time = delay; + dib8000_set_bandwidth(state->fe[index_frontend], fe->dtv_property_cache.bandwidth_hz / 1000); + dib8000_autosearch_start(state->fe[index_frontend]); } - /* tuning is done when the master frontend is done (failed or success) */ - if (dib8000_get_status(state->fe[0]) == FE_STATUS_TUNE_FAILED || - dib8000_get_status(state->fe[0]) == FE_STATUS_LOCKED || - dib8000_get_status(state->fe[0]) == FE_STATUS_DATA_LOCKED) { - active = 0; - /* we need to wait for all frontends to be finished */ + + do { + msleep(20); + nbr_pending = 0; + exit_condition = 0; /* 0: tune pending; 1: tune failed; 2:tune success */ for (index_frontend = 0; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) { - if (dib8000_get_tune_state(state->fe[index_frontend]) != CT_DEMOD_STOP) - active = 1; + if (((tune_failed >> index_frontend) & 0x1) == 0) { + found = dib8000_autosearch_irq(state->fe[index_frontend]); + switch (found) { + case 0: /* tune pending */ + nbr_pending++; + break; + case 2: + dprintk("autosearch succeed on the frontend%i", index_frontend); + exit_condition = 2; + index_frontend_success = index_frontend; + break; + default: + dprintk("unhandled autosearch result"); + case 1: + tune_failed |= (1 << index_frontend); + dprintk("autosearch failed for the frontend%i", index_frontend); + break; + } + } } - if (active == 0) - dprintk("tuning done with status %d", dib8000_get_status(state->fe[0])); - } - if ((active == 1) && (callback_time == FE_CALLBACK_TIME_NEVER)) { - dprintk("strange callback time something went wrong"); - active = 0; + /* if all tune are done and no success, exit: tune failed */ + if ((nbr_pending == 0) && (exit_condition == 0)) + exit_condition = 1; + } while ((exit_condition == 0) && i--); + + if (exit_condition == 1) { /* tune failed */ + dprintk("tune failed"); + return 0; } - while ((active == 1) && (systime() < callback_time)) - msleep(100); - } while (active); + dprintk("tune success on frontend%i", index_frontend_success); - /* set output mode */ - if (state->revision != 0x8090) + dib8000_get_frontend(fe); + } + + for (index_frontend = 0, ret = 0; (ret >= 0) && (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) + ret = dib8000_tune(state->fe[index_frontend]); + + /* set output mode and diversity input */ + if (state->revision != 0x8090) { dib8000_set_output_mode(state->fe[0], state->cfg.output_mode); - else { + for (index_frontend = 1; + (index_frontend < MAX_NUMBER_OF_FRONTENDS) && + (state->fe[index_frontend] != NULL); + index_frontend++) { + dib8000_set_output_mode(state->fe[index_frontend], + OUTMODE_DIVERSITY); + dib8000_set_diversity_in(state->fe[index_frontend-1], 1); + } + + /* turn off the diversity of the last chip */ + dib8000_set_diversity_in(state->fe[index_frontend-1], 0); + } else { dib8096p_set_output_mode(state->fe[0], state->cfg.output_mode); if (state->cfg.enMpegOutput == 0) { dib8096p_setDibTxMux(state, MPEG_ON_DIBTX); dib8096p_setHostBusMux(state, DIBTX_ON_HOSTBUS); } + for (index_frontend = 1; + (index_frontend < MAX_NUMBER_OF_FRONTENDS) && + (state->fe[index_frontend] != NULL); + index_frontend++) { + dib8096p_set_output_mode(state->fe[index_frontend], + OUTMODE_DIVERSITY); + dib8096p_set_diversity_in(state->fe[index_frontend-1], 1); + } + + /* turn off the diversity of the last chip */ + dib8096p_set_diversity_in(state->fe[index_frontend-1], 0); } return ret; } +static u16 dib8000_read_lock(struct dvb_frontend *fe) +{ + struct dib8000_state *state = fe->demodulator_priv; + + if (state->revision == 0x8090) + return dib8000_read_word(state, 570); + return dib8000_read_word(state, 568); +} + static int dib8000_read_status(struct dvb_frontend *fe, fe_status_t * stat) { struct dib8000_state *state = fe->demodulator_priv; u16 lock_slave = 0, lock; u8 index_frontend; - lock = dib8000_read_lock(fe); + if (state->revision == 0x8090) + lock = dib8000_read_word(state, 570); + else + lock = dib8000_read_word(state, 568); + for (index_frontend = 1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) lock_slave |= dib8000_read_lock(state->fe[index_frontend]); @@ -3937,11 +3545,10 @@ struct dvb_frontend *dib8000_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr, s dib8000_reset(fe); dib8000_write_word(state, 285, (dib8000_read_word(state, 285) & ~0x60) | (3 << 5)); /* ber_rs_len = 3 */ - state->current_demod_bw = 6000; return fe; -error: + error: kfree(state); return NULL; } diff --git a/trunk/drivers/media/dvb-frontends/dib8000.h b/trunk/drivers/media/dvb-frontends/dib8000.h index b8c11e52c512..9e7a2b170d55 100644 --- a/trunk/drivers/media/dvb-frontends/dib8000.h +++ b/trunk/drivers/media/dvb-frontends/dib8000.h @@ -33,8 +33,6 @@ struct dib8000_config { u8 output_mode; u8 refclksel; u8 enMpegOutput:1; - - struct dibx000_bandwidth_config *plltable; }; #define DEFAULT_DIB8000_I2C_ADDRESS 18 @@ -60,7 +58,7 @@ extern int dib8090p_get_dc_power(struct dvb_frontend *fe, u8 IQ); extern u32 dib8000_ctrl_timf(struct dvb_frontend *fe, uint8_t op, uint32_t timf); extern int dib8000_update_pll(struct dvb_frontend *fe, - struct dibx000_bandwidth_config *pll, u32 bw, u8 ratio); + struct dibx000_bandwidth_config *pll); extern int dib8000_set_slave_frontend(struct dvb_frontend *fe, struct dvb_frontend *fe_slave); extern int dib8000_remove_slave_frontend(struct dvb_frontend *fe); extern struct dvb_frontend *dib8000_get_slave_frontend(struct dvb_frontend *fe, int slave_index); @@ -149,7 +147,7 @@ static inline u32 dib8000_ctrl_timf(struct dvb_frontend *fe, return 0; } static inline int dib8000_update_pll(struct dvb_frontend *fe, - struct dibx000_bandwidth_config *pll, u32 bw, u8 ratio) + struct dibx000_bandwidth_config *pll) { printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); return -ENODEV; diff --git a/trunk/drivers/media/dvb-frontends/dibx000_common.h b/trunk/drivers/media/dvb-frontends/dibx000_common.h index b538e0555c95..5f484881d7b1 100644 --- a/trunk/drivers/media/dvb-frontends/dibx000_common.h +++ b/trunk/drivers/media/dvb-frontends/dibx000_common.h @@ -193,8 +193,7 @@ enum frontend_tune_state { CT_DEMOD_STEP_8, CT_DEMOD_STEP_9, CT_DEMOD_STEP_10, - CT_DEMOD_STEP_11, - CT_DEMOD_SEARCH_NEXT = 51, + CT_DEMOD_SEARCH_NEXT = 41, CT_DEMOD_STEP_LOCKED, CT_DEMOD_STOP, diff --git a/trunk/drivers/media/dvb-frontends/lg2160.h b/trunk/drivers/media/dvb-frontends/lg2160.h index 194a07a78dc1..a5f036824d68 100644 --- a/trunk/drivers/media/dvb-frontends/lg2160.h +++ b/trunk/drivers/media/dvb-frontends/lg2160.h @@ -57,10 +57,10 @@ struct lg2160_config { u16 if_khz; /* disable i2c repeater - 0:repeater enabled 1:repeater disabled */ - unsigned int deny_i2c_rptr:1; + int deny_i2c_rptr:1; /* spectral inversion - 0:disabled 1:enabled */ - unsigned int spectral_inversion:1; + int spectral_inversion:1; unsigned int output_if; enum lg2160_spi_clock spi_clock; diff --git a/trunk/drivers/media/i2c/s5c73m3/s5c73m3-core.c b/trunk/drivers/media/i2c/s5c73m3/s5c73m3-core.c index cb52438e53ac..b353c50a6881 100644 --- a/trunk/drivers/media/i2c/s5c73m3/s5c73m3-core.c +++ b/trunk/drivers/media/i2c/s5c73m3/s5c73m3-core.c @@ -1457,12 +1457,6 @@ static int s5c73m3_oif_registered(struct v4l2_subdev *sd) return ret; } -static void s5c73m3_oif_unregistered(struct v4l2_subdev *sd) -{ - struct s5c73m3 *state = oif_sd_to_s5c73m3(sd); - v4l2_device_unregister_subdev(&state->sensor_sd); -} - static const struct v4l2_subdev_internal_ops s5c73m3_internal_ops = { .open = s5c73m3_open, }; @@ -1480,7 +1474,6 @@ static const struct v4l2_subdev_ops s5c73m3_subdev_ops = { static const struct v4l2_subdev_internal_ops oif_internal_ops = { .registered = s5c73m3_oif_registered, - .unregistered = s5c73m3_oif_unregistered, .open = s5c73m3_oif_open, }; @@ -1675,17 +1668,13 @@ static int s5c73m3_probe(struct i2c_client *client, static int s5c73m3_remove(struct i2c_client *client) { - struct v4l2_subdev *oif_sd = i2c_get_clientdata(client); - struct s5c73m3 *state = oif_sd_to_s5c73m3(oif_sd); - struct v4l2_subdev *sensor_sd = &state->sensor_sd; - - v4l2_device_unregister_subdev(oif_sd); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct s5c73m3 *state = sensor_sd_to_s5c73m3(sd); - v4l2_ctrl_handler_free(oif_sd->ctrl_handler); - media_entity_cleanup(&oif_sd->entity); + v4l2_device_unregister_subdev(sd); - v4l2_device_unregister_subdev(sensor_sd); - media_entity_cleanup(&sensor_sd->entity); + v4l2_ctrl_handler_free(sd->ctrl_handler); + media_entity_cleanup(&sd->entity); s5c73m3_unregister_spi_driver(state); s5c73m3_free_gpios(state); diff --git a/trunk/drivers/media/media-device.c b/trunk/drivers/media/media-device.c index 1957c0df08fd..99b80b6f7f67 100644 --- a/trunk/drivers/media/media-device.c +++ b/trunk/drivers/media/media-device.c @@ -102,12 +102,9 @@ static long media_device_enum_entities(struct media_device *mdev, return -EINVAL; u_ent.id = ent->id; - if (ent->name) { - strncpy(u_ent.name, ent->name, sizeof(u_ent.name)); - u_ent.name[sizeof(u_ent.name) - 1] = '\0'; - } else { - memset(u_ent.name, 0, sizeof(u_ent.name)); - } + u_ent.name[0] = '\0'; + if (ent->name) + strlcpy(u_ent.name, ent->name, sizeof(u_ent.name)); u_ent.type = ent->type; u_ent.revision = ent->revision; u_ent.flags = ent->flags; diff --git a/trunk/drivers/media/pci/cx88/cx88-mpeg.c b/trunk/drivers/media/pci/cx88/cx88-mpeg.c index 2d3507eb4897..c9d3182f79d5 100644 --- a/trunk/drivers/media/pci/cx88/cx88-mpeg.c +++ b/trunk/drivers/media/pci/cx88/cx88-mpeg.c @@ -532,17 +532,16 @@ static int cx8802_suspend_common(struct pci_dev *pci_dev, pm_message_t state) { struct cx8802_dev *dev = pci_get_drvdata(pci_dev); struct cx88_core *core = dev->core; - unsigned long flags; /* stop mpeg dma */ - spin_lock_irqsave(&dev->slock, flags); + spin_lock(&dev->slock); if (!list_empty(&dev->mpegq.active)) { dprintk( 2, "suspend\n" ); printk("%s: suspend mpeg\n", core->name); cx8802_stop_dma(dev); del_timer(&dev->mpegq.timeout); } - spin_unlock_irqrestore(&dev->slock, flags); + spin_unlock(&dev->slock); /* FIXME -- shutdown device */ cx88_shutdown(dev->core); @@ -559,7 +558,6 @@ static int cx8802_resume_common(struct pci_dev *pci_dev) { struct cx8802_dev *dev = pci_get_drvdata(pci_dev); struct cx88_core *core = dev->core; - unsigned long flags; int err; if (dev->state.disabled) { @@ -586,12 +584,12 @@ static int cx8802_resume_common(struct pci_dev *pci_dev) cx88_reset(dev->core); /* restart video+vbi capture */ - spin_lock_irqsave(&dev->slock, flags); + spin_lock(&dev->slock); if (!list_empty(&dev->mpegq.active)) { printk("%s: resume mpeg\n", core->name); cx8802_restart_queue(dev,&dev->mpegq); } - spin_unlock_irqrestore(&dev->slock, flags); + spin_unlock(&dev->slock); return 0; } diff --git a/trunk/drivers/media/pci/cx88/cx88-video.c b/trunk/drivers/media/pci/cx88/cx88-video.c index 1b00615fd395..e3f6181d1044 100644 --- a/trunk/drivers/media/pci/cx88/cx88-video.c +++ b/trunk/drivers/media/pci/cx88/cx88-video.c @@ -1954,10 +1954,9 @@ static int cx8800_suspend(struct pci_dev *pci_dev, pm_message_t state) { struct cx8800_dev *dev = pci_get_drvdata(pci_dev); struct cx88_core *core = dev->core; - unsigned long flags; /* stop video+vbi capture */ - spin_lock_irqsave(&dev->slock, flags); + spin_lock(&dev->slock); if (!list_empty(&dev->vidq.active)) { printk("%s/0: suspend video\n", core->name); stop_video_dma(dev); @@ -1968,7 +1967,7 @@ static int cx8800_suspend(struct pci_dev *pci_dev, pm_message_t state) cx8800_stop_vbi_dma(dev); del_timer(&dev->vbiq.timeout); } - spin_unlock_irqrestore(&dev->slock, flags); + spin_unlock(&dev->slock); if (core->ir) cx88_ir_stop(core); @@ -1987,7 +1986,6 @@ static int cx8800_resume(struct pci_dev *pci_dev) { struct cx8800_dev *dev = pci_get_drvdata(pci_dev); struct cx88_core *core = dev->core; - unsigned long flags; int err; if (dev->state.disabled) { @@ -2018,7 +2016,7 @@ static int cx8800_resume(struct pci_dev *pci_dev) cx_set(MO_PCI_INTMSK, core->pci_irqmask); /* restart video+vbi capture */ - spin_lock_irqsave(&dev->slock, flags); + spin_lock(&dev->slock); if (!list_empty(&dev->vidq.active)) { printk("%s/0: resume video\n", core->name); restart_video_queue(dev,&dev->vidq); @@ -2027,7 +2025,7 @@ static int cx8800_resume(struct pci_dev *pci_dev) printk("%s/0: resume vbi\n", core->name); cx8800_restart_vbi_queue(dev,&dev->vbiq); } - spin_unlock_irqrestore(&dev->slock, flags); + spin_unlock(&dev->slock); return 0; } diff --git a/trunk/drivers/media/platform/coda.c b/trunk/drivers/media/platform/coda.c index 5612329f9ef7..20827ba168fc 100644 --- a/trunk/drivers/media/platform/coda.c +++ b/trunk/drivers/media/platform/coda.c @@ -1422,7 +1422,6 @@ static int coda_queue_init(void *priv, struct vb2_queue *src_vq, src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); src_vq->ops = &coda_qops; src_vq->mem_ops = &vb2_dma_contig_memops; - src_vq->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_COPY; ret = vb2_queue_init(src_vq); if (ret) @@ -1434,7 +1433,6 @@ static int coda_queue_init(void *priv, struct vb2_queue *src_vq, dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); dst_vq->ops = &coda_qops; dst_vq->mem_ops = &vb2_dma_contig_memops; - dst_vq->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_COPY; return vb2_queue_init(dst_vq); } @@ -1630,9 +1628,6 @@ static irqreturn_t coda_irq_handler(int irq, void *data) dst_buf->v4l2_buf.flags &= ~V4L2_BUF_FLAG_KEYFRAME; } - dst_buf->v4l2_buf.timestamp = src_buf->v4l2_buf.timestamp; - dst_buf->v4l2_buf.timecode = src_buf->v4l2_buf.timecode; - v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_DONE); v4l2_m2m_buf_done(dst_buf, VB2_BUF_STATE_DONE); diff --git a/trunk/drivers/media/platform/exynos-gsc/gsc-m2m.c b/trunk/drivers/media/platform/exynos-gsc/gsc-m2m.c index 40a73f7d20da..386c0a7a3a52 100644 --- a/trunk/drivers/media/platform/exynos-gsc/gsc-m2m.c +++ b/trunk/drivers/media/platform/exynos-gsc/gsc-m2m.c @@ -80,9 +80,6 @@ void gsc_m2m_job_finish(struct gsc_ctx *ctx, int vb_state) dst_vb = v4l2_m2m_dst_buf_remove(ctx->m2m_ctx); if (src_vb && dst_vb) { - src_vb->v4l2_buf.timestamp = dst_vb->v4l2_buf.timestamp; - src_vb->v4l2_buf.timecode = dst_vb->v4l2_buf.timecode; - v4l2_m2m_buf_done(src_vb, vb_state); v4l2_m2m_buf_done(dst_vb, vb_state); @@ -587,7 +584,6 @@ static int queue_init(void *priv, struct vb2_queue *src_vq, src_vq->ops = &gsc_m2m_qops; src_vq->mem_ops = &vb2_dma_contig_memops; src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); - src_vq->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_COPY; ret = vb2_queue_init(src_vq); if (ret) @@ -600,7 +596,6 @@ static int queue_init(void *priv, struct vb2_queue *src_vq, dst_vq->ops = &gsc_m2m_qops; dst_vq->mem_ops = &vb2_dma_contig_memops; dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); - dst_vq->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_COPY; return vb2_queue_init(dst_vq); } diff --git a/trunk/drivers/media/platform/exynos4-is/fimc-capture.c b/trunk/drivers/media/platform/exynos4-is/fimc-capture.c index 528f41369364..72c516af40f6 100644 --- a/trunk/drivers/media/platform/exynos4-is/fimc-capture.c +++ b/trunk/drivers/media/platform/exynos4-is/fimc-capture.c @@ -742,13 +742,16 @@ static void fimc_capture_try_selection(struct fimc_ctx *ctx, /* * The video node ioctl operations */ -static int fimc_cap_querycap(struct file *file, void *priv, +static int fimc_vidioc_querycap_capture(struct file *file, void *priv, struct v4l2_capability *cap) { struct fimc_dev *fimc = video_drvdata(file); - __fimc_vidioc_querycap(&fimc->pdev->dev, cap, V4L2_CAP_STREAMING | - V4L2_CAP_VIDEO_CAPTURE_MPLANE); + strncpy(cap->driver, fimc->pdev->name, sizeof(cap->driver) - 1); + strncpy(cap->card, fimc->pdev->name, sizeof(cap->card) - 1); + cap->bus_info[0] = 0; + cap->capabilities = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_CAPTURE_MPLANE; + return 0; } @@ -1372,7 +1375,7 @@ static int fimc_cap_s_selection(struct file *file, void *fh, } static const struct v4l2_ioctl_ops fimc_capture_ioctl_ops = { - .vidioc_querycap = fimc_cap_querycap, + .vidioc_querycap = fimc_vidioc_querycap_capture, .vidioc_enum_fmt_vid_cap_mplane = fimc_cap_enum_fmt_mplane, .vidioc_try_fmt_vid_cap_mplane = fimc_cap_try_fmt_mplane, @@ -1866,7 +1869,7 @@ int fimc_initialize_capture_subdev(struct fimc_dev *fimc) int ret; v4l2_subdev_init(sd, &fimc_subdev_ops); - sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + sd->flags = V4L2_SUBDEV_FL_HAS_DEVNODE; snprintf(sd->name, sizeof(sd->name), "FIMC.%d", fimc->id); fimc->vid_cap.sd_pads[FIMC_SD_PAD_SINK_CAM].flags = MEDIA_PAD_FL_SINK; diff --git a/trunk/drivers/media/platform/exynos4-is/fimc-core.c b/trunk/drivers/media/platform/exynos4-is/fimc-core.c index 379a5e9d52a7..f25807d7bc8a 100644 --- a/trunk/drivers/media/platform/exynos4-is/fimc-core.c +++ b/trunk/drivers/media/platform/exynos4-is/fimc-core.c @@ -213,17 +213,6 @@ struct fimc_fmt *fimc_get_format(unsigned int index) return &fimc_formats[index]; } -void __fimc_vidioc_querycap(struct device *dev, struct v4l2_capability *cap, - unsigned int caps) -{ - strlcpy(cap->driver, dev->driver->name, sizeof(cap->driver)); - strlcpy(cap->card, dev->driver->name, sizeof(cap->card)); - snprintf(cap->bus_info, sizeof(cap->bus_info), - "platform:%s", dev_name(dev)); - cap->device_caps = caps; - cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; -} - int fimc_check_scaler_ratio(struct fimc_ctx *ctx, int sw, int sh, int dw, int dh, int rotation) { @@ -966,8 +955,8 @@ static int fimc_probe(struct platform_device *pdev) } if (!fimc->drv_data || fimc->id >= fimc->drv_data->num_entities || fimc->id < 0) { - dev_err(dev, "Invalid driver data or device id (%d)\n", - fimc->id); + dev_err(dev, "Invalid driver data or device id (%d/%d)\n", + fimc->id, fimc->drv_data->num_entities); return -EINVAL; } if (!dev->of_node) @@ -1294,7 +1283,7 @@ static struct platform_driver fimc_driver = { .id_table = fimc_driver_ids, .driver = { .of_match_table = fimc_of_match, - .name = FIMC_DRIVER_NAME, + .name = FIMC_MODULE_NAME, .owner = THIS_MODULE, .pm = &fimc_pm_ops, } diff --git a/trunk/drivers/media/platform/exynos4-is/fimc-core.h b/trunk/drivers/media/platform/exynos4-is/fimc-core.h index 539a3f71c16a..d2fe162485a3 100644 --- a/trunk/drivers/media/platform/exynos4-is/fimc-core.h +++ b/trunk/drivers/media/platform/exynos4-is/fimc-core.h @@ -35,7 +35,7 @@ /* Time to wait for next frame VSYNC interrupt while stopping operation. */ #define FIMC_SHUTDOWN_TIMEOUT ((100*HZ)/1000) #define MAX_FIMC_CLOCKS 2 -#define FIMC_DRIVER_NAME "exynos4-fimc" +#define FIMC_MODULE_NAME "s5p-fimc" #define FIMC_MAX_DEVS 4 #define FIMC_MAX_OUT_BUFS 4 #define SCALER_MAX_HRATIO 64 @@ -425,7 +425,7 @@ struct fimc_dev { struct regmap *sysreg; const struct fimc_variant *variant; const struct fimc_drvdata *drv_data; - int id; + u16 id; struct clk *clock[MAX_FIMC_CLOCKS]; void __iomem *regs; wait_queue_head_t irq_queue; @@ -620,8 +620,6 @@ static inline struct fimc_frame *ctx_get_frame(struct fimc_ctx *ctx, /* fimc-core.c */ int fimc_vidioc_enum_fmt_mplane(struct file *file, void *priv, struct v4l2_fmtdesc *f); -void __fimc_vidioc_querycap(struct device *dev, struct v4l2_capability *cap, - unsigned int caps); int fimc_ctrls_create(struct fimc_ctx *ctx); void fimc_ctrls_delete(struct fimc_ctx *ctx); void fimc_ctrls_activate(struct fimc_ctx *ctx, bool active); diff --git a/trunk/drivers/media/platform/exynos4-is/fimc-is-i2c.c b/trunk/drivers/media/platform/exynos4-is/fimc-is-i2c.c index c397777d7cbb..1ec6b3c6a62a 100644 --- a/trunk/drivers/media/platform/exynos4-is/fimc-is-i2c.c +++ b/trunk/drivers/media/platform/exynos4-is/fimc-is-i2c.c @@ -103,6 +103,7 @@ static const struct of_device_id fimc_is_i2c_of_match[] = { { .compatible = FIMC_IS_I2C_COMPATIBLE }, { }, }; +MODULE_DEVICE_TABLE(of, fimc_is_i2c_of_match); static struct platform_driver fimc_is_i2c_driver = { .probe = fimc_is_i2c_probe, @@ -119,8 +120,10 @@ int fimc_is_register_i2c_driver(void) { return platform_driver_register(&fimc_is_i2c_driver); } +EXPORT_SYMBOL(fimc_is_register_i2c_driver); void fimc_is_unregister_i2c_driver(void) { platform_driver_unregister(&fimc_is_i2c_driver); } +EXPORT_SYMBOL(fimc_is_unregister_i2c_driver); diff --git a/trunk/drivers/media/platform/exynos4-is/fimc-is-sensor.c b/trunk/drivers/media/platform/exynos4-is/fimc-is-sensor.c index 6647421e5d3a..02b27190d2ae 100644 --- a/trunk/drivers/media/platform/exynos4-is/fimc-is-sensor.c +++ b/trunk/drivers/media/platform/exynos4-is/fimc-is-sensor.c @@ -40,6 +40,11 @@ static const struct v4l2_mbus_framefmt fimc_is_sensor_formats[] = { } }; +static struct fimc_is_sensor *sd_to_fimc_is_sensor(struct v4l2_subdev *sd) +{ + return container_of(sd, struct fimc_is_sensor, subdev); +} + static const struct v4l2_mbus_framefmt *find_sensor_format( struct v4l2_mbus_framefmt *mf) { @@ -142,7 +147,7 @@ static const struct v4l2_subdev_internal_ops fimc_is_sensor_sd_internal_ops = { static int fimc_is_sensor_s_power(struct v4l2_subdev *sd, int on) { - struct fimc_is_sensor *sensor = sd_to_fimc_is_sensor(sd); + struct fimc_is_sensor *sensor = v4l2_get_subdevdata(sd); int gpio = sensor->gpio_reset; int ret; @@ -211,8 +216,7 @@ static int fimc_is_sensor_probe(struct i2c_client *client, gpio = of_get_gpio_flags(dev->of_node, 0, NULL); if (gpio_is_valid(gpio)) { - ret = devm_gpio_request_one(dev, gpio, GPIOF_OUT_INIT_LOW, - DRIVER_NAME); + ret = gpio_request_one(gpio, GPIOF_OUT_INIT_LOW, DRIVER_NAME); if (ret < 0) return ret; } @@ -224,11 +228,13 @@ static int fimc_is_sensor_probe(struct i2c_client *client, ret = devm_regulator_bulk_get(&client->dev, SENSOR_NUM_SUPPLIES, sensor->supplies); if (ret < 0) - return ret; + goto err_gpio; of_id = of_match_node(fimc_is_sensor_of_match, dev->of_node); - if (!of_id) - return -ENODEV; + if (!of_id) { + ret = -ENODEV; + goto err_reg; + } sensor->drvdata = of_id->data; sensor->dev = dev; @@ -245,18 +251,28 @@ static int fimc_is_sensor_probe(struct i2c_client *client, sensor->pad.flags = MEDIA_PAD_FL_SOURCE; ret = media_entity_init(&sd->entity, 1, &sensor->pad, 0); if (ret < 0) - return ret; + goto err_reg; + v4l2_set_subdevdata(sd, sensor); pm_runtime_no_callbacks(dev); pm_runtime_enable(dev); + return 0; +err_reg: + regulator_bulk_free(SENSOR_NUM_SUPPLIES, sensor->supplies); +err_gpio: + if (gpio_is_valid(sensor->gpio_reset)) + gpio_free(sensor->gpio_reset); return ret; } static int fimc_is_sensor_remove(struct i2c_client *client) { - struct v4l2_subdev *sd = i2c_get_clientdata(client); - media_entity_cleanup(&sd->entity); + struct fimc_is_sensor *sensor; + + regulator_bulk_free(SENSOR_NUM_SUPPLIES, sensor->supplies); + media_entity_cleanup(&sensor->subdev.entity); + return 0; } @@ -278,6 +294,7 @@ static const struct of_device_id fimc_is_sensor_of_match[] = { }, { } }; +MODULE_DEVICE_TABLE(of, fimc_is_sensor_of_match); static struct i2c_driver fimc_is_sensor_driver = { .driver = { diff --git a/trunk/drivers/media/platform/exynos4-is/fimc-is-sensor.h b/trunk/drivers/media/platform/exynos4-is/fimc-is-sensor.h index 6036d49a6c68..50b8e4d59d62 100644 --- a/trunk/drivers/media/platform/exynos4-is/fimc-is-sensor.h +++ b/trunk/drivers/media/platform/exynos4-is/fimc-is-sensor.h @@ -77,12 +77,6 @@ struct fimc_is_sensor { struct v4l2_mbus_framefmt format; }; -static inline -struct fimc_is_sensor *sd_to_fimc_is_sensor(struct v4l2_subdev *sd) -{ - return container_of(sd, struct fimc_is_sensor, subdev); -} - int fimc_is_register_sensor_driver(void); void fimc_is_unregister_sensor_driver(void); diff --git a/trunk/drivers/media/platform/exynos4-is/fimc-is.c b/trunk/drivers/media/platform/exynos4-is/fimc-is.c index 47c6363d04e2..3c81c882bfd1 100644 --- a/trunk/drivers/media/platform/exynos4-is/fimc-is.c +++ b/trunk/drivers/media/platform/exynos4-is/fimc-is.c @@ -220,7 +220,7 @@ static int fimc_is_register_subdevs(struct fimc_is *is) if (WARN_ON(is->sensor)) continue; - is->sensor = sd_to_fimc_is_sensor(sd); + is->sensor = v4l2_get_subdevdata(sd); if (fimc_is_parse_sensor_config(is->sensor, child)) { dev_warn(&is->pdev->dev, "DT parse error: %s\n", @@ -766,7 +766,7 @@ static const struct file_operations fimc_is_debugfs_fops = { static void fimc_is_debugfs_remove(struct fimc_is *is) { - debugfs_remove_recursive(is->debugfs_entry); + debugfs_remove(is->debugfs_entry); is->debugfs_entry = NULL; } @@ -847,17 +847,16 @@ static int fimc_is_probe(struct platform_device *pdev) goto err_irq; ret = fimc_is_setup_clocks(is); - pm_runtime_put_sync(dev); - if (ret < 0) goto err_irq; + pm_runtime_put_sync(dev); is->clk_init = true; is->alloc_ctx = vb2_dma_contig_init_ctx(dev); if (IS_ERR(is->alloc_ctx)) { ret = PTR_ERR(is->alloc_ctx); - goto err_irq; + goto err_pm; } /* * Register FIMC-IS V4L2 subdevs to this driver. The video nodes @@ -886,6 +885,8 @@ static int fimc_is_probe(struct platform_device *pdev) fimc_is_unregister_subdevs(is); err_irq: free_irq(is->irq, is); +err_pm: + pm_runtime_put(dev); err_clk: fimc_is_put_clocks(is); return ret; @@ -994,9 +995,9 @@ static int fimc_is_module_init(void) static void fimc_is_module_exit(void) { - fimc_is_unregister_sensor_driver(); - fimc_is_unregister_i2c_driver(); platform_driver_unregister(&fimc_is_driver); + fimc_is_unregister_i2c_driver(); + fimc_is_unregister_sensor_driver(); } module_init(fimc_is_module_init); diff --git a/trunk/drivers/media/platform/exynos4-is/fimc-isp.c b/trunk/drivers/media/platform/exynos4-is/fimc-isp.c index d63947f7b302..3b9a6642a491 100644 --- a/trunk/drivers/media/platform/exynos4-is/fimc-isp.c +++ b/trunk/drivers/media/platform/exynos4-is/fimc-isp.c @@ -621,7 +621,7 @@ int fimc_isp_subdev_create(struct fimc_isp *isp) v4l2_subdev_init(sd, &fimc_is_subdev_ops); sd->grp_id = GRP_ID_FIMC_IS; - sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + sd->flags = V4L2_SUBDEV_FL_HAS_DEVNODE; snprintf(sd->name, sizeof(sd->name), "FIMC-IS-ISP"); isp->subdev_pads[FIMC_ISP_SD_PAD_SINK].flags = MEDIA_PAD_FL_SINK; diff --git a/trunk/drivers/media/platform/exynos4-is/fimc-lite.c b/trunk/drivers/media/platform/exynos4-is/fimc-lite.c index 14bb7bc8adbe..661d0d148cb5 100644 --- a/trunk/drivers/media/platform/exynos4-is/fimc-lite.c +++ b/trunk/drivers/media/platform/exynos4-is/fimc-lite.c @@ -1377,7 +1377,7 @@ static int fimc_lite_create_capture_subdev(struct fimc_lite *fimc) int ret; v4l2_subdev_init(sd, &fimc_lite_subdev_ops); - sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + sd->flags = V4L2_SUBDEV_FL_HAS_DEVNODE; snprintf(sd->name, sizeof(sd->name), "FIMC-LITE.%d", fimc->index); fimc->subdev_pads[FLITE_SD_PAD_SINK].flags = MEDIA_PAD_FL_SINK; @@ -1399,7 +1399,6 @@ static int fimc_lite_create_capture_subdev(struct fimc_lite *fimc) sd->ctrl_handler = handler; sd->internal_ops = &fimc_lite_subdev_internal_ops; sd->entity.ops = &fimc_lite_subdev_media_ops; - sd->owner = THIS_MODULE; v4l2_set_subdevdata(sd, fimc); return 0; diff --git a/trunk/drivers/media/platform/exynos4-is/fimc-lite.h b/trunk/drivers/media/platform/exynos4-is/fimc-lite.h index 47da5e049247..71fed5129e30 100644 --- a/trunk/drivers/media/platform/exynos4-is/fimc-lite.h +++ b/trunk/drivers/media/platform/exynos4-is/fimc-lite.h @@ -140,7 +140,7 @@ struct fimc_lite { struct v4l2_subdev *sensor; struct v4l2_ctrl_handler ctrl_handler; struct v4l2_ctrl *test_pattern; - int index; + u32 index; struct fimc_pipeline pipeline; const struct fimc_pipeline_ops *pipeline_ops; diff --git a/trunk/drivers/media/platform/exynos4-is/fimc-m2m.c b/trunk/drivers/media/platform/exynos4-is/fimc-m2m.c index bde1f47f7ed3..8449c0705e60 100644 --- a/trunk/drivers/media/platform/exynos4-is/fimc-m2m.c +++ b/trunk/drivers/media/platform/exynos4-is/fimc-m2m.c @@ -99,7 +99,7 @@ static int stop_streaming(struct vb2_queue *q) static void fimc_device_run(void *priv) { - struct vb2_buffer *src_vb, *dst_vb; + struct vb2_buffer *vb = NULL; struct fimc_ctx *ctx = priv; struct fimc_frame *sf, *df; struct fimc_dev *fimc; @@ -122,18 +122,16 @@ static void fimc_device_run(void *priv) fimc_prepare_dma_offset(ctx, df); } - src_vb = v4l2_m2m_next_src_buf(ctx->m2m_ctx); - ret = fimc_prepare_addr(ctx, src_vb, sf, &sf->paddr); + vb = v4l2_m2m_next_src_buf(ctx->m2m_ctx); + ret = fimc_prepare_addr(ctx, vb, sf, &sf->paddr); if (ret) goto dma_unlock; - dst_vb = v4l2_m2m_next_dst_buf(ctx->m2m_ctx); - ret = fimc_prepare_addr(ctx, dst_vb, df, &df->paddr); + vb = v4l2_m2m_next_dst_buf(ctx->m2m_ctx); + ret = fimc_prepare_addr(ctx, vb, df, &df->paddr); if (ret) goto dma_unlock; - dst_vb->v4l2_buf.timestamp = src_vb->v4l2_buf.timestamp; - /* Reconfigure hardware if the context has changed. */ if (fimc->m2m.ctx != ctx) { ctx->state |= FIMC_PARAMS; @@ -251,20 +249,22 @@ static struct vb2_ops fimc_qops = { * V4L2 ioctl handlers */ static int fimc_m2m_querycap(struct file *file, void *fh, - struct v4l2_capability *cap) + struct v4l2_capability *cap) { - struct fimc_dev *fimc = video_drvdata(file); - unsigned int caps; + struct fimc_ctx *ctx = fh_to_ctx(fh); + struct fimc_dev *fimc = ctx->fimc_dev; + strncpy(cap->driver, fimc->pdev->name, sizeof(cap->driver) - 1); + strncpy(cap->card, fimc->pdev->name, sizeof(cap->card) - 1); + cap->bus_info[0] = 0; /* * This is only a mem-to-mem video device. The capture and output * device capability flags are left only for backward compatibility * and are scheduled for removal. */ - caps = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_M2M_MPLANE | + cap->capabilities = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_M2M_MPLANE | V4L2_CAP_VIDEO_CAPTURE_MPLANE | V4L2_CAP_VIDEO_OUTPUT_MPLANE; - __fimc_vidioc_querycap(&fimc->pdev->dev, cap, caps); return 0; } @@ -622,7 +622,6 @@ static int queue_init(void *priv, struct vb2_queue *src_vq, src_vq->ops = &fimc_qops; src_vq->mem_ops = &vb2_dma_contig_memops; src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); - src_vq->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_COPY; ret = vb2_queue_init(src_vq); if (ret) @@ -634,7 +633,6 @@ static int queue_init(void *priv, struct vb2_queue *src_vq, dst_vq->ops = &fimc_qops; dst_vq->mem_ops = &vb2_dma_contig_memops; dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); - dst_vq->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_COPY; return vb2_queue_init(dst_vq); } diff --git a/trunk/drivers/media/platform/exynos4-is/media-dev.c b/trunk/drivers/media/platform/exynos4-is/media-dev.c index 15ef8f28239b..1dbd55422706 100644 --- a/trunk/drivers/media/platform/exynos4-is/media-dev.c +++ b/trunk/drivers/media/platform/exynos4-is/media-dev.c @@ -814,6 +814,7 @@ static void fimc_md_unregister_entities(struct fimc_md *fmd) if (fmd->csis[i].sd == NULL) continue; v4l2_device_unregister_subdev(fmd->csis[i].sd); + module_put(fmd->csis[i].sd->owner); fmd->csis[i].sd = NULL; } for (i = 0; i < fmd->num_sensors; i++) { @@ -822,10 +823,6 @@ static void fimc_md_unregister_entities(struct fimc_md *fmd) fimc_md_unregister_sensor(fmd->sensor[i].subdev); fmd->sensor[i].subdev = NULL; } - - if (fmd->fimc_is) - v4l2_device_unregister_subdev(&fmd->fimc_is->isp.subdev); - v4l2_info(&fmd->v4l2_dev, "Unregistered all entities\n"); } diff --git a/trunk/drivers/media/platform/exynos4-is/mipi-csis.c b/trunk/drivers/media/platform/exynos4-is/mipi-csis.c index a2eda9d5ac87..8636bcddde1b 100644 --- a/trunk/drivers/media/platform/exynos4-is/mipi-csis.c +++ b/trunk/drivers/media/platform/exynos4-is/mipi-csis.c @@ -549,10 +549,10 @@ static struct csis_pix_format const *s5pcsis_try_format( static struct v4l2_mbus_framefmt *__s5pcsis_get_format( struct csis_state *state, struct v4l2_subdev_fh *fh, - enum v4l2_subdev_format_whence which) + u32 pad, enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_TRY) - return fh ? v4l2_subdev_get_try_format(fh, 0) : NULL; + return fh ? v4l2_subdev_get_try_format(fh, pad) : NULL; return &state->format; } @@ -564,7 +564,10 @@ static int s5pcsis_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, struct csis_pix_format const *csis_fmt; struct v4l2_mbus_framefmt *mf; - mf = __s5pcsis_get_format(state, fh, fmt->which); + if (fmt->pad != CSIS_PAD_SOURCE && fmt->pad != CSIS_PAD_SINK) + return -EINVAL; + + mf = __s5pcsis_get_format(state, fh, fmt->pad, fmt->which); if (fmt->pad == CSIS_PAD_SOURCE) { if (mf) { @@ -591,7 +594,10 @@ static int s5pcsis_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, struct csis_state *state = sd_to_csis_state(sd); struct v4l2_mbus_framefmt *mf; - mf = __s5pcsis_get_format(state, fh, fmt->which); + if (fmt->pad != CSIS_PAD_SOURCE && fmt->pad != CSIS_PAD_SINK) + return -EINVAL; + + mf = __s5pcsis_get_format(state, fh, fmt->pad, fmt->which); if (!mf) return -EINVAL; diff --git a/trunk/drivers/media/platform/m2m-deinterlace.c b/trunk/drivers/media/platform/m2m-deinterlace.c index 758564649589..6c4db9b98989 100644 --- a/trunk/drivers/media/platform/m2m-deinterlace.c +++ b/trunk/drivers/media/platform/m2m-deinterlace.c @@ -207,9 +207,6 @@ static void dma_callback(void *data) src_vb = v4l2_m2m_src_buf_remove(curr_ctx->m2m_ctx); dst_vb = v4l2_m2m_dst_buf_remove(curr_ctx->m2m_ctx); - src_vb->v4l2_buf.timestamp = dst_vb->v4l2_buf.timestamp; - src_vb->v4l2_buf.timecode = dst_vb->v4l2_buf.timecode; - v4l2_m2m_buf_done(src_vb, VB2_BUF_STATE_DONE); v4l2_m2m_buf_done(dst_vb, VB2_BUF_STATE_DONE); @@ -869,7 +866,6 @@ static int queue_init(void *priv, struct vb2_queue *src_vq, src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); src_vq->ops = &deinterlace_qops; src_vq->mem_ops = &vb2_dma_contig_memops; - src_vq->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_COPY; q_data[V4L2_M2M_SRC].fmt = &formats[0]; q_data[V4L2_M2M_SRC].width = 640; q_data[V4L2_M2M_SRC].height = 480; @@ -886,7 +882,6 @@ static int queue_init(void *priv, struct vb2_queue *src_vq, dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); dst_vq->ops = &deinterlace_qops; dst_vq->mem_ops = &vb2_dma_contig_memops; - dst_vq->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_COPY; q_data[V4L2_M2M_DST].fmt = &formats[0]; q_data[V4L2_M2M_DST].width = 640; q_data[V4L2_M2M_DST].height = 480; diff --git a/trunk/drivers/media/platform/mem2mem_testdev.c b/trunk/drivers/media/platform/mem2mem_testdev.c index 4cc7f65d7d76..7487d7208dea 100644 --- a/trunk/drivers/media/platform/mem2mem_testdev.c +++ b/trunk/drivers/media/platform/mem2mem_testdev.c @@ -38,10 +38,6 @@ MODULE_AUTHOR("Pawel Osciak, "); MODULE_LICENSE("GPL"); MODULE_VERSION("0.1.1"); -static unsigned debug; -module_param(debug, uint, 0644); -MODULE_PARM_DESC(debug, "activates debug info"); - #define MIN_W 32 #define MIN_H 32 #define MAX_W 640 @@ -71,7 +67,7 @@ MODULE_PARM_DESC(debug, "activates debug info"); #define MEM2MEM_VFLIP (1 << 1) #define dprintk(dev, fmt, arg...) \ - v4l2_dbg(1, debug, &dev->v4l2_dev, "%s: " fmt, __func__, ## arg) + v4l2_dbg(1, 1, &dev->v4l2_dev, "%s: " fmt, __func__, ## arg) static void m2mtest_dev_release(struct device *dev) @@ -238,10 +234,6 @@ static int device_process(struct m2mtest_ctx *ctx, bytes_left = bytesperline - tile_w * MEM2MEM_NUM_TILES; w = 0; - memcpy(&out_vb->v4l2_buf.timestamp, - &in_vb->v4l2_buf.timestamp, - sizeof(struct timeval)); - switch (ctx->mode) { case MEM2MEM_HFLIP | MEM2MEM_VFLIP: p_out += bytesperline * height - bytes_left; @@ -852,7 +844,6 @@ static int queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *ds src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); src_vq->ops = &m2mtest_qops; src_vq->mem_ops = &vb2_vmalloc_memops; - src_vq->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_COPY; ret = vb2_queue_init(src_vq); if (ret) @@ -864,7 +855,6 @@ static int queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *ds dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); dst_vq->ops = &m2mtest_qops; dst_vq->mem_ops = &vb2_vmalloc_memops; - dst_vq->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_COPY; return vb2_queue_init(dst_vq); } diff --git a/trunk/drivers/media/platform/mx2_emmaprp.c b/trunk/drivers/media/platform/mx2_emmaprp.c index f7440e585b6b..4b9e0a28616a 100644 --- a/trunk/drivers/media/platform/mx2_emmaprp.c +++ b/trunk/drivers/media/platform/mx2_emmaprp.c @@ -377,9 +377,6 @@ static irqreturn_t emmaprp_irq(int irq_emma, void *data) src_vb = v4l2_m2m_src_buf_remove(curr_ctx->m2m_ctx); dst_vb = v4l2_m2m_dst_buf_remove(curr_ctx->m2m_ctx); - src_vb->v4l2_buf.timestamp = dst_vb->v4l2_buf.timestamp; - src_vb->v4l2_buf.timecode = dst_vb->v4l2_buf.timecode; - spin_lock_irqsave(&pcdev->irqlock, flags); v4l2_m2m_buf_done(src_vb, VB2_BUF_STATE_DONE); v4l2_m2m_buf_done(dst_vb, VB2_BUF_STATE_DONE); @@ -766,7 +763,6 @@ static int queue_init(void *priv, struct vb2_queue *src_vq, src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); src_vq->ops = &emmaprp_qops; src_vq->mem_ops = &vb2_dma_contig_memops; - src_vq->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_COPY; ret = vb2_queue_init(src_vq); if (ret) @@ -778,7 +774,6 @@ static int queue_init(void *priv, struct vb2_queue *src_vq, dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); dst_vq->ops = &emmaprp_qops; dst_vq->mem_ops = &vb2_dma_contig_memops; - dst_vq->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_COPY; return vb2_queue_init(dst_vq); } diff --git a/trunk/drivers/media/platform/s5p-g2d/g2d.c b/trunk/drivers/media/platform/s5p-g2d/g2d.c index 553d87e5ceab..14d663dacdbd 100644 --- a/trunk/drivers/media/platform/s5p-g2d/g2d.c +++ b/trunk/drivers/media/platform/s5p-g2d/g2d.c @@ -158,7 +158,6 @@ static int queue_init(void *priv, struct vb2_queue *src_vq, src_vq->ops = &g2d_qops; src_vq->mem_ops = &vb2_dma_contig_memops; src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); - src_vq->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_COPY; ret = vb2_queue_init(src_vq); if (ret) @@ -170,7 +169,6 @@ static int queue_init(void *priv, struct vb2_queue *src_vq, dst_vq->ops = &g2d_qops; dst_vq->mem_ops = &vb2_dma_contig_memops; dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); - dst_vq->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_COPY; return vb2_queue_init(dst_vq); } @@ -637,9 +635,6 @@ static irqreturn_t g2d_isr(int irq, void *prv) BUG_ON(src == NULL); BUG_ON(dst == NULL); - dst->v4l2_buf.timecode = src->v4l2_buf.timecode; - dst->v4l2_buf.timestamp = src->v4l2_buf.timestamp; - v4l2_m2m_buf_done(src, VB2_BUF_STATE_DONE); v4l2_m2m_buf_done(dst, VB2_BUF_STATE_DONE); v4l2_m2m_job_finish(dev->m2m_dev, ctx->m2m_ctx); diff --git a/trunk/drivers/media/platform/s5p-jpeg/jpeg-core.c b/trunk/drivers/media/platform/s5p-jpeg/jpeg-core.c index 15d23968d1de..3b023752bcb4 100644 --- a/trunk/drivers/media/platform/s5p-jpeg/jpeg-core.c +++ b/trunk/drivers/media/platform/s5p-jpeg/jpeg-core.c @@ -1229,7 +1229,6 @@ static int queue_init(void *priv, struct vb2_queue *src_vq, src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); src_vq->ops = &s5p_jpeg_qops; src_vq->mem_ops = &vb2_dma_contig_memops; - src_vq->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_COPY; ret = vb2_queue_init(src_vq); if (ret) @@ -1241,7 +1240,6 @@ static int queue_init(void *priv, struct vb2_queue *src_vq, dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); dst_vq->ops = &s5p_jpeg_qops; dst_vq->mem_ops = &vb2_dma_contig_memops; - dst_vq->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_COPY; return vb2_queue_init(dst_vq); } @@ -1289,9 +1287,6 @@ static irqreturn_t s5p_jpeg_irq(int irq, void *dev_id) payload_size = jpeg_compressed_size(jpeg->regs); } - dst_buf->v4l2_buf.timecode = src_buf->v4l2_buf.timecode; - dst_buf->v4l2_buf.timestamp = src_buf->v4l2_buf.timestamp; - v4l2_m2m_buf_done(src_buf, state); if (curr_ctx->mode == S5P_JPEG_ENCODE) vb2_set_plane_payload(dst_buf, 0, payload_size); diff --git a/trunk/drivers/media/platform/s5p-mfc/s5p_mfc.c b/trunk/drivers/media/platform/s5p-mfc/s5p_mfc.c index 01f9ae0dadb0..e810b1abf40e 100644 --- a/trunk/drivers/media/platform/s5p-mfc/s5p_mfc.c +++ b/trunk/drivers/media/platform/s5p-mfc/s5p_mfc.c @@ -243,10 +243,12 @@ static void s5p_mfc_handle_frame_copy_time(struct s5p_mfc_ctx *ctx) src_buf = list_entry(ctx->src_queue.next, struct s5p_mfc_buf, list); list_for_each_entry(dst_buf, &ctx->dst_queue, list) { if (vb2_dma_contig_plane_dma_addr(dst_buf->b, 0) == dec_y_addr) { - dst_buf->b->v4l2_buf.timecode = - src_buf->b->v4l2_buf.timecode; - dst_buf->b->v4l2_buf.timestamp = - src_buf->b->v4l2_buf.timestamp; + memcpy(&dst_buf->b->v4l2_buf.timecode, + &src_buf->b->v4l2_buf.timecode, + sizeof(struct v4l2_timecode)); + memcpy(&dst_buf->b->v4l2_buf.timestamp, + &src_buf->b->v4l2_buf.timestamp, + sizeof(struct timeval)); switch (frame_type) { case S5P_FIMV_DECODE_FRAME_I_FRAME: dst_buf->b->v4l2_buf.flags |= @@ -1108,8 +1110,7 @@ static int s5p_mfc_probe(struct platform_device *pdev) } if (pdev->dev.of_node) { - ret = s5p_mfc_alloc_memdevs(dev); - if (ret < 0) + if (s5p_mfc_alloc_memdevs(dev) < 0) goto err_res; } else { dev->mem_dev_l = device_find_child(&dev->plat_dev->dev, diff --git a/trunk/drivers/media/radio/Kconfig b/trunk/drivers/media/radio/Kconfig index 24e64a09884c..fef427e386c1 100644 --- a/trunk/drivers/media/radio/Kconfig +++ b/trunk/drivers/media/radio/Kconfig @@ -18,6 +18,23 @@ config RADIO_SI470X source "drivers/media/radio/si470x/Kconfig" +config RADIO_SI476X + tristate "Silicon Laboratories Si476x I2C FM Radio" + depends on I2C && VIDEO_V4L2 && SND && SND_SOC + select MFD_CORE + select MFD_SI476X_CORE + select SND_SOC_SI476X + ---help--- + Choose Y here if you have this FM radio chip. + + In order to control your radio card, you will need to use programs + that are compatible with the Video For Linux 2 API. Information on + this API and pointers to "v4l2" programs may be found at + . + + To compile this driver as a module, choose M here: the + module will be called radio-si476x. + config USB_MR800 tristate "AverMedia MR 800 USB FM radio support" depends on USB && VIDEO_V4L2 diff --git a/trunk/drivers/media/radio/Makefile b/trunk/drivers/media/radio/Makefile index 303eaebdb85a..0dcdb320cfc7 100644 --- a/trunk/drivers/media/radio/Makefile +++ b/trunk/drivers/media/radio/Makefile @@ -19,6 +19,7 @@ obj-$(CONFIG_RADIO_GEMTEK) += radio-gemtek.o obj-$(CONFIG_RADIO_TRUST) += radio-trust.o obj-$(CONFIG_I2C_SI4713) += si4713-i2c.o obj-$(CONFIG_RADIO_SI4713) += radio-si4713.o +obj-$(CONFIG_RADIO_SI476X) += radio-si476x.o obj-$(CONFIG_RADIO_MIROPCM20) += radio-miropcm20.o obj-$(CONFIG_USB_DSBR) += dsbr100.o obj-$(CONFIG_RADIO_SI470X) += si470x/ diff --git a/trunk/drivers/media/radio/radio-si476x.c b/trunk/drivers/media/radio/radio-si476x.c new file mode 100644 index 000000000000..9430c6a29937 --- /dev/null +++ b/trunk/drivers/media/radio/radio-si476x.c @@ -0,0 +1,1599 @@ +/* + * drivers/media/radio/radio-si476x.c -- V4L2 driver for SI476X chips + * + * Copyright (C) 2012 Innovative Converged Devices(ICD) + * Copyright (C) 2013 Andrey Smirnov + * + * Author: Andrey Smirnov + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define FM_FREQ_RANGE_LOW 64000000 +#define FM_FREQ_RANGE_HIGH 108000000 + +#define AM_FREQ_RANGE_LOW 520000 +#define AM_FREQ_RANGE_HIGH 30000000 + +#define PWRLINEFLTR (1 << 8) + +#define FREQ_MUL (10000000 / 625) + +#define SI476X_PHDIV_STATUS_LINK_LOCKED(status) (0b10000000 & (status)) + +#define DRIVER_NAME "si476x-radio" +#define DRIVER_CARD "SI476x AM/FM Receiver" + +enum si476x_freq_bands { + SI476X_BAND_FM, + SI476X_BAND_AM, +}; + +static const struct v4l2_frequency_band si476x_bands[] = { + [SI476X_BAND_FM] = { + .type = V4L2_TUNER_RADIO, + .index = SI476X_BAND_FM, + .capability = V4L2_TUNER_CAP_LOW + | V4L2_TUNER_CAP_STEREO + | V4L2_TUNER_CAP_RDS + | V4L2_TUNER_CAP_RDS_BLOCK_IO + | V4L2_TUNER_CAP_FREQ_BANDS, + .rangelow = 64 * FREQ_MUL, + .rangehigh = 108 * FREQ_MUL, + .modulation = V4L2_BAND_MODULATION_FM, + }, + [SI476X_BAND_AM] = { + .type = V4L2_TUNER_RADIO, + .index = SI476X_BAND_AM, + .capability = V4L2_TUNER_CAP_LOW + | V4L2_TUNER_CAP_FREQ_BANDS, + .rangelow = 0.52 * FREQ_MUL, + .rangehigh = 30 * FREQ_MUL, + .modulation = V4L2_BAND_MODULATION_AM, + }, +}; + +static inline bool si476x_radio_freq_is_inside_of_the_band(u32 freq, int band) +{ + return freq >= si476x_bands[band].rangelow && + freq <= si476x_bands[band].rangehigh; +} + +static inline bool si476x_radio_range_is_inside_of_the_band(u32 low, u32 high, + int band) +{ + return low >= si476x_bands[band].rangelow && + high <= si476x_bands[band].rangehigh; +} + +static int si476x_radio_s_ctrl(struct v4l2_ctrl *ctrl); +static int si476x_radio_g_volatile_ctrl(struct v4l2_ctrl *ctrl); + +enum phase_diversity_modes_idx { + SI476X_IDX_PHDIV_DISABLED, + SI476X_IDX_PHDIV_PRIMARY_COMBINING, + SI476X_IDX_PHDIV_PRIMARY_ANTENNA, + SI476X_IDX_PHDIV_SECONDARY_ANTENNA, + SI476X_IDX_PHDIV_SECONDARY_COMBINING, +}; + +static const char * const phase_diversity_modes[] = { + [SI476X_IDX_PHDIV_DISABLED] = "Disabled", + [SI476X_IDX_PHDIV_PRIMARY_COMBINING] = "Primary with Secondary", + [SI476X_IDX_PHDIV_PRIMARY_ANTENNA] = "Primary Antenna", + [SI476X_IDX_PHDIV_SECONDARY_ANTENNA] = "Secondary Antenna", + [SI476X_IDX_PHDIV_SECONDARY_COMBINING] = "Secondary with Primary", +}; + +static inline enum phase_diversity_modes_idx +si476x_phase_diversity_mode_to_idx(enum si476x_phase_diversity_mode mode) +{ + switch (mode) { + default: /* FALLTHROUGH */ + case SI476X_PHDIV_DISABLED: + return SI476X_IDX_PHDIV_DISABLED; + case SI476X_PHDIV_PRIMARY_COMBINING: + return SI476X_IDX_PHDIV_PRIMARY_COMBINING; + case SI476X_PHDIV_PRIMARY_ANTENNA: + return SI476X_IDX_PHDIV_PRIMARY_ANTENNA; + case SI476X_PHDIV_SECONDARY_ANTENNA: + return SI476X_IDX_PHDIV_SECONDARY_ANTENNA; + case SI476X_PHDIV_SECONDARY_COMBINING: + return SI476X_IDX_PHDIV_SECONDARY_COMBINING; + } +} + +static inline enum si476x_phase_diversity_mode +si476x_phase_diversity_idx_to_mode(enum phase_diversity_modes_idx idx) +{ + static const int idx_to_value[] = { + [SI476X_IDX_PHDIV_DISABLED] = SI476X_PHDIV_DISABLED, + [SI476X_IDX_PHDIV_PRIMARY_COMBINING] = SI476X_PHDIV_PRIMARY_COMBINING, + [SI476X_IDX_PHDIV_PRIMARY_ANTENNA] = SI476X_PHDIV_PRIMARY_ANTENNA, + [SI476X_IDX_PHDIV_SECONDARY_ANTENNA] = SI476X_PHDIV_SECONDARY_ANTENNA, + [SI476X_IDX_PHDIV_SECONDARY_COMBINING] = SI476X_PHDIV_SECONDARY_COMBINING, + }; + + return idx_to_value[idx]; +} + +static const struct v4l2_ctrl_ops si476x_ctrl_ops = { + .g_volatile_ctrl = si476x_radio_g_volatile_ctrl, + .s_ctrl = si476x_radio_s_ctrl, +}; + + +enum si476x_ctrl_idx { + SI476X_IDX_RSSI_THRESHOLD, + SI476X_IDX_SNR_THRESHOLD, + SI476X_IDX_MAX_TUNE_ERROR, + SI476X_IDX_HARMONICS_COUNT, + SI476X_IDX_DIVERSITY_MODE, + SI476X_IDX_INTERCHIP_LINK, +}; +static struct v4l2_ctrl_config si476x_ctrls[] = { + + /** + * SI476X during its station seeking(or tuning) process uses several + * parameters to detrmine if "the station" is valid: + * + * - Signal's SNR(in dBuV) must be lower than + * #V4L2_CID_SI476X_SNR_THRESHOLD + * - Signal's RSSI(in dBuV) must be greater than + * #V4L2_CID_SI476X_RSSI_THRESHOLD + * - Signal's frequency deviation(in units of 2ppm) must not be + * more than #V4L2_CID_SI476X_MAX_TUNE_ERROR + */ + [SI476X_IDX_RSSI_THRESHOLD] = { + .ops = &si476x_ctrl_ops, + .id = V4L2_CID_SI476X_RSSI_THRESHOLD, + .name = "Valid RSSI Threshold", + .type = V4L2_CTRL_TYPE_INTEGER, + .min = -128, + .max = 127, + .step = 1, + }, + [SI476X_IDX_SNR_THRESHOLD] = { + .ops = &si476x_ctrl_ops, + .id = V4L2_CID_SI476X_SNR_THRESHOLD, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Valid SNR Threshold", + .min = -128, + .max = 127, + .step = 1, + }, + [SI476X_IDX_MAX_TUNE_ERROR] = { + .ops = &si476x_ctrl_ops, + .id = V4L2_CID_SI476X_MAX_TUNE_ERROR, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Max Tune Errors", + .min = 0, + .max = 126 * 2, + .step = 2, + }, + + /** + * #V4L2_CID_SI476X_HARMONICS_COUNT -- number of harmonics + * built-in power-line noise supression filter is to reject + * during AM-mode operation. + */ + [SI476X_IDX_HARMONICS_COUNT] = { + .ops = &si476x_ctrl_ops, + .id = V4L2_CID_SI476X_HARMONICS_COUNT, + .type = V4L2_CTRL_TYPE_INTEGER, + + .name = "Count of Harmonics to Reject", + .min = 0, + .max = 20, + .step = 1, + }, + + /** + * #V4L2_CID_SI476X_DIVERSITY_MODE -- configuration which + * two tuners working in diversity mode are to work in. + * + * - #SI476X_IDX_PHDIV_DISABLED diversity mode disabled + * - #SI476X_IDX_PHDIV_PRIMARY_COMBINING diversity mode is + * on, primary tuner's antenna is the main one. + * - #SI476X_IDX_PHDIV_PRIMARY_ANTENNA diversity mode is + * off, primary tuner's antenna is the main one. + * - #SI476X_IDX_PHDIV_SECONDARY_ANTENNA diversity mode is + * off, secondary tuner's antenna is the main one. + * - #SI476X_IDX_PHDIV_SECONDARY_COMBINING diversity mode is + * on, secondary tuner's antenna is the main one. + */ + [SI476X_IDX_DIVERSITY_MODE] = { + .ops = &si476x_ctrl_ops, + .id = V4L2_CID_SI476X_DIVERSITY_MODE, + .type = V4L2_CTRL_TYPE_MENU, + .name = "Phase Diversity Mode", + .qmenu = phase_diversity_modes, + .min = 0, + .max = ARRAY_SIZE(phase_diversity_modes) - 1, + }, + + /** + * #V4L2_CID_SI476X_INTERCHIP_LINK -- inter-chip link in + * diversity mode indicator. Allows user to determine if two + * chips working in diversity mode have established a link + * between each other and if the system as a whole uses + * signals from both antennas to receive FM radio. + */ + [SI476X_IDX_INTERCHIP_LINK] = { + .ops = &si476x_ctrl_ops, + .id = V4L2_CID_SI476X_INTERCHIP_LINK, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .flags = V4L2_CTRL_FLAG_READ_ONLY | V4L2_CTRL_FLAG_VOLATILE, + .name = "Inter-Chip Link", + .min = 0, + .max = 1, + .step = 1, + }, +}; + +struct si476x_radio; + +/** + * struct si476x_radio_ops - vtable of tuner functions + * + * This table holds pointers to functions implementing particular + * operations depending on the mode in which the tuner chip was + * configured to start in. If the function is not supported + * corresponding element is set to #NULL. + * + * @tune_freq: Tune chip to a specific frequency + * @seek_start: Star station seeking + * @rsq_status: Get Recieved Signal Quality(RSQ) status + * @rds_blckcnt: Get recived RDS blocks count + * @phase_diversity: Change phase diversity mode of the tuner + * @phase_div_status: Get phase diversity mode status + * @acf_status: Get the status of Automatically Controlled + * Features(ACF) + * @agc_status: Get Automatic Gain Control(AGC) status + */ +struct si476x_radio_ops { + int (*tune_freq)(struct si476x_core *, struct si476x_tune_freq_args *); + int (*seek_start)(struct si476x_core *, bool, bool); + int (*rsq_status)(struct si476x_core *, struct si476x_rsq_status_args *, + struct si476x_rsq_status_report *); + int (*rds_blckcnt)(struct si476x_core *, bool, + struct si476x_rds_blockcount_report *); + + int (*phase_diversity)(struct si476x_core *, + enum si476x_phase_diversity_mode); + int (*phase_div_status)(struct si476x_core *); + int (*acf_status)(struct si476x_core *, + struct si476x_acf_status_report *); + int (*agc_status)(struct si476x_core *, + struct si476x_agc_status_report *); +}; + +/** + * struct si476x_radio - radio device + * + * @core: Pointer to underlying core device + * @videodev: Pointer to video device created by V4L2 subsystem + * @ops: Vtable of functions. See struct si476x_radio_ops for details + * @kref: Reference counter + * @core_lock: An r/w semaphore to brebvent the deletion of underlying + * core structure is the radio device is being used + */ +struct si476x_radio { + struct v4l2_device v4l2dev; + struct video_device videodev; + struct v4l2_ctrl_handler ctrl_handler; + + struct si476x_core *core; + /* This field should not be accesses unless core lock is held */ + const struct si476x_radio_ops *ops; + + struct dentry *debugfs; + u32 audmode; +}; + +static inline struct si476x_radio * +v4l2_dev_to_radio(struct v4l2_device *d) +{ + return container_of(d, struct si476x_radio, v4l2dev); +} + +static inline struct si476x_radio * +v4l2_ctrl_handler_to_radio(struct v4l2_ctrl_handler *d) +{ + return container_of(d, struct si476x_radio, ctrl_handler); +} + +/* + * si476x_vidioc_querycap - query device capabilities + */ +static int si476x_radio_querycap(struct file *file, void *priv, + struct v4l2_capability *capability) +{ + struct si476x_radio *radio = video_drvdata(file); + + strlcpy(capability->driver, radio->v4l2dev.name, + sizeof(capability->driver)); + strlcpy(capability->card, DRIVER_CARD, sizeof(capability->card)); + snprintf(capability->bus_info, sizeof(capability->bus_info), + "platform:%s", radio->v4l2dev.name); + + capability->device_caps = V4L2_CAP_TUNER + | V4L2_CAP_RADIO + | V4L2_CAP_HW_FREQ_SEEK; + + si476x_core_lock(radio->core); + if (!si476x_core_is_a_secondary_tuner(radio->core)) + capability->device_caps |= V4L2_CAP_RDS_CAPTURE + | V4L2_CAP_READWRITE; + si476x_core_unlock(radio->core); + + capability->capabilities = capability->device_caps + | V4L2_CAP_DEVICE_CAPS; + return 0; +} + +static int si476x_radio_enum_freq_bands(struct file *file, void *priv, + struct v4l2_frequency_band *band) +{ + int err; + struct si476x_radio *radio = video_drvdata(file); + + if (band->tuner != 0) + return -EINVAL; + + switch (radio->core->chip_id) { + /* AM/FM tuners -- all bands are supported */ + case SI476X_CHIP_SI4761: + case SI476X_CHIP_SI4764: + if (band->index < ARRAY_SIZE(si476x_bands)) { + *band = si476x_bands[band->index]; + err = 0; + } else { + err = -EINVAL; + } + break; + /* FM companion tuner chips -- only FM bands are + * supported */ + case SI476X_CHIP_SI4768: + if (band->index == SI476X_BAND_FM) { + *band = si476x_bands[band->index]; + err = 0; + } else { + err = -EINVAL; + } + break; + default: + err = -EINVAL; + } + + return err; +} + +static int si476x_radio_g_tuner(struct file *file, void *priv, + struct v4l2_tuner *tuner) +{ + int err; + struct si476x_rsq_status_report report; + struct si476x_radio *radio = video_drvdata(file); + + struct si476x_rsq_status_args args = { + .primary = false, + .rsqack = false, + .attune = false, + .cancel = false, + .stcack = false, + }; + + if (tuner->index != 0) + return -EINVAL; + + tuner->type = V4L2_TUNER_RADIO; + tuner->capability = V4L2_TUNER_CAP_LOW /* Measure frequencies + * in multiples of + * 62.5 Hz */ + | V4L2_TUNER_CAP_STEREO + | V4L2_TUNER_CAP_HWSEEK_BOUNDED + | V4L2_TUNER_CAP_HWSEEK_WRAP + | V4L2_TUNER_CAP_HWSEEK_PROG_LIM; + + si476x_core_lock(radio->core); + + if (si476x_core_is_a_secondary_tuner(radio->core)) { + strlcpy(tuner->name, "FM (secondary)", sizeof(tuner->name)); + tuner->rxsubchans = 0; + tuner->rangelow = si476x_bands[SI476X_BAND_FM].rangelow; + } else if (si476x_core_has_am(radio->core)) { + if (si476x_core_is_a_primary_tuner(radio->core)) + strlcpy(tuner->name, "AM/FM (primary)", + sizeof(tuner->name)); + else + strlcpy(tuner->name, "AM/FM", sizeof(tuner->name)); + + tuner->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO + | V4L2_TUNER_SUB_RDS; + tuner->capability |= V4L2_TUNER_CAP_RDS + | V4L2_TUNER_CAP_RDS_BLOCK_IO + | V4L2_TUNER_CAP_FREQ_BANDS; + + tuner->rangelow = si476x_bands[SI476X_BAND_AM].rangelow; + } else { + strlcpy(tuner->name, "FM", sizeof(tuner->name)); + tuner->rxsubchans = V4L2_TUNER_SUB_RDS; + tuner->capability |= V4L2_TUNER_CAP_RDS + | V4L2_TUNER_CAP_RDS_BLOCK_IO + | V4L2_TUNER_CAP_FREQ_BANDS; + tuner->rangelow = si476x_bands[SI476X_BAND_FM].rangelow; + } + + tuner->audmode = radio->audmode; + + tuner->afc = 1; + tuner->rangehigh = si476x_bands[SI476X_BAND_FM].rangehigh; + + err = radio->ops->rsq_status(radio->core, + &args, &report); + if (err < 0) { + tuner->signal = 0; + } else { + /* + * tuner->signal value range: 0x0000 .. 0xFFFF, + * report.rssi: -128 .. 127 + */ + tuner->signal = (report.rssi + 128) * 257; + } + si476x_core_unlock(radio->core); + + return err; +} + +static int si476x_radio_s_tuner(struct file *file, void *priv, + const struct v4l2_tuner *tuner) +{ + struct si476x_radio *radio = video_drvdata(file); + + if (tuner->index != 0) + return -EINVAL; + + if (tuner->audmode == V4L2_TUNER_MODE_MONO || + tuner->audmode == V4L2_TUNER_MODE_STEREO) + radio->audmode = tuner->audmode; + else + radio->audmode = V4L2_TUNER_MODE_STEREO; + + return 0; +} + +static int si476x_radio_init_vtable(struct si476x_radio *radio, + enum si476x_func func) +{ + static const struct si476x_radio_ops fm_ops = { + .tune_freq = si476x_core_cmd_fm_tune_freq, + .seek_start = si476x_core_cmd_fm_seek_start, + .rsq_status = si476x_core_cmd_fm_rsq_status, + .rds_blckcnt = si476x_core_cmd_fm_rds_blockcount, + .phase_diversity = si476x_core_cmd_fm_phase_diversity, + .phase_div_status = si476x_core_cmd_fm_phase_div_status, + .acf_status = si476x_core_cmd_fm_acf_status, + .agc_status = si476x_core_cmd_agc_status, + }; + + static const struct si476x_radio_ops am_ops = { + .tune_freq = si476x_core_cmd_am_tune_freq, + .seek_start = si476x_core_cmd_am_seek_start, + .rsq_status = si476x_core_cmd_am_rsq_status, + .rds_blckcnt = NULL, + .phase_diversity = NULL, + .phase_div_status = NULL, + .acf_status = si476x_core_cmd_am_acf_status, + .agc_status = NULL, + }; + + switch (func) { + case SI476X_FUNC_FM_RECEIVER: + radio->ops = &fm_ops; + return 0; + + case SI476X_FUNC_AM_RECEIVER: + radio->ops = &am_ops; + return 0; + default: + WARN(1, "Unexpected tuner function value\n"); + return -EINVAL; + } +} + +static int si476x_radio_pretune(struct si476x_radio *radio, + enum si476x_func func) +{ + int retval; + + struct si476x_tune_freq_args args = { + .zifsr = false, + .hd = false, + .injside = SI476X_INJSIDE_AUTO, + .tunemode = SI476X_TM_VALIDATED_NORMAL_TUNE, + .smoothmetrics = SI476X_SM_INITIALIZE_AUDIO, + .antcap = 0, + }; + + switch (func) { + case SI476X_FUNC_FM_RECEIVER: + args.freq = v4l2_to_si476x(radio->core, + 92 * FREQ_MUL); + retval = radio->ops->tune_freq(radio->core, &args); + break; + case SI476X_FUNC_AM_RECEIVER: + args.freq = v4l2_to_si476x(radio->core, + 0.6 * FREQ_MUL); + retval = radio->ops->tune_freq(radio->core, &args); + break; + default: + WARN(1, "Unexpected tuner function value\n"); + retval = -EINVAL; + } + + return retval; +} +static int si476x_radio_do_post_powerup_init(struct si476x_radio *radio, + enum si476x_func func) +{ + int err; + + /* regcache_mark_dirty(radio->core->regmap); */ + err = regcache_sync_region(radio->core->regmap, + SI476X_PROP_DIGITAL_IO_INPUT_SAMPLE_RATE, + SI476X_PROP_DIGITAL_IO_OUTPUT_FORMAT); + if (err < 0) + return err; + + err = regcache_sync_region(radio->core->regmap, + SI476X_PROP_AUDIO_DEEMPHASIS, + SI476X_PROP_AUDIO_PWR_LINE_FILTER); + if (err < 0) + return err; + + err = regcache_sync_region(radio->core->regmap, + SI476X_PROP_INT_CTL_ENABLE, + SI476X_PROP_INT_CTL_ENABLE); + if (err < 0) + return err; + + /* + * Is there any point in restoring SNR and the like + * when switching between AM/FM? + */ + err = regcache_sync_region(radio->core->regmap, + SI476X_PROP_VALID_MAX_TUNE_ERROR, + SI476X_PROP_VALID_MAX_TUNE_ERROR); + if (err < 0) + return err; + + err = regcache_sync_region(radio->core->regmap, + SI476X_PROP_VALID_SNR_THRESHOLD, + SI476X_PROP_VALID_RSSI_THRESHOLD); + if (err < 0) + return err; + + if (func == SI476X_FUNC_FM_RECEIVER) { + if (si476x_core_has_diversity(radio->core)) { + err = si476x_core_cmd_fm_phase_diversity(radio->core, + radio->core->diversity_mode); + if (err < 0) + return err; + } + + err = regcache_sync_region(radio->core->regmap, + SI476X_PROP_FM_RDS_INTERRUPT_SOURCE, + SI476X_PROP_FM_RDS_CONFIG); + if (err < 0) + return err; + } + + return si476x_radio_init_vtable(radio, func); + +} + +static int si476x_radio_change_func(struct si476x_radio *radio, + enum si476x_func func) +{ + int err; + bool soft; + /* + * Since power/up down is a very time consuming operation, + * try to avoid doing it if the requested mode matches the one + * the tuner is in + */ + if (func == radio->core->power_up_parameters.func) + return 0; + + soft = true; + err = si476x_core_stop(radio->core, soft); + if (err < 0) { + /* + * OK, if the chip does not want to play nice let's + * try to reset it in more brutal way + */ + soft = false; + err = si476x_core_stop(radio->core, soft); + if (err < 0) + return err; + } + /* + Set the desired radio tuner function + */ + radio->core->power_up_parameters.func = func; + + err = si476x_core_start(radio->core, soft); + if (err < 0) + return err; + + /* + * No need to do the rest of manipulations for the bootlader + * mode + */ + if (func != SI476X_FUNC_FM_RECEIVER && + func != SI476X_FUNC_AM_RECEIVER) + return err; + + return si476x_radio_do_post_powerup_init(radio, func); +} + +static int si476x_radio_g_frequency(struct file *file, void *priv, + struct v4l2_frequency *f) +{ + int err; + struct si476x_radio *radio = video_drvdata(file); + + if (f->tuner != 0 || + f->type != V4L2_TUNER_RADIO) + return -EINVAL; + + si476x_core_lock(radio->core); + + if (radio->ops->rsq_status) { + struct si476x_rsq_status_report report; + struct si476x_rsq_status_args args = { + .primary = false, + .rsqack = false, + .attune = true, + .cancel = false, + .stcack = false, + }; + + err = radio->ops->rsq_status(radio->core, &args, &report); + if (!err) + f->frequency = si476x_to_v4l2(radio->core, + report.readfreq); + } else { + err = -EINVAL; + } + + si476x_core_unlock(radio->core); + + return err; +} + +static int si476x_radio_s_frequency(struct file *file, void *priv, + const struct v4l2_frequency *f) +{ + int err; + u32 freq = f->frequency; + struct si476x_tune_freq_args args; + struct si476x_radio *radio = video_drvdata(file); + + const u32 midrange = (si476x_bands[SI476X_BAND_AM].rangehigh + + si476x_bands[SI476X_BAND_FM].rangelow) / 2; + const int band = (freq > midrange) ? + SI476X_BAND_FM : SI476X_BAND_AM; + const enum si476x_func func = (band == SI476X_BAND_AM) ? + SI476X_FUNC_AM_RECEIVER : SI476X_FUNC_FM_RECEIVER; + + if (f->tuner != 0 || + f->type != V4L2_TUNER_RADIO) + return -EINVAL; + + si476x_core_lock(radio->core); + + freq = clamp(freq, + si476x_bands[band].rangelow, + si476x_bands[band].rangehigh); + + if (si476x_radio_freq_is_inside_of_the_band(freq, + SI476X_BAND_AM) && + (!si476x_core_has_am(radio->core) || + si476x_core_is_a_secondary_tuner(radio->core))) { + err = -EINVAL; + goto unlock; + } + + err = si476x_radio_change_func(radio, func); + if (err < 0) + goto unlock; + + args.zifsr = false; + args.hd = false; + args.injside = SI476X_INJSIDE_AUTO; + args.freq = v4l2_to_si476x(radio->core, freq); + args.tunemode = SI476X_TM_VALIDATED_NORMAL_TUNE; + args.smoothmetrics = SI476X_SM_INITIALIZE_AUDIO; + args.antcap = 0; + + err = radio->ops->tune_freq(radio->core, &args); + +unlock: + si476x_core_unlock(radio->core); + return err; +} + +static int si476x_radio_s_hw_freq_seek(struct file *file, void *priv, + const struct v4l2_hw_freq_seek *seek) +{ + int err; + enum si476x_func func; + u32 rangelow, rangehigh; + struct si476x_radio *radio = video_drvdata(file); + + if (file->f_flags & O_NONBLOCK) + return -EAGAIN; + + if (seek->tuner != 0 || + seek->type != V4L2_TUNER_RADIO) + return -EINVAL; + + si476x_core_lock(radio->core); + + if (!seek->rangelow) { + err = regmap_read(radio->core->regmap, + SI476X_PROP_SEEK_BAND_BOTTOM, + &rangelow); + if (!err) + rangelow = si476x_to_v4l2(radio->core, rangelow); + else + goto unlock; + } + if (!seek->rangehigh) { + err = regmap_read(radio->core->regmap, + SI476X_PROP_SEEK_BAND_TOP, + &rangehigh); + if (!err) + rangehigh = si476x_to_v4l2(radio->core, rangehigh); + else + goto unlock; + } + + if (rangelow > rangehigh) { + err = -EINVAL; + goto unlock; + } + + if (si476x_radio_range_is_inside_of_the_band(rangelow, rangehigh, + SI476X_BAND_FM)) { + func = SI476X_FUNC_FM_RECEIVER; + + } else if (si476x_core_has_am(radio->core) && + si476x_radio_range_is_inside_of_the_band(rangelow, rangehigh, + SI476X_BAND_AM)) { + func = SI476X_FUNC_AM_RECEIVER; + } else { + err = -EINVAL; + goto unlock; + } + + err = si476x_radio_change_func(radio, func); + if (err < 0) + goto unlock; + + if (seek->rangehigh) { + err = regmap_write(radio->core->regmap, + SI476X_PROP_SEEK_BAND_TOP, + v4l2_to_si476x(radio->core, + seek->rangehigh)); + if (err) + goto unlock; + } + if (seek->rangelow) { + err = regmap_write(radio->core->regmap, + SI476X_PROP_SEEK_BAND_BOTTOM, + v4l2_to_si476x(radio->core, + seek->rangelow)); + if (err) + goto unlock; + } + if (seek->spacing) { + err = regmap_write(radio->core->regmap, + SI476X_PROP_SEEK_FREQUENCY_SPACING, + v4l2_to_si476x(radio->core, + seek->spacing)); + if (err) + goto unlock; + } + + err = radio->ops->seek_start(radio->core, + seek->seek_upward, + seek->wrap_around); +unlock: + si476x_core_unlock(radio->core); + + + + return err; +} + +static int si476x_radio_g_volatile_ctrl(struct v4l2_ctrl *ctrl) +{ + int retval; + struct si476x_radio *radio = v4l2_ctrl_handler_to_radio(ctrl->handler); + + si476x_core_lock(radio->core); + + switch (ctrl->id) { + case V4L2_CID_SI476X_INTERCHIP_LINK: + if (si476x_core_has_diversity(radio->core)) { + if (radio->ops->phase_diversity) { + retval = radio->ops->phase_div_status(radio->core); + if (retval < 0) + break; + + ctrl->val = !!SI476X_PHDIV_STATUS_LINK_LOCKED(retval); + retval = 0; + break; + } else { + retval = -ENOTTY; + break; + } + } + retval = -EINVAL; + break; + default: + retval = -EINVAL; + break; + } + si476x_core_unlock(radio->core); + return retval; + +} + +static int si476x_radio_s_ctrl(struct v4l2_ctrl *ctrl) +{ + int retval; + enum si476x_phase_diversity_mode mode; + struct si476x_radio *radio = v4l2_ctrl_handler_to_radio(ctrl->handler); + + si476x_core_lock(radio->core); + + switch (ctrl->id) { + case V4L2_CID_SI476X_HARMONICS_COUNT: + retval = regmap_update_bits(radio->core->regmap, + SI476X_PROP_AUDIO_PWR_LINE_FILTER, + SI476X_PROP_PWR_HARMONICS_MASK, + ctrl->val); + break; + case V4L2_CID_POWER_LINE_FREQUENCY: + switch (ctrl->val) { + case V4L2_CID_POWER_LINE_FREQUENCY_DISABLED: + retval = regmap_update_bits(radio->core->regmap, + SI476X_PROP_AUDIO_PWR_LINE_FILTER, + SI476X_PROP_PWR_ENABLE_MASK, + 0); + break; + case V4L2_CID_POWER_LINE_FREQUENCY_50HZ: + retval = regmap_update_bits(radio->core->regmap, + SI476X_PROP_AUDIO_PWR_LINE_FILTER, + SI476X_PROP_PWR_GRID_MASK, + SI476X_PROP_PWR_GRID_50HZ); + break; + case V4L2_CID_POWER_LINE_FREQUENCY_60HZ: + retval = regmap_update_bits(radio->core->regmap, + SI476X_PROP_AUDIO_PWR_LINE_FILTER, + SI476X_PROP_PWR_GRID_MASK, + SI476X_PROP_PWR_GRID_60HZ); + break; + default: + retval = -EINVAL; + break; + } + break; + case V4L2_CID_SI476X_RSSI_THRESHOLD: + retval = regmap_write(radio->core->regmap, + SI476X_PROP_VALID_RSSI_THRESHOLD, + ctrl->val); + break; + case V4L2_CID_SI476X_SNR_THRESHOLD: + retval = regmap_write(radio->core->regmap, + SI476X_PROP_VALID_SNR_THRESHOLD, + ctrl->val); + break; + case V4L2_CID_SI476X_MAX_TUNE_ERROR: + retval = regmap_write(radio->core->regmap, + SI476X_PROP_VALID_MAX_TUNE_ERROR, + ctrl->val); + break; + case V4L2_CID_RDS_RECEPTION: + /* + * It looks like RDS related properties are + * inaccesable when tuner is in AM mode, so cache the + * changes + */ + if (si476x_core_is_in_am_receiver_mode(radio->core)) + regcache_cache_only(radio->core->regmap, true); + + if (ctrl->val) { + retval = regmap_write(radio->core->regmap, + SI476X_PROP_FM_RDS_INTERRUPT_FIFO_COUNT, + radio->core->rds_fifo_depth); + if (retval < 0) + break; + + if (radio->core->client->irq) { + retval = regmap_write(radio->core->regmap, + SI476X_PROP_FM_RDS_INTERRUPT_SOURCE, + SI476X_RDSRECV); + if (retval < 0) + break; + } + + /* Drain RDS FIFO before enabling RDS processing */ + retval = si476x_core_cmd_fm_rds_status(radio->core, + false, + true, + true, + NULL); + if (retval < 0) + break; + + retval = regmap_update_bits(radio->core->regmap, + SI476X_PROP_FM_RDS_CONFIG, + SI476X_PROP_RDSEN_MASK, + SI476X_PROP_RDSEN); + } else { + retval = regmap_update_bits(radio->core->regmap, + SI476X_PROP_FM_RDS_CONFIG, + SI476X_PROP_RDSEN_MASK, + !SI476X_PROP_RDSEN); + } + + if (si476x_core_is_in_am_receiver_mode(radio->core)) + regcache_cache_only(radio->core->regmap, false); + break; + case V4L2_CID_TUNE_DEEMPHASIS: + retval = regmap_write(radio->core->regmap, + SI476X_PROP_AUDIO_DEEMPHASIS, + ctrl->val); + break; + + case V4L2_CID_SI476X_DIVERSITY_MODE: + mode = si476x_phase_diversity_idx_to_mode(ctrl->val); + + if (mode == radio->core->diversity_mode) { + retval = 0; + break; + } + + if (si476x_core_is_in_am_receiver_mode(radio->core)) { + /* + * Diversity cannot be configured while tuner + * is in AM mode so save the changes and carry on. + */ + radio->core->diversity_mode = mode; + retval = 0; + } else { + retval = radio->ops->phase_diversity(radio->core, mode); + if (!retval) + radio->core->diversity_mode = mode; + } + break; + + default: + retval = -EINVAL; + break; + } + + si476x_core_unlock(radio->core); + + return retval; +} + +static int si476x_radio_g_chip_ident(struct file *file, void *fh, + struct v4l2_dbg_chip_ident *chip) +{ + if (chip->match.type == V4L2_CHIP_MATCH_HOST && + v4l2_chip_match_host(&chip->match)) + return 0; + return -EINVAL; +} + + +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int si476x_radio_g_register(struct file *file, void *fh, + struct v4l2_dbg_register *reg) +{ + int err; + unsigned int value; + struct si476x_radio *radio = video_drvdata(file); + + si476x_core_lock(radio->core); + reg->size = 2; + err = regmap_read(radio->core->regmap, + (unsigned int)reg->reg, &value); + reg->val = value; + si476x_core_unlock(radio->core); + + return err; +} +static int si476x_radio_s_register(struct file *file, void *fh, + const struct v4l2_dbg_register *reg) +{ + + int err; + struct si476x_radio *radio = video_drvdata(file); + + si476x_core_lock(radio->core); + err = regmap_write(radio->core->regmap, + (unsigned int)reg->reg, + (unsigned int)reg->val); + si476x_core_unlock(radio->core); + + return err; +} +#endif + +static int si476x_radio_fops_open(struct file *file) +{ + struct si476x_radio *radio = video_drvdata(file); + int err; + + err = v4l2_fh_open(file); + if (err) + return err; + + if (v4l2_fh_is_singular_file(file)) { + si476x_core_lock(radio->core); + err = si476x_core_set_power_state(radio->core, + SI476X_POWER_UP_FULL); + if (err < 0) + goto done; + + err = si476x_radio_do_post_powerup_init(radio, + radio->core->power_up_parameters.func); + if (err < 0) + goto power_down; + + err = si476x_radio_pretune(radio, + radio->core->power_up_parameters.func); + if (err < 0) + goto power_down; + + si476x_core_unlock(radio->core); + /*Must be done after si476x_core_unlock to prevent a deadlock*/ + v4l2_ctrl_handler_setup(&radio->ctrl_handler); + } + + return err; + +power_down: + si476x_core_set_power_state(radio->core, + SI476X_POWER_DOWN); +done: + si476x_core_unlock(radio->core); + v4l2_fh_release(file); + + return err; +} + +static int si476x_radio_fops_release(struct file *file) +{ + int err; + struct si476x_radio *radio = video_drvdata(file); + + if (v4l2_fh_is_singular_file(file) && + atomic_read(&radio->core->is_alive)) + si476x_core_set_power_state(radio->core, + SI476X_POWER_DOWN); + + err = v4l2_fh_release(file); + + return err; +} + +static ssize_t si476x_radio_fops_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + ssize_t rval; + size_t fifo_len; + unsigned int copied; + + struct si476x_radio *radio = video_drvdata(file); + + /* block if no new data available */ + if (kfifo_is_empty(&radio->core->rds_fifo)) { + if (file->f_flags & O_NONBLOCK) + return -EWOULDBLOCK; + + rval = wait_event_interruptible(radio->core->rds_read_queue, + (!kfifo_is_empty(&radio->core->rds_fifo) || + !atomic_read(&radio->core->is_alive))); + if (rval < 0) + return -EINTR; + + if (!atomic_read(&radio->core->is_alive)) + return -ENODEV; + } + + fifo_len = kfifo_len(&radio->core->rds_fifo); + + if (kfifo_to_user(&radio->core->rds_fifo, buf, + min(fifo_len, count), + &copied) != 0) { + dev_warn(&radio->videodev.dev, + "Error during FIFO to userspace copy\n"); + rval = -EIO; + } else { + rval = (ssize_t)copied; + } + + return rval; +} + +static unsigned int si476x_radio_fops_poll(struct file *file, + struct poll_table_struct *pts) +{ + struct si476x_radio *radio = video_drvdata(file); + unsigned long req_events = poll_requested_events(pts); + unsigned int err = v4l2_ctrl_poll(file, pts); + + if (req_events & (POLLIN | POLLRDNORM)) { + if (atomic_read(&radio->core->is_alive)) + poll_wait(file, &radio->core->rds_read_queue, pts); + + if (!atomic_read(&radio->core->is_alive)) + err = POLLHUP; + + if (!kfifo_is_empty(&radio->core->rds_fifo)) + err = POLLIN | POLLRDNORM; + } + + return err; +} + +static const struct v4l2_file_operations si476x_fops = { + .owner = THIS_MODULE, + .read = si476x_radio_fops_read, + .poll = si476x_radio_fops_poll, + .unlocked_ioctl = video_ioctl2, + .open = si476x_radio_fops_open, + .release = si476x_radio_fops_release, +}; + + +static const struct v4l2_ioctl_ops si4761_ioctl_ops = { + .vidioc_querycap = si476x_radio_querycap, + .vidioc_g_tuner = si476x_radio_g_tuner, + .vidioc_s_tuner = si476x_radio_s_tuner, + + .vidioc_g_frequency = si476x_radio_g_frequency, + .vidioc_s_frequency = si476x_radio_s_frequency, + .vidioc_s_hw_freq_seek = si476x_radio_s_hw_freq_seek, + .vidioc_enum_freq_bands = si476x_radio_enum_freq_bands, + + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, + + .vidioc_g_chip_ident = si476x_radio_g_chip_ident, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .vidioc_g_register = si476x_radio_g_register, + .vidioc_s_register = si476x_radio_s_register, +#endif +}; + + +static const struct video_device si476x_viddev_template = { + .fops = &si476x_fops, + .name = DRIVER_NAME, + .release = video_device_release_empty, +}; + + + +static ssize_t si476x_radio_read_acf_blob(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + int err; + struct si476x_radio *radio = file->private_data; + struct si476x_acf_status_report report; + + si476x_core_lock(radio->core); + if (radio->ops->acf_status) + err = radio->ops->acf_status(radio->core, &report); + else + err = -ENOENT; + si476x_core_unlock(radio->core); + + if (err < 0) + return err; + + return simple_read_from_buffer(user_buf, count, ppos, &report, + sizeof(report)); +} + +static const struct file_operations radio_acf_fops = { + .open = simple_open, + .llseek = default_llseek, + .read = si476x_radio_read_acf_blob, +}; + +static ssize_t si476x_radio_read_rds_blckcnt_blob(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + int err; + struct si476x_radio *radio = file->private_data; + struct si476x_rds_blockcount_report report; + + si476x_core_lock(radio->core); + if (radio->ops->rds_blckcnt) + err = radio->ops->rds_blckcnt(radio->core, true, + &report); + else + err = -ENOENT; + si476x_core_unlock(radio->core); + + if (err < 0) + return err; + + return simple_read_from_buffer(user_buf, count, ppos, &report, + sizeof(report)); +} + +static const struct file_operations radio_rds_blckcnt_fops = { + .open = simple_open, + .llseek = default_llseek, + .read = si476x_radio_read_rds_blckcnt_blob, +}; + +static ssize_t si476x_radio_read_agc_blob(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + int err; + struct si476x_radio *radio = file->private_data; + struct si476x_agc_status_report report; + + si476x_core_lock(radio->core); + if (radio->ops->rds_blckcnt) + err = radio->ops->agc_status(radio->core, &report); + else + err = -ENOENT; + si476x_core_unlock(radio->core); + + if (err < 0) + return err; + + return simple_read_from_buffer(user_buf, count, ppos, &report, + sizeof(report)); +} + +static const struct file_operations radio_agc_fops = { + .open = simple_open, + .llseek = default_llseek, + .read = si476x_radio_read_agc_blob, +}; + +static ssize_t si476x_radio_read_rsq_blob(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + int err; + struct si476x_radio *radio = file->private_data; + struct si476x_rsq_status_report report; + struct si476x_rsq_status_args args = { + .primary = false, + .rsqack = false, + .attune = false, + .cancel = false, + .stcack = false, + }; + + si476x_core_lock(radio->core); + if (radio->ops->rds_blckcnt) + err = radio->ops->rsq_status(radio->core, &args, &report); + else + err = -ENOENT; + si476x_core_unlock(radio->core); + + if (err < 0) + return err; + + return simple_read_from_buffer(user_buf, count, ppos, &report, + sizeof(report)); +} + +static const struct file_operations radio_rsq_fops = { + .open = simple_open, + .llseek = default_llseek, + .read = si476x_radio_read_rsq_blob, +}; + +static ssize_t si476x_radio_read_rsq_primary_blob(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + int err; + struct si476x_radio *radio = file->private_data; + struct si476x_rsq_status_report report; + struct si476x_rsq_status_args args = { + .primary = true, + .rsqack = false, + .attune = false, + .cancel = false, + .stcack = false, + }; + + si476x_core_lock(radio->core); + if (radio->ops->rds_blckcnt) + err = radio->ops->rsq_status(radio->core, &args, &report); + else + err = -ENOENT; + si476x_core_unlock(radio->core); + + if (err < 0) + return err; + + return simple_read_from_buffer(user_buf, count, ppos, &report, + sizeof(report)); +} + +static const struct file_operations radio_rsq_primary_fops = { + .open = simple_open, + .llseek = default_llseek, + .read = si476x_radio_read_rsq_primary_blob, +}; + + +static int si476x_radio_init_debugfs(struct si476x_radio *radio) +{ + struct dentry *dentry; + int ret; + + dentry = debugfs_create_dir(dev_name(radio->v4l2dev.dev), NULL); + if (IS_ERR(dentry)) { + ret = PTR_ERR(dentry); + goto exit; + } + radio->debugfs = dentry; + + dentry = debugfs_create_file("acf", S_IRUGO, + radio->debugfs, radio, &radio_acf_fops); + if (IS_ERR(dentry)) { + ret = PTR_ERR(dentry); + goto cleanup; + } + + dentry = debugfs_create_file("rds_blckcnt", S_IRUGO, + radio->debugfs, radio, + &radio_rds_blckcnt_fops); + if (IS_ERR(dentry)) { + ret = PTR_ERR(dentry); + goto cleanup; + } + + dentry = debugfs_create_file("agc", S_IRUGO, + radio->debugfs, radio, &radio_agc_fops); + if (IS_ERR(dentry)) { + ret = PTR_ERR(dentry); + goto cleanup; + } + + dentry = debugfs_create_file("rsq", S_IRUGO, + radio->debugfs, radio, &radio_rsq_fops); + if (IS_ERR(dentry)) { + ret = PTR_ERR(dentry); + goto cleanup; + } + + dentry = debugfs_create_file("rsq_primary", S_IRUGO, + radio->debugfs, radio, + &radio_rsq_primary_fops); + if (IS_ERR(dentry)) { + ret = PTR_ERR(dentry); + goto cleanup; + } + + return 0; +cleanup: + debugfs_remove_recursive(radio->debugfs); +exit: + return ret; +} + + +static int si476x_radio_add_new_custom(struct si476x_radio *radio, + enum si476x_ctrl_idx idx) +{ + int rval; + struct v4l2_ctrl *ctrl; + + ctrl = v4l2_ctrl_new_custom(&radio->ctrl_handler, + &si476x_ctrls[idx], + NULL); + rval = radio->ctrl_handler.error; + if (ctrl == NULL && rval) + dev_err(radio->v4l2dev.dev, + "Could not initialize '%s' control %d\n", + si476x_ctrls[idx].name, rval); + + return rval; +} + +static int si476x_radio_probe(struct platform_device *pdev) +{ + int rval; + struct si476x_radio *radio; + struct v4l2_ctrl *ctrl; + + static atomic_t instance = ATOMIC_INIT(0); + + radio = devm_kzalloc(&pdev->dev, sizeof(*radio), GFP_KERNEL); + if (!radio) + return -ENOMEM; + + radio->core = i2c_mfd_cell_to_core(&pdev->dev); + + v4l2_device_set_name(&radio->v4l2dev, DRIVER_NAME, &instance); + + rval = v4l2_device_register(&pdev->dev, &radio->v4l2dev); + if (rval) { + dev_err(&pdev->dev, "Cannot register v4l2_device.\n"); + return rval; + } + + memcpy(&radio->videodev, &si476x_viddev_template, + sizeof(struct video_device)); + + radio->videodev.v4l2_dev = &radio->v4l2dev; + radio->videodev.ioctl_ops = &si4761_ioctl_ops; + + video_set_drvdata(&radio->videodev, radio); + platform_set_drvdata(pdev, radio); + + set_bit(V4L2_FL_USE_FH_PRIO, &radio->videodev.flags); + + radio->v4l2dev.ctrl_handler = &radio->ctrl_handler; + v4l2_ctrl_handler_init(&radio->ctrl_handler, + 1 + ARRAY_SIZE(si476x_ctrls)); + + if (si476x_core_has_am(radio->core)) { + ctrl = v4l2_ctrl_new_std_menu(&radio->ctrl_handler, + &si476x_ctrl_ops, + V4L2_CID_POWER_LINE_FREQUENCY, + V4L2_CID_POWER_LINE_FREQUENCY_60HZ, + 0, 0); + rval = radio->ctrl_handler.error; + if (ctrl == NULL && rval) { + dev_err(&pdev->dev, "Could not initialize V4L2_CID_POWER_LINE_FREQUENCY control %d\n", + rval); + goto exit; + } + + rval = si476x_radio_add_new_custom(radio, + SI476X_IDX_HARMONICS_COUNT); + if (rval < 0) + goto exit; + } + + rval = si476x_radio_add_new_custom(radio, SI476X_IDX_RSSI_THRESHOLD); + if (rval < 0) + goto exit; + + rval = si476x_radio_add_new_custom(radio, SI476X_IDX_SNR_THRESHOLD); + if (rval < 0) + goto exit; + + rval = si476x_radio_add_new_custom(radio, SI476X_IDX_MAX_TUNE_ERROR); + if (rval < 0) + goto exit; + + ctrl = v4l2_ctrl_new_std_menu(&radio->ctrl_handler, + &si476x_ctrl_ops, + V4L2_CID_TUNE_DEEMPHASIS, + V4L2_DEEMPHASIS_75_uS, 0, 0); + rval = radio->ctrl_handler.error; + if (ctrl == NULL && rval) { + dev_err(&pdev->dev, "Could not initialize V4L2_CID_TUNE_DEEMPHASIS control %d\n", + rval); + goto exit; + } + + ctrl = v4l2_ctrl_new_std(&radio->ctrl_handler, &si476x_ctrl_ops, + V4L2_CID_RDS_RECEPTION, + 0, 1, 1, 1); + rval = radio->ctrl_handler.error; + if (ctrl == NULL && rval) { + dev_err(&pdev->dev, "Could not initialize V4L2_CID_RDS_RECEPTION control %d\n", + rval); + goto exit; + } + + if (si476x_core_has_diversity(radio->core)) { + si476x_ctrls[SI476X_IDX_DIVERSITY_MODE].def = + si476x_phase_diversity_mode_to_idx(radio->core->diversity_mode); + si476x_radio_add_new_custom(radio, SI476X_IDX_DIVERSITY_MODE); + if (rval < 0) + goto exit; + + si476x_radio_add_new_custom(radio, SI476X_IDX_INTERCHIP_LINK); + if (rval < 0) + goto exit; + } + + /* register video device */ + rval = video_register_device(&radio->videodev, VFL_TYPE_RADIO, -1); + if (rval < 0) { + dev_err(&pdev->dev, "Could not register video device\n"); + goto exit; + } + + rval = si476x_radio_init_debugfs(radio); + if (rval < 0) { + dev_err(&pdev->dev, "Could not creat debugfs interface\n"); + goto exit; + } + + return 0; +exit: + v4l2_ctrl_handler_free(radio->videodev.ctrl_handler); + return rval; +} + +static int si476x_radio_remove(struct platform_device *pdev) +{ + struct si476x_radio *radio = platform_get_drvdata(pdev); + + v4l2_ctrl_handler_free(radio->videodev.ctrl_handler); + video_unregister_device(&radio->videodev); + v4l2_device_unregister(&radio->v4l2dev); + debugfs_remove_recursive(radio->debugfs); + + return 0; +} + +MODULE_ALIAS("platform:si476x-radio"); + +static struct platform_driver si476x_radio_driver = { + .driver = { + .name = DRIVER_NAME, + .owner = THIS_MODULE, + }, + .probe = si476x_radio_probe, + .remove = si476x_radio_remove, +}; +module_platform_driver(si476x_radio_driver); + +MODULE_AUTHOR("Andrey Smirnov "); +MODULE_DESCRIPTION("Driver for Si4761/64/68 AM/FM Radio MFD Cell"); +MODULE_LICENSE("GPL"); diff --git a/trunk/drivers/media/rc/imon.c b/trunk/drivers/media/rc/imon.c index 72e3fa652481..b8f9f85a11c9 100644 --- a/trunk/drivers/media/rc/imon.c +++ b/trunk/drivers/media/rc/imon.c @@ -528,10 +528,8 @@ static int send_packet(struct imon_context *ictx) mutex_unlock(&ictx->lock); retval = wait_for_completion_interruptible( &ictx->tx.finished); - if (retval) { - usb_kill_urb(ictx->tx_urb); + if (retval) pr_err_ratelimited("task interrupted\n"); - } mutex_lock(&ictx->lock); retval = ictx->tx.status; @@ -2095,8 +2093,7 @@ static bool imon_find_endpoints(struct imon_context *ictx, } -static struct imon_context *imon_init_intf0(struct usb_interface *intf, - const struct usb_device_id *id) +static struct imon_context *imon_init_intf0(struct usb_interface *intf) { struct imon_context *ictx; struct urb *rx_urb; @@ -2136,10 +2133,6 @@ static struct imon_context *imon_init_intf0(struct usb_interface *intf, ictx->vendor = le16_to_cpu(ictx->usbdev_intf0->descriptor.idVendor); ictx->product = le16_to_cpu(ictx->usbdev_intf0->descriptor.idProduct); - /* default send_packet delay is 5ms but some devices need more */ - ictx->send_packet_delay = id->driver_info & IMON_NEED_20MS_PKT_DELAY ? - 20 : 5; - ret = -ENODEV; iface_desc = intf->cur_altsetting; if (!imon_find_endpoints(ictx, iface_desc)) { @@ -2318,7 +2311,7 @@ static int imon_probe(struct usb_interface *interface, first_if_ctx = usb_get_intfdata(first_if); if (ifnum == 0) { - ictx = imon_init_intf0(interface, id); + ictx = imon_init_intf0(interface); if (!ictx) { pr_err("failed to initialize context!\n"); ret = -ENODEV; @@ -2326,14 +2319,7 @@ static int imon_probe(struct usb_interface *interface, } } else { - /* this is the secondary interface on the device */ - - /* fail early if first intf failed to register */ - if (!first_if_ctx) { - ret = -ENODEV; - goto fail; - } - + /* this is the secondary interface on the device */ ictx = imon_init_intf1(interface, first_if_ctx); if (!ictx) { pr_err("failed to attach to context!\n"); @@ -2343,6 +2329,10 @@ static int imon_probe(struct usb_interface *interface, } + /* default send_packet delay is 5ms but some devices need more */ + ictx->send_packet_delay = id->driver_info & IMON_NEED_20MS_PKT_DELAY ? + 20 : 5; + usb_set_intfdata(interface, ictx); if (ifnum == 0) { diff --git a/trunk/drivers/media/tuners/Kconfig b/trunk/drivers/media/tuners/Kconfig index ffabd66dd14d..f6768cad001a 100644 --- a/trunk/drivers/media/tuners/Kconfig +++ b/trunk/drivers/media/tuners/Kconfig @@ -248,4 +248,11 @@ config MEDIA_TUNER_IT913X default m if !MEDIA_SUBDRV_AUTOSELECT help ITE Tech IT913x silicon tuner driver. + +config MEDIA_TUNER_R820T + tristate "Rafael Micro R820T silicon tuner" + depends on MEDIA_SUPPORT && I2C + default m if !MEDIA_SUBDRV_AUTOSELECT + help + Rafael Micro R820T silicon tuner driver. endmenu diff --git a/trunk/drivers/media/tuners/Makefile b/trunk/drivers/media/tuners/Makefile index 2ebe4b725b51..308f108eadba 100644 --- a/trunk/drivers/media/tuners/Makefile +++ b/trunk/drivers/media/tuners/Makefile @@ -35,6 +35,7 @@ obj-$(CONFIG_MEDIA_TUNER_FC0011) += fc0011.o obj-$(CONFIG_MEDIA_TUNER_FC0012) += fc0012.o obj-$(CONFIG_MEDIA_TUNER_FC0013) += fc0013.o obj-$(CONFIG_MEDIA_TUNER_IT913X) += tuner_it913x.o +obj-$(CONFIG_MEDIA_TUNER_R820T) += r820t.o ccflags-y += -I$(srctree)/drivers/media/dvb-core ccflags-y += -I$(srctree)/drivers/media/dvb-frontends diff --git a/trunk/drivers/media/tuners/r820t.c b/trunk/drivers/media/tuners/r820t.c new file mode 100644 index 000000000000..7e02920f385a --- /dev/null +++ b/trunk/drivers/media/tuners/r820t.c @@ -0,0 +1,1486 @@ +/* + * Rafael Micro R820T driver + * + * Copyright (C) 2013 Mauro Carvalho Chehab + * + * This driver was written from scratch, based on an existing driver + * that it is part of rtl-sdr git tree, released under GPLv2: + * https://groups.google.com/forum/#!topic/ultra-cheap-sdr/Y3rBEOFtHug + * https://github.com/n1gp/gr-baz + * + * From what I understood from the threads, the original driver was converted + * to userspace from a Realtek tree. I couldn't find the original tree. + * However, the original driver look awkward on my eyes. So, I decided to + * write a new version from it from the scratch, while trying to reproduce + * everything found there. + * + * TODO: + * After locking, the original driver seems to have some routines to + * improve reception. This was not implemented here yet. + * + * RF Gain set/get is not implemented. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include "tuner-i2c.h" +#include +#include "r820t.h" + +/* + * FIXME: I think that there are only 32 registers, but better safe than + * sorry. After finishing the driver, we may review it. + */ +#define REG_SHADOW_START 5 +#define NUM_REGS 27 + +#define VER_NUM 49 + +static int debug; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "enable verbose debug messages"); + +/* + * enums and structures + */ + +enum xtal_cap_value { + XTAL_LOW_CAP_30P = 0, + XTAL_LOW_CAP_20P, + XTAL_LOW_CAP_10P, + XTAL_LOW_CAP_0P, + XTAL_HIGH_CAP_0P +}; + +struct r820t_priv { + struct list_head hybrid_tuner_instance_list; + const struct r820t_config *cfg; + struct tuner_i2c_props i2c_props; + struct mutex lock; + + u8 regs[NUM_REGS]; + u8 buf[NUM_REGS + 1]; + enum xtal_cap_value xtal_cap_sel; + u16 pll; /* kHz */ + u32 int_freq; + u8 fil_cal_code; + bool imr_done; + + /* Store current mode */ + u32 delsys; + enum v4l2_tuner_type type; + v4l2_std_id std; + u32 bw; /* in MHz */ + + bool has_lock; +}; + +struct r820t_freq_range { + u32 freq; + u8 open_d; + u8 rf_mux_ploy; + u8 tf_c; + u8 xtal_cap20p; + u8 xtal_cap10p; + u8 xtal_cap0p; + u8 imr_mem; /* Not used, currently */ +}; + +#define VCO_POWER_REF 0x02 + +/* + * Static constants + */ + +static LIST_HEAD(hybrid_tuner_instance_list); +static DEFINE_MUTEX(r820t_list_mutex); + +/* Those initial values start from REG_SHADOW_START */ +static const u8 r820t_init_array[NUM_REGS] = { + 0x83, 0x32, 0x75, /* 05 to 07 */ + 0xc0, 0x40, 0xd6, 0x6c, /* 08 to 0b */ + 0xf5, 0x63, 0x75, 0x68, /* 0c to 0f */ + 0x6c, 0x83, 0x80, 0x00, /* 10 to 13 */ + 0x0f, 0x00, 0xc0, 0x30, /* 14 to 17 */ + 0x48, 0xcc, 0x60, 0x00, /* 18 to 1b */ + 0x54, 0xae, 0x4a, 0xc0 /* 1c to 1f */ +}; + +/* Tuner frequency ranges */ +static const struct r820t_freq_range freq_ranges[] = { + { + .freq = 0, + .open_d = 0x08, /* low */ + .rf_mux_ploy = 0x02, /* R26[7:6]=0 (LPF) R26[1:0]=2 (low) */ + .tf_c = 0xdf, /* R27[7:0] band2,band0 */ + .xtal_cap20p = 0x02, /* R16[1:0] 20pF (10) */ + .xtal_cap10p = 0x01, + .xtal_cap0p = 0x00, + .imr_mem = 0, + }, { + .freq = 50, /* Start freq, in MHz */ + .open_d = 0x08, /* low */ + .rf_mux_ploy = 0x02, /* R26[7:6]=0 (LPF) R26[1:0]=2 (low) */ + .tf_c = 0xbe, /* R27[7:0] band4,band1 */ + .xtal_cap20p = 0x02, /* R16[1:0] 20pF (10) */ + .xtal_cap10p = 0x01, + .xtal_cap0p = 0x00, + .imr_mem = 0, + }, { + .freq = 55, /* Start freq, in MHz */ + .open_d = 0x08, /* low */ + .rf_mux_ploy = 0x02, /* R26[7:6]=0 (LPF) R26[1:0]=2 (low) */ + .tf_c = 0x8b, /* R27[7:0] band7,band4 */ + .xtal_cap20p = 0x02, /* R16[1:0] 20pF (10) */ + .xtal_cap10p = 0x01, + .xtal_cap0p = 0x00, + .imr_mem = 0, + }, { + .freq = 60, /* Start freq, in MHz */ + .open_d = 0x08, /* low */ + .rf_mux_ploy = 0x02, /* R26[7:6]=0 (LPF) R26[1:0]=2 (low) */ + .tf_c = 0x7b, /* R27[7:0] band8,band4 */ + .xtal_cap20p = 0x02, /* R16[1:0] 20pF (10) */ + .xtal_cap10p = 0x01, + .xtal_cap0p = 0x00, + .imr_mem = 0, + }, { + .freq = 65, /* Start freq, in MHz */ + .open_d = 0x08, /* low */ + .rf_mux_ploy = 0x02, /* R26[7:6]=0 (LPF) R26[1:0]=2 (low) */ + .tf_c = 0x69, /* R27[7:0] band9,band6 */ + .xtal_cap20p = 0x02, /* R16[1:0] 20pF (10) */ + .xtal_cap10p = 0x01, + .xtal_cap0p = 0x00, + .imr_mem = 0, + }, { + .freq = 70, /* Start freq, in MHz */ + .open_d = 0x08, /* low */ + .rf_mux_ploy = 0x02, /* R26[7:6]=0 (LPF) R26[1:0]=2 (low) */ + .tf_c = 0x58, /* R27[7:0] band10,band7 */ + .xtal_cap20p = 0x02, /* R16[1:0] 20pF (10) */ + .xtal_cap10p = 0x01, + .xtal_cap0p = 0x00, + .imr_mem = 0, + }, { + .freq = 75, /* Start freq, in MHz */ + .open_d = 0x00, /* high */ + .rf_mux_ploy = 0x02, /* R26[7:6]=0 (LPF) R26[1:0]=2 (low) */ + .tf_c = 0x44, /* R27[7:0] band11,band11 */ + .xtal_cap20p = 0x02, /* R16[1:0] 20pF (10) */ + .xtal_cap10p = 0x01, + .xtal_cap0p = 0x00, + .imr_mem = 0, + }, { + .freq = 80, /* Start freq, in MHz */ + .open_d = 0x00, /* high */ + .rf_mux_ploy = 0x02, /* R26[7:6]=0 (LPF) R26[1:0]=2 (low) */ + .tf_c = 0x44, /* R27[7:0] band11,band11 */ + .xtal_cap20p = 0x02, /* R16[1:0] 20pF (10) */ + .xtal_cap10p = 0x01, + .xtal_cap0p = 0x00, + .imr_mem = 0, + }, { + .freq = 90, /* Start freq, in MHz */ + .open_d = 0x00, /* high */ + .rf_mux_ploy = 0x02, /* R26[7:6]=0 (LPF) R26[1:0]=2 (low) */ + .tf_c = 0x34, /* R27[7:0] band12,band11 */ + .xtal_cap20p = 0x01, /* R16[1:0] 10pF (01) */ + .xtal_cap10p = 0x01, + .xtal_cap0p = 0x00, + .imr_mem = 0, + }, { + .freq = 100, /* Start freq, in MHz */ + .open_d = 0x00, /* high */ + .rf_mux_ploy = 0x02, /* R26[7:6]=0 (LPF) R26[1:0]=2 (low) */ + .tf_c = 0x34, /* R27[7:0] band12,band11 */ + .xtal_cap20p = 0x01, /* R16[1:0] 10pF (01) */ + .xtal_cap10p = 0x01, + .xtal_cap0p = 0x00, + .imr_mem = 0, + }, { + .freq = 110, /* Start freq, in MHz */ + .open_d = 0x00, /* high */ + .rf_mux_ploy = 0x02, /* R26[7:6]=0 (LPF) R26[1:0]=2 (low) */ + .tf_c = 0x24, /* R27[7:0] band13,band11 */ + .xtal_cap20p = 0x01, /* R16[1:0] 10pF (01) */ + .xtal_cap10p = 0x01, + .xtal_cap0p = 0x00, + .imr_mem = 1, + }, { + .freq = 120, /* Start freq, in MHz */ + .open_d = 0x00, /* high */ + .rf_mux_ploy = 0x02, /* R26[7:6]=0 (LPF) R26[1:0]=2 (low) */ + .tf_c = 0x24, /* R27[7:0] band13,band11 */ + .xtal_cap20p = 0x01, /* R16[1:0] 10pF (01) */ + .xtal_cap10p = 0x01, + .xtal_cap0p = 0x00, + .imr_mem = 1, + }, { + .freq = 140, /* Start freq, in MHz */ + .open_d = 0x00, /* high */ + .rf_mux_ploy = 0x02, /* R26[7:6]=0 (LPF) R26[1:0]=2 (low) */ + .tf_c = 0x14, /* R27[7:0] band14,band11 */ + .xtal_cap20p = 0x01, /* R16[1:0] 10pF (01) */ + .xtal_cap10p = 0x01, + .xtal_cap0p = 0x00, + .imr_mem = 1, + }, { + .freq = 180, /* Start freq, in MHz */ + .open_d = 0x00, /* high */ + .rf_mux_ploy = 0x02, /* R26[7:6]=0 (LPF) R26[1:0]=2 (low) */ + .tf_c = 0x13, /* R27[7:0] band14,band12 */ + .xtal_cap20p = 0x00, /* R16[1:0] 0pF (00) */ + .xtal_cap10p = 0x00, + .xtal_cap0p = 0x00, + .imr_mem = 1, + }, { + .freq = 220, /* Start freq, in MHz */ + .open_d = 0x00, /* high */ + .rf_mux_ploy = 0x02, /* R26[7:6]=0 (LPF) R26[1:0]=2 (low) */ + .tf_c = 0x13, /* R27[7:0] band14,band12 */ + .xtal_cap20p = 0x00, /* R16[1:0] 0pF (00) */ + .xtal_cap10p = 0x00, + .xtal_cap0p = 0x00, + .imr_mem = 2, + }, { + .freq = 250, /* Start freq, in MHz */ + .open_d = 0x00, /* high */ + .rf_mux_ploy = 0x02, /* R26[7:6]=0 (LPF) R26[1:0]=2 (low) */ + .tf_c = 0x11, /* R27[7:0] highest,highest */ + .xtal_cap20p = 0x00, /* R16[1:0] 0pF (00) */ + .xtal_cap10p = 0x00, + .xtal_cap0p = 0x00, + .imr_mem = 2, + }, { + .freq = 280, /* Start freq, in MHz */ + .open_d = 0x00, /* high */ + .rf_mux_ploy = 0x02, /* R26[7:6]=0 (LPF) R26[1:0]=2 (low) */ + .tf_c = 0x00, /* R27[7:0] highest,highest */ + .xtal_cap20p = 0x00, /* R16[1:0] 0pF (00) */ + .xtal_cap10p = 0x00, + .xtal_cap0p = 0x00, + .imr_mem = 2, + }, { + .freq = 310, /* Start freq, in MHz */ + .open_d = 0x00, /* high */ + .rf_mux_ploy = 0x41, /* R26[7:6]=1 (bypass) R26[1:0]=1 (middle) */ + .tf_c = 0x00, /* R27[7:0] highest,highest */ + .xtal_cap20p = 0x00, /* R16[1:0] 0pF (00) */ + .xtal_cap10p = 0x00, + .xtal_cap0p = 0x00, + .imr_mem = 2, + }, { + .freq = 450, /* Start freq, in MHz */ + .open_d = 0x00, /* high */ + .rf_mux_ploy = 0x41, /* R26[7:6]=1 (bypass) R26[1:0]=1 (middle) */ + .tf_c = 0x00, /* R27[7:0] highest,highest */ + .xtal_cap20p = 0x00, /* R16[1:0] 0pF (00) */ + .xtal_cap10p = 0x00, + .xtal_cap0p = 0x00, + .imr_mem = 3, + }, { + .freq = 588, /* Start freq, in MHz */ + .open_d = 0x00, /* high */ + .rf_mux_ploy = 0x40, /* R26[7:6]=1 (bypass) R26[1:0]=0 (highest) */ + .tf_c = 0x00, /* R27[7:0] highest,highest */ + .xtal_cap20p = 0x00, /* R16[1:0] 0pF (00) */ + .xtal_cap10p = 0x00, + .xtal_cap0p = 0x00, + .imr_mem = 3, + }, { + .freq = 650, /* Start freq, in MHz */ + .open_d = 0x00, /* high */ + .rf_mux_ploy = 0x40, /* R26[7:6]=1 (bypass) R26[1:0]=0 (highest) */ + .tf_c = 0x00, /* R27[7:0] highest,highest */ + .xtal_cap20p = 0x00, /* R16[1:0] 0pF (00) */ + .xtal_cap10p = 0x00, + .xtal_cap0p = 0x00, + .imr_mem = 4, + } +}; + +static int r820t_xtal_capacitor[][2] = { + { 0x0b, XTAL_LOW_CAP_30P }, + { 0x02, XTAL_LOW_CAP_20P }, + { 0x01, XTAL_LOW_CAP_10P }, + { 0x00, XTAL_LOW_CAP_0P }, + { 0x10, XTAL_HIGH_CAP_0P }, +}; + +/* + * I2C read/write code and shadow registers logic + */ +static void shadow_store(struct r820t_priv *priv, u8 reg, const u8 *val, + int len) +{ + int r = reg - REG_SHADOW_START; + + if (r < 0) { + len += r; + r = 0; + } + if (len <= 0) + return; + if (len > NUM_REGS) + len = NUM_REGS; + + tuner_dbg("%s: prev reg=%02x len=%d: %*ph\n", + __func__, r + REG_SHADOW_START, len, len, val); + + memcpy(&priv->regs[r], val, len); +} + +static int r820t_write(struct r820t_priv *priv, u8 reg, const u8 *val, + int len) +{ + int rc, size, pos = 0; + + /* Store the shadow registers */ + shadow_store(priv, reg, val, len); + + do { + if (len > priv->cfg->max_i2c_msg_len - 1) + size = priv->cfg->max_i2c_msg_len - 1; + else + size = len; + + /* Fill I2C buffer */ + priv->buf[0] = reg; + memcpy(&priv->buf[1], &val[pos], size); + + rc = tuner_i2c_xfer_send(&priv->i2c_props, priv->buf, size + 1); + if (rc != size + 1) { + tuner_info("%s: i2c wr failed=%d reg=%02x len=%d: %*ph\n", + __func__, rc, reg, size, size, &priv->buf[1]); + if (rc < 0) + return rc; + return -EREMOTEIO; + } + tuner_dbg("%s: i2c wr reg=%02x len=%d: %*ph\n", + __func__, reg, size, size, &priv->buf[1]); + + reg += size; + len -= size; + pos += size; + } while (len > 0); + + return 0; +} + +static int r820t_write_reg(struct r820t_priv *priv, u8 reg, u8 val) +{ + return r820t_write(priv, reg, &val, 1); +} + +static int r820t_write_reg_mask(struct r820t_priv *priv, u8 reg, u8 val, + u8 bit_mask) +{ + int r = reg - REG_SHADOW_START; + + if (r >= 0 && r < NUM_REGS) + val = (priv->regs[r] & ~bit_mask) | (val & bit_mask); + else + return -EINVAL; + + return r820t_write(priv, reg, &val, 1); +} + +static int r820_read(struct r820t_priv *priv, u8 reg, u8 *val, int len) +{ + int rc; + u8 *p = &priv->buf[1]; + + priv->buf[0] = reg; + + rc = tuner_i2c_xfer_send_recv(&priv->i2c_props, priv->buf, 1, p, len); + if (rc != len) { + tuner_info("%s: i2c rd failed=%d reg=%02x len=%d: %*ph\n", + __func__, rc, reg, len, len, p); + if (rc < 0) + return rc; + return -EREMOTEIO; + } + tuner_dbg("%s: i2c rd reg=%02x len=%d: %*ph\n", + __func__, reg, len, len, p); + + /* Copy data to the output buffer */ + memcpy(val, p, len); + + return 0; +} + +/* + * r820t tuning logic + */ + +static int r820t_set_mux(struct r820t_priv *priv, u32 freq) +{ + const struct r820t_freq_range *range; + int i, rc; + u8 val; + + /* Get the proper frequency range */ + freq = freq / 1000000; + for (i = 0; i < ARRAY_SIZE(freq_ranges) - 1; i++) { + if (freq < freq_ranges[i + 1].freq) + break; + } + range = &freq_ranges[i]; + + tuner_dbg("set r820t range#%d for frequency %d MHz\n", i, freq); + + /* Open Drain */ + rc = r820t_write_reg_mask(priv, 0x17, range->open_d, 0x08); + if (rc < 0) + return rc; + + /* RF_MUX,Polymux */ + rc = r820t_write_reg_mask(priv, 0x1a, range->rf_mux_ploy, 0xc3); + if (rc < 0) + return rc; + + /* TF BAND */ + rc = r820t_write_reg(priv, 0x1b, range->tf_c); + if (rc < 0) + return rc; + + /* XTAL CAP & Drive */ + switch (priv->xtal_cap_sel) { + case XTAL_LOW_CAP_30P: + case XTAL_LOW_CAP_20P: + val = range->xtal_cap20p | 0x08; + break; + case XTAL_LOW_CAP_10P: + val = range->xtal_cap10p | 0x08; + break; + case XTAL_HIGH_CAP_0P: + val = range->xtal_cap0p | 0x00; + break; + default: + case XTAL_LOW_CAP_0P: + val = range->xtal_cap0p | 0x08; + break; + } + rc = r820t_write_reg_mask(priv, 0x10, val, 0x0b); + if (rc < 0) + return rc; + + /* + * FIXME: the original driver has a logic there with preserves + * gain/phase from registers 8 and 9 reading the data from the + * registers before writing, if "IMF done". That code was sort of + * commented there, as the flag is always false. + */ + rc = r820t_write_reg_mask(priv, 0x08, 0, 0x3f); + if (rc < 0) + return rc; + + rc = r820t_write_reg_mask(priv, 0x09, 0, 0x3f); + + return rc; +} + +static int r820t_set_pll(struct r820t_priv *priv, u32 freq) +{ + u64 tmp64, vco_freq; + int rc, i; + u32 vco_fra; /* VCO contribution by SDM (kHz) */ + u32 vco_min = 1770000; + u32 vco_max = vco_min * 2; + u32 pll_ref; + u16 n_sdm = 2; + u16 sdm = 0; + u8 mix_div = 2; + u8 div_buf = 0; + u8 div_num = 0; + u8 ni, si, nint, vco_fine_tune, val; + u8 data[5]; + + freq = freq / 1000; /* Frequency in kHz */ + + pll_ref = priv->cfg->xtal / 1000; + + tuner_dbg("set r820t pll for frequency %d kHz = %d\n", freq, pll_ref); + + /* FIXME: this seems to be a hack - probably it can be removed */ + rc = r820t_write_reg_mask(priv, 0x10, 0x00, 0x00); + if (rc < 0) + return rc; + + /* set pll autotune = 128kHz */ + rc = r820t_write_reg_mask(priv, 0x1a, 0x00, 0x0c); + if (rc < 0) + return rc; + + /* set VCO current = 100 */ + rc = r820t_write_reg_mask(priv, 0x12, 0x80, 0xe0); + if (rc < 0) + return rc; + + /* Calculate divider */ + while (mix_div <= 64) { + if (((freq * mix_div) >= vco_min) && + ((freq * mix_div) < vco_max)) { + div_buf = mix_div; + while (div_buf > 2) { + div_buf = div_buf >> 1; + div_num++; + } + break; + } + mix_div = mix_div << 1; + } + + rc = r820_read(priv, 0x00, data, sizeof(data)); + if (rc < 0) + return rc; + + vco_fine_tune = (data[4] & 0x30) >> 4; + + if (vco_fine_tune > VCO_POWER_REF) + div_num = div_num - 1; + else if (vco_fine_tune < VCO_POWER_REF) + div_num = div_num + 1; + + rc = r820t_write_reg_mask(priv, 0x10, div_num << 5, 0xe0); + if (rc < 0) + return rc; + + vco_freq = (u64)(freq * (u64)mix_div); + + tmp64 = vco_freq; + do_div(tmp64, 2 * pll_ref); + nint = (u8)tmp64; + + tmp64 = vco_freq - ((u64)2) * pll_ref * nint; + do_div(tmp64, 1000); + vco_fra = (u16)(tmp64); + + pll_ref /= 1000; + + /* boundary spur prevention */ + if (vco_fra < pll_ref / 64) { + vco_fra = 0; + } else if (vco_fra > pll_ref * 127 / 64) { + vco_fra = 0; + nint++; + } else if ((vco_fra > pll_ref * 127 / 128) && (vco_fra < pll_ref)) { + vco_fra = pll_ref * 127 / 128; + } else if ((vco_fra > pll_ref) && (vco_fra < pll_ref * 129 / 128)) { + vco_fra = pll_ref * 129 / 128; + } + + if (nint > 63) { + tuner_info("No valid PLL values for %u kHz!\n", freq); + return -EINVAL; + } + + ni = (nint - 13) / 4; + si = nint - 4 * ni - 13; + + rc = r820t_write_reg(priv, 0x14, ni + (si << 6)); + if (rc < 0) + return rc; + + /* pw_sdm */ + if (!vco_fra) + val = 0x08; + else + val = 0x00; + + rc = r820t_write_reg_mask(priv, 0x12, val, 0x08); + if (rc < 0) + return rc; + + /* sdm calculator */ + while (vco_fra > 1) { + if (vco_fra > (2 * pll_ref / n_sdm)) { + sdm = sdm + 32768 / (n_sdm / 2); + vco_fra = vco_fra - 2 * pll_ref / n_sdm; + if (n_sdm >= 0x8000) + break; + } + n_sdm = n_sdm << 1; + } + + rc = r820t_write_reg_mask(priv, 0x16, sdm >> 8, 0x08); + if (rc < 0) + return rc; + rc = r820t_write_reg_mask(priv, 0x15, sdm & 0xff, 0x08); + if (rc < 0) + return rc; + + for (i = 0; i < 2; i++) { + /* + * FIXME: Rafael chips R620D, R828D and R828 seems to + * need 20 ms for analog TV + */ + msleep(10); + + /* Check if PLL has locked */ + rc = r820_read(priv, 0x00, data, 3); + if (rc < 0) + return rc; + if (data[2] & 0x40) + break; + + if (!i) { + /* Didn't lock. Increase VCO current */ + rc = r820t_write_reg_mask(priv, 0x12, 0x60, 0xe0); + if (rc < 0) + return rc; + } + } + + if (!(data[2] & 0x40)) { + priv->has_lock = false; + return 0; + } + + priv->has_lock = true; + tuner_dbg("tuner has lock at frequency %d kHz\n", freq); + + /* set pll autotune = 8kHz */ + rc = r820t_write_reg_mask(priv, 0x1a, 0x08, 0x08); + + return rc; +} + +static int r820t_sysfreq_sel(struct r820t_priv *priv, u32 freq, + enum v4l2_tuner_type type, + v4l2_std_id std, + u32 delsys) +{ + int rc; + u8 mixer_top, lna_top, cp_cur, div_buf_cur, lna_vth_l, mixer_vth_l; + u8 air_cable1_in, cable2_in, pre_dect, lna_discharge, filter_cur; + + tuner_dbg("adjusting tuner parameters for the standard\n"); + + switch (delsys) { + case SYS_DVBT: + if ((freq == 506000000) || (freq == 666000000) || + (freq == 818000000)) { + mixer_top = 0x14; /* mixer top:14 , top-1, low-discharge */ + lna_top = 0xe5; /* detect bw 3, lna top:4, predet top:2 */ + cp_cur = 0x28; /* 101, 0.2 */ + div_buf_cur = 0x20; /* 10, 200u */ + } else { + mixer_top = 0x24; /* mixer top:13 , top-1, low-discharge */ + lna_top = 0xe5; /* detect bw 3, lna top:4, predet top:2 */ + cp_cur = 0x38; /* 111, auto */ + div_buf_cur = 0x30; /* 11, 150u */ + } + lna_vth_l = 0x53; /* lna vth 0.84 , vtl 0.64 */ + mixer_vth_l = 0x75; /* mixer vth 1.04, vtl 0.84 */ + air_cable1_in = 0x00; + cable2_in = 0x00; + pre_dect = 0x40; + lna_discharge = 14; + filter_cur = 0x40; /* 10, low */ + break; + case SYS_DVBT2: + mixer_top = 0x24; /* mixer top:13 , top-1, low-discharge */ + lna_top = 0xe5; /* detect bw 3, lna top:4, predet top:2 */ + lna_vth_l = 0x53; /* lna vth 0.84 , vtl 0.64 */ + mixer_vth_l = 0x75; /* mixer vth 1.04, vtl 0.84 */ + air_cable1_in = 0x00; + cable2_in = 0x00; + pre_dect = 0x40; + lna_discharge = 14; + cp_cur = 0x38; /* 111, auto */ + div_buf_cur = 0x30; /* 11, 150u */ + filter_cur = 0x40; /* 10, low */ + break; + case SYS_ISDBT: + mixer_top = 0x24; /* mixer top:13 , top-1, low-discharge */ + lna_top = 0xe5; /* detect bw 3, lna top:4, predet top:2 */ + lna_vth_l = 0x75; /* lna vth 1.04 , vtl 0.84 */ + mixer_vth_l = 0x75; /* mixer vth 1.04, vtl 0.84 */ + air_cable1_in = 0x00; + cable2_in = 0x00; + pre_dect = 0x40; + lna_discharge = 14; + cp_cur = 0x38; /* 111, auto */ + div_buf_cur = 0x30; /* 11, 150u */ + filter_cur = 0x40; /* 10, low */ + break; + default: /* DVB-T 8M */ + mixer_top = 0x24; /* mixer top:13 , top-1, low-discharge */ + lna_top = 0xe5; /* detect bw 3, lna top:4, predet top:2 */ + lna_vth_l = 0x53; /* lna vth 0.84 , vtl 0.64 */ + mixer_vth_l = 0x75; /* mixer vth 1.04, vtl 0.84 */ + air_cable1_in = 0x00; + cable2_in = 0x00; + pre_dect = 0x40; + lna_discharge = 14; + cp_cur = 0x38; /* 111, auto */ + div_buf_cur = 0x30; /* 11, 150u */ + filter_cur = 0x40; /* 10, low */ + break; + } + + rc = r820t_write_reg_mask(priv, 0x1d, lna_top, 0xc7); + if (rc < 0) + return rc; + rc = r820t_write_reg_mask(priv, 0x1c, mixer_top, 0xf8); + if (rc < 0) + return rc; + rc = r820t_write_reg(priv, 0x0d, lna_vth_l); + if (rc < 0) + return rc; + rc = r820t_write_reg(priv, 0x0e, mixer_vth_l); + if (rc < 0) + return rc; + + /* Air-IN only for Astrometa */ + rc = r820t_write_reg_mask(priv, 0x05, air_cable1_in, 0x60); + if (rc < 0) + return rc; + rc = r820t_write_reg_mask(priv, 0x06, cable2_in, 0x08); + if (rc < 0) + return rc; + + rc = r820t_write_reg_mask(priv, 0x11, cp_cur, 0x38); + if (rc < 0) + return rc; + rc = r820t_write_reg_mask(priv, 0x17, div_buf_cur, 0x30); + if (rc < 0) + return rc; + rc = r820t_write_reg_mask(priv, 0x0a, filter_cur, 0x60); + if (rc < 0) + return rc; + /* + * Original driver initializes regs 0x05 and 0x06 with the + * same value again on this point. Probably, it is just an + * error there + */ + + /* + * Set LNA + */ + + tuner_dbg("adjusting LNA parameters\n"); + if (type != V4L2_TUNER_ANALOG_TV) { + /* LNA TOP: lowest */ + rc = r820t_write_reg_mask(priv, 0x1d, 0, 0x38); + if (rc < 0) + return rc; + + /* 0: normal mode */ + rc = r820t_write_reg_mask(priv, 0x1c, 0, 0x04); + if (rc < 0) + return rc; + + /* 0: PRE_DECT off */ + rc = r820t_write_reg_mask(priv, 0x06, 0, 0x40); + if (rc < 0) + return rc; + + /* agc clk 250hz */ + rc = r820t_write_reg_mask(priv, 0x1a, 0x30, 0x30); + if (rc < 0) + return rc; + + msleep(250); + + /* write LNA TOP = 3 */ + rc = r820t_write_reg_mask(priv, 0x1d, 0x18, 0x38); + if (rc < 0) + return rc; + + /* + * write discharge mode + * FIXME: IMHO, the mask here is wrong, but it matches + * what's there at the original driver + */ + rc = r820t_write_reg_mask(priv, 0x1c, mixer_top, 0x04); + if (rc < 0) + return rc; + + /* LNA discharge current */ + rc = r820t_write_reg_mask(priv, 0x1e, lna_discharge, 0x1f); + if (rc < 0) + return rc; + + /* agc clk 60hz */ + rc = r820t_write_reg_mask(priv, 0x1a, 0x20, 0x30); + if (rc < 0) + return rc; + } else { + /* PRE_DECT off */ + rc = r820t_write_reg_mask(priv, 0x06, 0, 0x40); + if (rc < 0) + return rc; + + /* write LNA TOP */ + rc = r820t_write_reg_mask(priv, 0x1d, lna_top, 0x38); + if (rc < 0) + return rc; + + /* + * write discharge mode + * FIXME: IMHO, the mask here is wrong, but it matches + * what's there at the original driver + */ + rc = r820t_write_reg_mask(priv, 0x1c, mixer_top, 0x04); + if (rc < 0) + return rc; + + /* LNA discharge current */ + rc = r820t_write_reg_mask(priv, 0x1e, lna_discharge, 0x1f); + if (rc < 0) + return rc; + + /* agc clk 1Khz, external det1 cap 1u */ + rc = r820t_write_reg_mask(priv, 0x1a, 0x00, 0x30); + if (rc < 0) + return rc; + + rc = r820t_write_reg_mask(priv, 0x10, 0x00, 0x04); + if (rc < 0) + return rc; + } + return 0; +} + +static int r820t_set_tv_standard(struct r820t_priv *priv, + unsigned bw, + enum v4l2_tuner_type type, + v4l2_std_id std, u32 delsys) + +{ + int rc, i; + u32 if_khz, filt_cal_lo; + u8 data[5], val; + u8 filt_gain, img_r, filt_q, hp_cor, ext_enable, loop_through; + u8 lt_att, flt_ext_widest, polyfil_cur; + bool need_calibration; + + tuner_dbg("selecting the delivery system\n"); + + if (delsys == SYS_ISDBT) { + if_khz = 4063; + filt_cal_lo = 59000; + filt_gain = 0x10; /* +3db, 6mhz on */ + img_r = 0x00; /* image negative */ + filt_q = 0x10; /* r10[4]:low q(1'b1) */ + hp_cor = 0x6a; /* 1.7m disable, +2cap, 1.25mhz */ + ext_enable = 0x40; /* r30[6], ext enable; r30[5]:0 ext at lna max */ + loop_through = 0x00; /* r5[7], lt on */ + lt_att = 0x00; /* r31[7], lt att enable */ + flt_ext_widest = 0x00; /* r15[7]: flt_ext_wide off */ + polyfil_cur = 0x60; /* r25[6:5]:min */ + } else { + if (bw <= 6) { + if_khz = 3570; + filt_cal_lo = 56000; /* 52000->56000 */ + filt_gain = 0x10; /* +3db, 6mhz on */ + img_r = 0x00; /* image negative */ + filt_q = 0x10; /* r10[4]:low q(1'b1) */ + hp_cor = 0x6b; /* 1.7m disable, +2cap, 1.0mhz */ + ext_enable = 0x60; /* r30[6]=1 ext enable; r30[5]:1 ext at lna max-1 */ + loop_through = 0x00; /* r5[7], lt on */ + lt_att = 0x00; /* r31[7], lt att enable */ + flt_ext_widest = 0x00; /* r15[7]: flt_ext_wide off */ + polyfil_cur = 0x60; /* r25[6:5]:min */ + } else if (bw == 7) { + if_khz = 4070; + filt_cal_lo = 60000; + filt_gain = 0x10; /* +3db, 6mhz on */ + img_r = 0x00; /* image negative */ + filt_q = 0x10; /* r10[4]:low q(1'b1) */ + hp_cor = 0x2b; /* 1.7m disable, +1cap, 1.0mhz */ + ext_enable = 0x60; /* r30[6]=1 ext enable; r30[5]:1 ext at lna max-1 */ + loop_through = 0x00; /* r5[7], lt on */ + lt_att = 0x00; /* r31[7], lt att enable */ + flt_ext_widest = 0x00; /* r15[7]: flt_ext_wide off */ + polyfil_cur = 0x60; /* r25[6:5]:min */ +#if 0 /* 7 MHz type 2 - nor sure why/where this is used - Perhaps Australia? */ + if_khz = 4570; + filt_cal_lo = 63000; + filt_gain = 0x10; /* +3db, 6mhz on */ + img_r = 0x00; /* image negative */ + filt_q = 0x10; /* r10[4]:low q(1'b1) */ + hp_cor = 0x2a; /* 1.7m disable, +1cap, 1.25mhz */ + ext_enable = 0x60; /* r30[6]=1 ext enable; r30[5]:1 ext at lna max-1 */ + loop_through = 0x00; /* r5[7], lt on */ + lt_att = 0x00; /* r31[7], lt att enable */ + flt_ext_widest = 0x00; /* r15[7]: flt_ext_wide off */ + polyfil_cur = 0x60; /* r25[6:5]:min */ +#endif + } else { + if_khz = 4570; + filt_cal_lo = 68500; + filt_gain = 0x10; /* +3db, 6mhz on */ + img_r = 0x00; /* image negative */ + filt_q = 0x10; /* r10[4]:low q(1'b1) */ + hp_cor = 0x0b; /* 1.7m disable, +0cap, 1.0mhz */ + ext_enable = 0x60; /* r30[6]=1 ext enable; r30[5]:1 ext at lna max-1 */ + loop_through = 0x00; /* r5[7], lt on */ + lt_att = 0x00; /* r31[7], lt att enable */ + flt_ext_widest = 0x00; /* r15[7]: flt_ext_wide off */ + polyfil_cur = 0x60; /* r25[6:5]:min */ + } + } + + /* Initialize the shadow registers */ + memcpy(priv->regs, r820t_init_array, sizeof(r820t_init_array)); + + /* Init Flag & Xtal_check Result */ + if (priv->imr_done) + val = 1 | priv->xtal_cap_sel << 1; + else + val = 0; + rc = r820t_write_reg_mask(priv, 0x0c, val, 0x0f); + if (rc < 0) + return rc; + + /* version */ + rc = r820t_write_reg_mask(priv, 0x13, VER_NUM, 0x3f); + if (rc < 0) + return rc; + + /* for LT Gain test */ + if (type != V4L2_TUNER_ANALOG_TV) { + rc = r820t_write_reg_mask(priv, 0x1d, 0x00, 0x38); + if (rc < 0) + return rc; + msleep(1); + } + priv->int_freq = if_khz; + + /* Check if standard changed. If so, filter calibration is needed */ + if (type != priv->type) + need_calibration = true; + else if ((type == V4L2_TUNER_ANALOG_TV) && (std != priv->std)) + need_calibration = true; + else if ((type == V4L2_TUNER_DIGITAL_TV) && + ((delsys != priv->delsys) || bw != priv->bw)) + need_calibration = true; + else + need_calibration = false; + + if (need_calibration) { + tuner_dbg("calibrating the tuner\n"); + for (i = 0; i < 2; i++) { + /* Set filt_cap */ + rc = r820t_write_reg_mask(priv, 0x0b, hp_cor, 0x60); + if (rc < 0) + return rc; + + /* set cali clk =on */ + rc = r820t_write_reg_mask(priv, 0x0f, 0x04, 0x04); + if (rc < 0) + return rc; + + /* X'tal cap 0pF for PLL */ + rc = r820t_write_reg_mask(priv, 0x10, 0x00, 0x03); + if (rc < 0) + return rc; + + rc = r820t_set_pll(priv, filt_cal_lo); + if (rc < 0 || !priv->has_lock) + return rc; + + /* Start Trigger */ + rc = r820t_write_reg_mask(priv, 0x0b, 0x10, 0x10); + if (rc < 0) + return rc; + + msleep(1); + + /* Stop Trigger */ + rc = r820t_write_reg_mask(priv, 0x0b, 0x00, 0x10); + if (rc < 0) + return rc; + + /* set cali clk =off */ + rc = r820t_write_reg_mask(priv, 0x0f, 0x00, 0x04); + if (rc < 0) + return rc; + + /* Check if calibration worked */ + rc = r820_read(priv, 0x00, data, sizeof(data)); + if (rc < 0) + return rc; + + priv->fil_cal_code = data[4] & 0x0f; + if (priv->fil_cal_code && priv->fil_cal_code != 0x0f) + break; + } + /* narrowest */ + if (priv->fil_cal_code == 0x0f) + priv->fil_cal_code = 0; + } + + rc = r820t_write_reg_mask(priv, 0x0a, + filt_q | priv->fil_cal_code, 0x1f); + if (rc < 0) + return rc; + + /* Set BW, Filter_gain, & HP corner */ + rc = r820t_write_reg_mask(priv, 0x0b, hp_cor, 0x10); + if (rc < 0) + return rc; + + + /* Set Img_R */ + rc = r820t_write_reg_mask(priv, 0x07, img_r, 0x80); + if (rc < 0) + return rc; + + /* Set filt_3dB, V6MHz */ + rc = r820t_write_reg_mask(priv, 0x06, filt_gain, 0x30); + if (rc < 0) + return rc; + + /* channel filter extension */ + rc = r820t_write_reg_mask(priv, 0x1e, ext_enable, 0x60); + if (rc < 0) + return rc; + + /* Loop through */ + rc = r820t_write_reg_mask(priv, 0x05, loop_through, 0x80); + if (rc < 0) + return rc; + + /* Loop through attenuation */ + rc = r820t_write_reg_mask(priv, 0x1f, lt_att, 0x80); + if (rc < 0) + return rc; + + /* filter extension widest */ + rc = r820t_write_reg_mask(priv, 0x0f, flt_ext_widest, 0x80); + if (rc < 0) + return rc; + + /* RF poly filter current */ + rc = r820t_write_reg_mask(priv, 0x19, polyfil_cur, 0x60); + if (rc < 0) + return rc; + + /* Store current standard. If it changes, re-calibrate the tuner */ + priv->delsys = delsys; + priv->type = type; + priv->std = std; + priv->bw = bw; + + return 0; +} + +static int generic_set_freq(struct dvb_frontend *fe, + u32 freq /* in HZ */, + unsigned bw, + enum v4l2_tuner_type type, + v4l2_std_id std, u32 delsys) +{ + struct r820t_priv *priv = fe->tuner_priv; + int rc = -EINVAL; + u32 lo_freq; + + tuner_dbg("should set frequency to %d kHz, bw %d MHz\n", + freq / 1000, bw); + + mutex_lock(&priv->lock); + + if ((type == V4L2_TUNER_ANALOG_TV) && (std == V4L2_STD_SECAM_LC)) + lo_freq = freq - priv->int_freq; + else + lo_freq = freq + priv->int_freq; + + rc = r820t_set_tv_standard(priv, bw, type, std, delsys); + if (rc < 0) + goto err; + + rc = r820t_set_mux(priv, lo_freq); + if (rc < 0) + goto err; + rc = r820t_set_pll(priv, lo_freq); + if (rc < 0 || !priv->has_lock) + goto err; + + rc = r820t_sysfreq_sel(priv, freq, type, std, delsys); +err: + mutex_unlock(&priv->lock); + + if (rc < 0) + tuner_dbg("%s: failed=%d\n", __func__, rc); + return rc; +} + +/* + * r820t standby logic + */ + +static int r820t_standby(struct r820t_priv *priv) +{ + int rc; + + rc = r820t_write_reg(priv, 0x06, 0xb1); + if (rc < 0) + return rc; + rc = r820t_write_reg(priv, 0x05, 0x03); + if (rc < 0) + return rc; + rc = r820t_write_reg(priv, 0x07, 0x3a); + if (rc < 0) + return rc; + rc = r820t_write_reg(priv, 0x08, 0x40); + if (rc < 0) + return rc; + rc = r820t_write_reg(priv, 0x09, 0xc0); + if (rc < 0) + return rc; + rc = r820t_write_reg(priv, 0x0a, 0x36); + if (rc < 0) + return rc; + rc = r820t_write_reg(priv, 0x0c, 0x35); + if (rc < 0) + return rc; + rc = r820t_write_reg(priv, 0x0f, 0x68); + if (rc < 0) + return rc; + rc = r820t_write_reg(priv, 0x11, 0x03); + if (rc < 0) + return rc; + rc = r820t_write_reg(priv, 0x17, 0xf4); + if (rc < 0) + return rc; + rc = r820t_write_reg(priv, 0x19, 0x0c); + + /* Force initial calibration */ + priv->type = -1; + + return rc; +} + +/* + * r820t device init logic + */ + +static int r820t_xtal_check(struct r820t_priv *priv) +{ + int rc, i; + u8 data[3], val; + + /* Initialize the shadow registers */ + memcpy(priv->regs, r820t_init_array, sizeof(r820t_init_array)); + + /* cap 30pF & Drive Low */ + rc = r820t_write_reg_mask(priv, 0x10, 0x0b, 0x0b); + if (rc < 0) + return rc; + + /* set pll autotune = 128kHz */ + rc = r820t_write_reg_mask(priv, 0x1a, 0x00, 0x0c); + if (rc < 0) + return rc; + + /* set manual initial reg = 111111; */ + rc = r820t_write_reg_mask(priv, 0x13, 0x7f, 0x7f); + if (rc < 0) + return rc; + + /* set auto */ + rc = r820t_write_reg_mask(priv, 0x13, 0x00, 0x40); + if (rc < 0) + return rc; + + /* Try several xtal capacitor alternatives */ + for (i = 0; i < ARRAY_SIZE(r820t_xtal_capacitor); i++) { + rc = r820t_write_reg_mask(priv, 0x10, + r820t_xtal_capacitor[i][0], 0x1b); + if (rc < 0) + return rc; + + msleep(5); + + rc = r820_read(priv, 0x00, data, sizeof(data)); + if (rc < 0) + return rc; + if ((!data[2]) & 0x40) + continue; + + val = data[2] & 0x3f; + + if (priv->cfg->xtal == 16000000 && (val > 29 || val < 23)) + break; + + if (val != 0x3f) + break; + } + + if (i == ARRAY_SIZE(r820t_xtal_capacitor)) + return -EINVAL; + + return r820t_xtal_capacitor[i][1]; +} + +/* + * r820t frontend operations and tuner attach code + */ + +static int r820t_init(struct dvb_frontend *fe) +{ + struct r820t_priv *priv = fe->tuner_priv; + int rc, i; + int xtal_cap = 0; + + tuner_dbg("%s:\n", __func__); + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + + mutex_lock(&priv->lock); + + if ((priv->cfg->rafael_chip == CHIP_R820T) || + (priv->cfg->rafael_chip == CHIP_R828S) || + (priv->cfg->rafael_chip == CHIP_R820C)) { + priv->xtal_cap_sel = XTAL_HIGH_CAP_0P; + } else { + for (i = 0; i < 3; i++) { + rc = r820t_xtal_check(priv); + if (rc < 0) + goto err; + if (!i || rc > xtal_cap) + xtal_cap = rc; + } + priv->xtal_cap_sel = xtal_cap; + } + + /* Initialize registers */ + rc = r820t_write(priv, 0x05, + r820t_init_array, sizeof(r820t_init_array)); + + mutex_unlock(&priv->lock); + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + + return rc; +err: + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + + tuner_dbg("%s: failed=%d\n", __func__, rc); + return rc; +} + +static int r820t_sleep(struct dvb_frontend *fe) +{ + struct r820t_priv *priv = fe->tuner_priv; + int rc; + + tuner_dbg("%s:\n", __func__); + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + + mutex_lock(&priv->lock); + rc = r820t_standby(priv); + mutex_unlock(&priv->lock); + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + + tuner_dbg("%s: failed=%d\n", __func__, rc); + return rc; +} + +static int r820t_set_analog_freq(struct dvb_frontend *fe, + struct analog_parameters *p) +{ + struct r820t_priv *priv = fe->tuner_priv; + unsigned bw; + + tuner_dbg("%s called\n", __func__); + + /* if std is not defined, choose one */ + if (!p->std) + p->std = V4L2_STD_MN; + + if ((p->std == V4L2_STD_PAL_M) || (p->std == V4L2_STD_NTSC)) + bw = 6; + else + bw = 8; + + return generic_set_freq(fe, 62500l * p->frequency, bw, + V4L2_TUNER_ANALOG_TV, p->std, SYS_UNDEFINED); +} + +static int r820t_set_params(struct dvb_frontend *fe) +{ + struct r820t_priv *priv = fe->tuner_priv; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + int rc; + unsigned bw; + + tuner_dbg("%s: delivery_system=%d frequency=%d bandwidth_hz=%d\n", + __func__, c->delivery_system, c->frequency, c->bandwidth_hz); + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + + bw = (c->bandwidth_hz + 500000) / 1000000; + if (!bw) + bw = 8; + + rc = generic_set_freq(fe, c->frequency, bw, + V4L2_TUNER_DIGITAL_TV, 0, c->delivery_system); + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + + if (rc) + tuner_dbg("%s: failed=%d\n", __func__, rc); + return rc; +} + +static int r820t_signal(struct dvb_frontend *fe, u16 *strength) +{ + struct r820t_priv *priv = fe->tuner_priv; + + if (priv->has_lock) + *strength = 0xffff; + else + *strength = 0; + + return 0; +} + +static int r820t_get_if_frequency(struct dvb_frontend *fe, u32 *frequency) +{ + struct r820t_priv *priv = fe->tuner_priv; + + tuner_dbg("%s:\n", __func__); + + *frequency = priv->int_freq; + + return 0; +} + +static int r820t_release(struct dvb_frontend *fe) +{ + struct r820t_priv *priv = fe->tuner_priv; + + tuner_dbg("%s:\n", __func__); + + mutex_lock(&r820t_list_mutex); + + if (priv) + hybrid_tuner_release_state(priv); + + mutex_unlock(&r820t_list_mutex); + + fe->tuner_priv = NULL; + + kfree(fe->tuner_priv); + + return 0; +} + +static const struct dvb_tuner_ops r820t_tuner_ops = { + .info = { + .name = "Rafael Micro R820T", + .frequency_min = 42000000, + .frequency_max = 1002000000, + }, + .init = r820t_init, + .release = r820t_release, + .sleep = r820t_sleep, + .set_params = r820t_set_params, + .set_analog_params = r820t_set_analog_freq, + .get_if_frequency = r820t_get_if_frequency, + .get_rf_strength = r820t_signal, +}; + +struct dvb_frontend *r820t_attach(struct dvb_frontend *fe, + struct i2c_adapter *i2c, + const struct r820t_config *cfg) +{ + struct r820t_priv *priv; + int rc = -ENODEV; + u8 data[5]; + int instance; + + mutex_lock(&r820t_list_mutex); + + instance = hybrid_tuner_request_state(struct r820t_priv, priv, + hybrid_tuner_instance_list, + i2c, cfg->i2c_addr, + "r820t"); + switch (instance) { + case 0: + /* memory allocation failure */ + goto err_no_gate; + break; + case 1: + /* new tuner instance */ + priv->cfg = cfg; + + mutex_init(&priv->lock); + + fe->tuner_priv = priv; + break; + case 2: + /* existing tuner instance */ + fe->tuner_priv = priv; + break; + } + + memcpy(&fe->ops.tuner_ops, &r820t_tuner_ops, sizeof(r820t_tuner_ops)); + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + + /* check if the tuner is there */ + rc = r820_read(priv, 0x00, data, sizeof(data)); + if (rc < 0) + goto err; + + rc = r820t_sleep(fe); + if (rc < 0) + goto err; + + tuner_info("Rafael Micro r820t successfully identified\n"); + + fe->tuner_priv = priv; + memcpy(&fe->ops.tuner_ops, &r820t_tuner_ops, + sizeof(struct dvb_tuner_ops)); + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + + mutex_unlock(&r820t_list_mutex); + + return fe; +err: + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + +err_no_gate: + mutex_unlock(&r820t_list_mutex); + + tuner_info("%s: failed=%d\n", __func__, rc); + r820t_release(fe); + return NULL; +} +EXPORT_SYMBOL_GPL(r820t_attach); + +MODULE_DESCRIPTION("Rafael Micro r820t silicon tuner driver"); +MODULE_AUTHOR("Mauro Carvalho Chehab "); +MODULE_LICENSE("GPL"); diff --git a/trunk/drivers/media/tuners/r820t.h b/trunk/drivers/media/tuners/r820t.h new file mode 100644 index 000000000000..a64a7b630729 --- /dev/null +++ b/trunk/drivers/media/tuners/r820t.h @@ -0,0 +1,55 @@ +/* + * Elonics R820T silicon tuner driver + * + * Copyright (C) 2012 Antti Palosaari + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef R820T_H +#define R820T_H + +#include +#include "dvb_frontend.h" + +enum r820t_chip { + CHIP_R820T, + CHIP_R828S, + CHIP_R820C, +}; + +struct r820t_config { + u8 i2c_addr; /* 0x34 */ + + u32 xtal; + enum r820t_chip rafael_chip; + unsigned max_i2c_msg_len; +}; + +#if IS_ENABLED(CONFIG_MEDIA_TUNER_R820T) +struct dvb_frontend *r820t_attach(struct dvb_frontend *fe, + struct i2c_adapter *i2c, + const struct r820t_config *cfg); +#else +static inline struct dvb_frontend *r820t_attach(struct dvb_frontend *fe, + struct i2c_adapter *i2c, + const struct r820t_config *cfg) +{ + pr_warn("%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +#endif + +#endif diff --git a/trunk/drivers/media/usb/dvb-usb-v2/anysee.c b/trunk/drivers/media/usb/dvb-usb-v2/anysee.c index 90cfa35ef6e6..2e762742a4c7 100644 --- a/trunk/drivers/media/usb/dvb-usb-v2/anysee.c +++ b/trunk/drivers/media/usb/dvb-usb-v2/anysee.c @@ -635,7 +635,7 @@ static int anysee_frontend_attach(struct dvb_usb_adapter *adap) { struct anysee_state *state = adap_to_priv(adap); struct dvb_usb_device *d = adap_to_d(adap); - int ret = 0; + int ret; u8 tmp; struct i2c_msg msg[2] = { { @@ -882,7 +882,7 @@ static int anysee_frontend_attach(struct dvb_usb_adapter *adap) /* we have no frontend :-( */ ret = -ENODEV; dev_err(&d->udev->dev, - "%s: Unsupported Anysee version. Please report to .\n", + "%s: Unsupported Anysee version. Please report the .\n", KBUILD_MODNAME); } error: diff --git a/trunk/drivers/media/usb/dvb-usb/dib0700_devices.c b/trunk/drivers/media/usb/dvb-usb/dib0700_devices.c index f08136052f9c..11798426fa88 100644 --- a/trunk/drivers/media/usb/dvb-usb/dib0700_devices.c +++ b/trunk/drivers/media/usb/dvb-usb/dib0700_devices.c @@ -1431,22 +1431,13 @@ static int dib8090_get_adc_power(struct dvb_frontend *fe) return dib8000_get_adc_power(fe, 1); } -static void dib8090_agc_control(struct dvb_frontend *fe, u8 restart) -{ - deb_info("AGC control callback: %i\n", restart); - dib0090_dcc_freq(fe, restart); - - if (restart == 0) /* before AGC startup */ - dib0090_set_dc_servo(fe, 1); -} - static struct dib8000_config dib809x_dib8000_config[2] = { { .output_mpeg2_in_188_bytes = 1, .agc_config_count = 2, .agc = dib8090_agc_config, - .agc_control = dib8090_agc_control, + .agc_control = dib0090_dcc_freq, .pll = &dib8090_pll_config_12mhz, .tuner_is_baseband = 1, @@ -1465,7 +1456,7 @@ static struct dib8000_config dib809x_dib8000_config[2] = { .agc_config_count = 2, .agc = dib8090_agc_config, - .agc_control = dib8090_agc_control, + .agc_control = dib0090_dcc_freq, .pll = &dib8090_pll_config_12mhz, .tuner_is_baseband = 1, @@ -1513,89 +1504,28 @@ static struct dib0090_config dib809x_dib0090_config = { .fref_clock_ratio = 6, }; -static u8 dib8090_compute_pll_parameters(struct dvb_frontend *fe) -{ - u8 optimal_pll_ratio = 20; - u32 freq_adc, ratio, rest, max = 0; - u8 pll_ratio; - - for (pll_ratio = 17; pll_ratio <= 20; pll_ratio++) { - freq_adc = 12 * pll_ratio * (1 << 8) / 16; - ratio = ((fe->dtv_property_cache.frequency / 1000) * (1 << 8) / 1000) / freq_adc; - rest = ((fe->dtv_property_cache.frequency / 1000) * (1 << 8) / 1000) - ratio * freq_adc; - - if (rest > freq_adc / 2) - rest = freq_adc - rest; - deb_info("PLL ratio=%i rest=%i\n", pll_ratio, rest); - if ((rest > max) && (rest > 717)) { - optimal_pll_ratio = pll_ratio; - max = rest; - } - } - deb_info("optimal PLL ratio=%i\n", optimal_pll_ratio); - - return optimal_pll_ratio; -} - static int dib8096_set_param_override(struct dvb_frontend *fe) { + struct dtv_frontend_properties *p = &fe->dtv_property_cache; struct dvb_usb_adapter *adap = fe->dvb->priv; struct dib0700_adapter_state *state = adap->priv; - u8 pll_ratio, band = BAND_OF_FREQUENCY(fe->dtv_property_cache.frequency / 1000); - u16 target, ltgain, rf_gain_limit; - u32 timf; + u8 band = BAND_OF_FREQUENCY(p->frequency/1000); + u16 target; int ret = 0; enum frontend_tune_state tune_state = CT_SHUTDOWN; - - switch (band) { - default: - deb_info("Warning : Rf frequency (%iHz) is not in the supported range, using VHF switch ", fe->dtv_property_cache.frequency); - case BAND_VHF: - dib8000_set_gpio(fe, 3, 0, 1); - break; - case BAND_UHF: - dib8000_set_gpio(fe, 3, 0, 0); - break; - } + u16 ltgain, rf_gain_limit; ret = state->set_param_save(fe); if (ret < 0) return ret; - if (fe->dtv_property_cache.bandwidth_hz != 6000000) { - deb_info("only 6MHz bandwidth is supported\n"); - return -EINVAL; - } - - /** Update PLL if needed ratio **/ - dib8000_update_pll(fe, &dib8090_pll_config_12mhz, fe->dtv_property_cache.bandwidth_hz / 1000, 0); - - /** Get optimize PLL ratio to remove spurious **/ - pll_ratio = dib8090_compute_pll_parameters(fe); - if (pll_ratio == 17) - timf = 21387946; - else if (pll_ratio == 18) - timf = 20199727; - else if (pll_ratio == 19) - timf = 19136583; - else - timf = 18179756; - - /** Update ratio **/ - dib8000_update_pll(fe, &dib8090_pll_config_12mhz, fe->dtv_property_cache.bandwidth_hz / 1000, pll_ratio); - - dib8000_ctrl_timf(fe, DEMOD_TIMF_SET, timf); + target = (dib0090_get_wbd_target(fe) * 8 * 18 / 33 + 1) / 2; + dib8000_set_wbd_ref(fe, target); - if (band != BAND_CBAND) { - /* dib0090_get_wbd_target is returning any possible temperature compensated wbd-target */ - target = (dib0090_get_wbd_target(fe) * 8 * 18 / 33 + 1) / 2; - dib8000_set_wbd_ref(fe, target); - } if (band == BAND_CBAND) { deb_info("tuning in CBAND - soft-AGC startup\n"); dib0090_set_tune_state(fe, CT_AGC_START); - do { ret = dib0090_gain_control(fe); msleep(ret); @@ -1604,17 +1534,14 @@ static int dib8096_set_param_override(struct dvb_frontend *fe) dib8000_set_gpio(fe, 6, 0, 1); else if (tune_state == CT_AGC_STEP_1) { dib0090_get_current_gain(fe, NULL, NULL, &rf_gain_limit, <gain); - if (rf_gain_limit < 2000) /* activate the external attenuator in case of very high input power */ + if (rf_gain_limit == 0) dib8000_set_gpio(fe, 6, 0, 0); } } while (tune_state < CT_AGC_STOP); - - deb_info("switching to PWM AGC\n"); dib0090_pwm_gain_reset(fe); dib8000_pwm_agc_reset(fe); dib8000_set_tune_state(fe, CT_DEMOD_START); } else { - /* for everything else than CBAND we are using standard AGC */ deb_info("not tuning in CBAND - standard AGC startup\n"); dib0090_pwm_gain_reset(fe); } @@ -1887,92 +1814,21 @@ struct dibx090p_adc { u32 pll_prediv; /* New loopdiv */ }; -struct dibx090p_best_adc { - u32 timf; - u32 pll_loopdiv; - u32 pll_prediv; +struct dibx090p_adc dib8090p_adc_tab[] = { + { 50000, 17043521, 16, 3}, /* 64 MHz */ + {878000, 20199729, 9, 1}, /* 60 MHz */ + {0xffffffff, 0, 0, 0}, /* 60 MHz */ }; -static int dib8096p_get_best_sampling(struct dvb_frontend *fe, struct dibx090p_best_adc *adc) -{ - u8 spur = 0, prediv = 0, loopdiv = 0, min_prediv = 1, max_prediv = 1; - u16 xtal = 12000; - u16 fcp_min = 1900; /* PLL, Minimum Frequency of phase comparator (KHz) */ - u16 fcp_max = 20000; /* PLL, Maximum Frequency of phase comparator (KHz) */ - u32 fmem_max = 140000; /* 140MHz max SDRAM freq */ - u32 fdem_min = 66000; - u32 fcp = 0, fs = 0, fdem = 0, fmem = 0; - u32 harmonic_id = 0; - - adc->timf = 0; - adc->pll_loopdiv = loopdiv; - adc->pll_prediv = prediv; - - deb_info("bandwidth = %d", fe->dtv_property_cache.bandwidth_hz); - - /* Find Min and Max prediv */ - while ((xtal / max_prediv) >= fcp_min) - max_prediv++; - - max_prediv--; - min_prediv = max_prediv; - while ((xtal / min_prediv) <= fcp_max) { - min_prediv--; - if (min_prediv == 1) - break; - } - deb_info("MIN prediv = %d : MAX prediv = %d", min_prediv, max_prediv); - - min_prediv = 1; - - for (prediv = min_prediv; prediv < max_prediv; prediv++) { - fcp = xtal / prediv; - if (fcp > fcp_min && fcp < fcp_max) { - for (loopdiv = 1; loopdiv < 64; loopdiv++) { - fmem = ((xtal/prediv) * loopdiv); - fdem = fmem / 2; - fs = fdem / 4; - - /* test min/max system restrictions */ - if ((fdem >= fdem_min) && (fmem <= fmem_max) && (fs >= fe->dtv_property_cache.bandwidth_hz / 1000)) { - spur = 0; - /* test fs harmonics positions */ - for (harmonic_id = (fe->dtv_property_cache.frequency / (1000 * fs)); harmonic_id <= ((fe->dtv_property_cache.frequency / (1000 * fs)) + 1); harmonic_id++) { - if (((fs * harmonic_id) >= (fe->dtv_property_cache.frequency / 1000 - (fe->dtv_property_cache.bandwidth_hz / 2000))) && ((fs * harmonic_id) <= (fe->dtv_property_cache.frequency / 1000 + (fe->dtv_property_cache.bandwidth_hz / 2000)))) { - spur = 1; - break; - } - } - - if (!spur) { - adc->pll_loopdiv = loopdiv; - adc->pll_prediv = prediv; - adc->timf = (4260880253U / fdem) * (1 << 8); - adc->timf += ((4260880253U % fdem) << 8) / fdem; - - deb_info("RF %6d; BW %6d; Xtal %6d; Fmem %6d; Fdem %6d; Fs %6d; Prediv %2d; Loopdiv %2d; Timf %8d;", fe->dtv_property_cache.frequency, fe->dtv_property_cache.bandwidth_hz, xtal, fmem, fdem, fs, prediv, loopdiv, adc->timf); - break; - } - } - } - } - if (!spur) - break; - } - - if (adc->pll_loopdiv == 0 && adc->pll_prediv == 0) - return -EINVAL; - return 0; -} - static int dib8096p_agc_startup(struct dvb_frontend *fe) { + struct dtv_frontend_properties *p = &fe->dtv_property_cache; struct dvb_usb_adapter *adap = fe->dvb->priv; struct dib0700_adapter_state *state = adap->priv; struct dibx000_bandwidth_config pll; - struct dibx090p_best_adc adc; u16 target; - int ret; + int better_sampling_freq = 0, ret; + struct dibx090p_adc *adc_table = &dib8090p_adc_tab[0]; ret = state->set_param_save(fe); if (ret < 0) @@ -1985,27 +1841,23 @@ static int dib8096p_agc_startup(struct dvb_frontend *fe) target = (dib0090_get_wbd_target(fe) * 8 + 1) / 2; dib8000_set_wbd_ref(fe, target); - if (dib8096p_get_best_sampling(fe, &adc) == 0) { - pll.pll_ratio = adc.pll_loopdiv; - pll.pll_prediv = adc.pll_prediv; - dib0700_set_i2c_speed(adap->dev, 200); - dib8000_update_pll(fe, &pll, fe->dtv_property_cache.bandwidth_hz / 1000, 0); - dib8000_ctrl_timf(fe, DEMOD_TIMF_SET, adc.timf); - dib0700_set_i2c_speed(adap->dev, 1000); + while (p->frequency / 1000 > adc_table->freq) { + better_sampling_freq = 1; + adc_table++; + } + + if ((adc_table->freq != 0xffffffff) && better_sampling_freq) { + pll.pll_ratio = adc_table->pll_loopdiv; + pll.pll_prediv = adc_table->pll_prediv; + dib8000_update_pll(fe, &pll); + dib8000_ctrl_timf(fe, DEMOD_TIMF_SET, adc_table->timf); } return 0; } static int tfe8096p_frontend_attach(struct dvb_usb_adapter *adap) { - struct dib0700_state *st = adap->dev->priv; - u32 fw_version; - - dib0700_get_version(adap->dev, NULL, NULL, &fw_version, NULL); - if (fw_version >= 0x10200) - st->fw_use_new_i2c_api = 1; - dib0700_set_gpio(adap->dev, GPIO6, GPIO_OUT, 1); msleep(20); dib0700_set_gpio(adap->dev, GPIO9, GPIO_OUT, 1); @@ -2390,7 +2242,13 @@ static int nim9090md_tuner_attach(struct dvb_usb_adapter *adap) } /* NIM7090 */ -static int dib7090p_get_best_sampling(struct dvb_frontend *fe , struct dibx090p_best_adc *adc) +struct dib7090p_best_adc { + u32 timf; + u32 pll_loopdiv; + u32 pll_prediv; +}; + +static int dib7090p_get_best_sampling(struct dvb_frontend *fe , struct dib7090p_best_adc *adc) { u8 spur = 0, prediv = 0, loopdiv = 0, min_prediv = 1, max_prediv = 1; @@ -2469,7 +2327,7 @@ static int dib7090_agc_startup(struct dvb_frontend *fe) struct dib0700_adapter_state *state = adap->priv; struct dibx000_bandwidth_config pll; u16 target; - struct dibx090p_best_adc adc; + struct dib7090p_best_adc adc; int ret; ret = state->set_param_save(fe); @@ -2499,16 +2357,36 @@ static int dib7090_agc_restart(struct dvb_frontend *fe, u8 restart) return 0; } -static int tfe7790p_update_lna(struct dvb_frontend *fe, u16 agc_global) +static int dib7090e_update_lna(struct dvb_frontend *fe, u16 agc_global) { - deb_info("update LNA: agc global=%i", agc_global); + u16 agc1 = 0, agc2, wbd = 0, wbd_target, wbd_offset, threshold_agc1; + s16 wbd_delta; - if (agc_global < 25000) { - dib7000p_set_gpio(fe, 8, 0, 0); - dib7000p_set_agc1_min(fe, 0); + if ((fe->dtv_property_cache.frequency) < 400000000) + threshold_agc1 = 25000; + else + threshold_agc1 = 30000; + + wbd_target = (dib0090_get_wbd_target(fe)*8+1)/2; + wbd_offset = dib0090_get_wbd_offset(fe); + dib7000p_get_agc_values(fe, NULL, &agc1, &agc2, &wbd); + wbd_delta = (s16)wbd - (((s16)wbd_offset+10)*4) ; + + deb_info("update lna, agc_global=%d agc1=%d agc2=%d", + agc_global, agc1, agc2); + deb_info("update lna, wbd=%d wbd target=%d wbd offset=%d wbd delta=%d", + wbd, wbd_target, wbd_offset, wbd_delta); + + if ((agc1 < threshold_agc1) && (wbd_delta > 0)) { + dib0090_set_switch(fe, 1, 1, 1); + dib0090_set_vga(fe, 0); + dib0090_update_rframp_7090(fe, 0); + dib0090_update_tuning_table_7090(fe, 0); } else { - dib7000p_set_gpio(fe, 8, 0, 1); - dib7000p_set_agc1_min(fe, 32768); + dib0090_set_vga(fe, 1); + dib0090_update_rframp_7090(fe, 1); + dib0090_update_tuning_table_7090(fe, 1); + dib0090_set_switch(fe, 0, 0, 0); } return 0; @@ -2522,6 +2400,15 @@ static struct dib0090_wbd_slope dib7090_wbd_table[] = { { 0xFFFF, 0, 0, 0, 0, 0}, }; +static struct dib0090_wbd_slope dib7090e_wbd_table[] = { + { 380, 81, 850, 64, 540, 4}, + { 700, 51, 866, 21, 320, 4}, + { 860, 48, 666, 18, 330, 6}, + {1700, 0, 250, 0, 100, 6}, + {2600, 0, 250, 0, 100, 6}, + { 0xFFFF, 0, 0, 0, 0, 0}, +}; + static struct dibx000_agc_config dib7090_agc_config[2] = { { .band_caps = BAND_UHF, @@ -2541,7 +2428,7 @@ static struct dibx000_agc_config dib7090_agc_config[2] = { .wbd_alpha = 5, .agc1_max = 65535, - .agc1_min = 32768, + .agc1_min = 0, .agc2_max = 65535, .agc2_min = 0, @@ -2618,7 +2505,7 @@ static struct dib7000p_config nim7090_dib7000p_config = { .output_mpeg2_in_188_bytes = 1, .hostbus_diversity = 1, .tuner_is_baseband = 1, - .update_lna = tfe7790p_update_lna, /* GPIO used is the same as TFE7790 */ + .update_lna = NULL, .agc_config_count = 2, .agc = dib7090_agc_config, @@ -2642,26 +2529,12 @@ static struct dib7000p_config nim7090_dib7000p_config = { .enMpegOutput = 1, }; -static int tfe7090p_pvr_update_lna(struct dvb_frontend *fe, u16 agc_global) -{ - deb_info("TFE7090P-PVR update LNA: agc global=%i", agc_global); - if (agc_global < 25000) { - dib7000p_set_gpio(fe, 5, 0, 0); - dib7000p_set_agc1_min(fe, 0); - } else { - dib7000p_set_gpio(fe, 5, 0, 1); - dib7000p_set_agc1_min(fe, 32768); - } - - return 0; -} - static struct dib7000p_config tfe7090pvr_dib7000p_config[2] = { { .output_mpeg2_in_188_bytes = 1, .hostbus_diversity = 1, .tuner_is_baseband = 1, - .update_lna = tfe7090p_pvr_update_lna, + .update_lna = NULL, .agc_config_count = 2, .agc = dib7090_agc_config, @@ -2688,7 +2561,7 @@ static struct dib7000p_config tfe7090pvr_dib7000p_config[2] = { .output_mpeg2_in_188_bytes = 1, .hostbus_diversity = 1, .tuner_is_baseband = 1, - .update_lna = tfe7090p_pvr_update_lna, + .update_lna = NULL, .agc_config_count = 2, .agc = dib7090_agc_config, @@ -2714,6 +2587,34 @@ static struct dib7000p_config tfe7090pvr_dib7000p_config[2] = { } }; +static struct dib7000p_config tfe7090e_dib7000p_config = { + .output_mpeg2_in_188_bytes = 1, + .hostbus_diversity = 1, + .tuner_is_baseband = 1, + .update_lna = dib7090e_update_lna, + + .agc_config_count = 2, + .agc = dib7090_agc_config, + + .bw = &dib7090_clock_config_12_mhz, + + .gpio_dir = DIB7000P_GPIO_DEFAULT_DIRECTIONS, + .gpio_val = DIB7000P_GPIO_DEFAULT_VALUES, + .gpio_pwm_pos = DIB7000P_GPIO_DEFAULT_PWM_POS, + + .pwm_freq_div = 0, + + .agc_control = dib7090_agc_restart, + + .spur_protect = 0, + .disable_sample_and_hold = 0, + .enable_current_mirror = 0, + .diversity_delay = 0, + + .output_mode = OUTMODE_MPEG2_FIFO, + .enMpegOutput = 1, +}; + static const struct dib0090_config nim7090_dib0090_config = { .io.clock_khz = 12000, .io.pll_bypass = 0, @@ -2748,11 +2649,47 @@ static const struct dib0090_config nim7090_dib0090_config = { .in_soc = 1, }; -static struct dib7000p_config tfe7790p_dib7000p_config = { +static const struct dib0090_config tfe7090e_dib0090_config = { + .io.clock_khz = 12000, + .io.pll_bypass = 0, + .io.pll_range = 0, + .io.pll_prediv = 3, + .io.pll_loopdiv = 6, + .io.adc_clock_ratio = 0, + .io.pll_int_loop_filt = 0, + .reset = dib7090_tuner_sleep, + .sleep = dib7090_tuner_sleep, + + .freq_offset_khz_uhf = 0, + .freq_offset_khz_vhf = 0, + + .get_adc_power = dib7090_get_adc_power, + + .clkouttobamse = 1, + .analog_output = 0, + + .wbd_vhf_offset = 0, + .wbd_cband_offset = 0, + .use_pwm_agc = 1, + .clkoutdrive = 0, + + .fref_clock_ratio = 0, + + .wbd = dib7090e_wbd_table, + + .ls_cfg_pad_drv = 0, + .data_tx_drv = 0, + .low_if = NULL, + .in_soc = 1, + .force_cband_input = 1, + .is_dib7090e = 1, +}; + +static struct dib7000p_config tfe7790e_dib7000p_config = { .output_mpeg2_in_188_bytes = 1, .hostbus_diversity = 1, .tuner_is_baseband = 1, - .update_lna = tfe7790p_update_lna, + .update_lna = dib7090e_update_lna, .agc_config_count = 2, .agc = dib7090_agc_config, @@ -2776,7 +2713,7 @@ static struct dib7000p_config tfe7790p_dib7000p_config = { .enMpegOutput = 1, }; -static const struct dib0090_config tfe7790p_dib0090_config = { +static const struct dib0090_config tfe7790e_dib0090_config = { .io.clock_khz = 12000, .io.pll_bypass = 0, .io.pll_range = 0, @@ -2802,14 +2739,14 @@ static const struct dib0090_config tfe7790p_dib0090_config = { .fref_clock_ratio = 0, - .wbd = dib7090_wbd_table, + .wbd = dib7090e_wbd_table, .ls_cfg_pad_drv = 0, .data_tx_drv = 0, .low_if = NULL, .in_soc = 1, - .force_cband_input = 0, - .is_dib7090e = 0, + .force_cband_input = 1, + .is_dib7090e = 1, .force_crystal_mode = 1, }; @@ -3005,11 +2942,37 @@ static int tfe7090pvr_tuner1_attach(struct dvb_usb_adapter *adap) return 0; } -static int tfe7790p_frontend_attach(struct dvb_usb_adapter *adap) +static int tfe7090e_frontend_attach(struct dvb_usb_adapter *adap) +{ + dib0700_set_gpio(adap->dev, GPIO6, GPIO_OUT, 1); + msleep(20); + dib0700_set_gpio(adap->dev, GPIO9, GPIO_OUT, 1); + dib0700_set_gpio(adap->dev, GPIO4, GPIO_OUT, 1); + dib0700_set_gpio(adap->dev, GPIO7, GPIO_OUT, 1); + dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 0); + + msleep(20); + dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 1); + msleep(20); + dib0700_set_gpio(adap->dev, GPIO0, GPIO_OUT, 1); + + if (dib7000p_i2c_enumeration(&adap->dev->i2c_adap, + 1, 0x10, &tfe7090e_dib7000p_config) != 0) { + err("%s: dib7000p_i2c_enumeration failed. Cannot continue\n", + __func__); + return -ENODEV; + } + adap->fe_adap[0].fe = dvb_attach(dib7000p_attach, &adap->dev->i2c_adap, + 0x80, &tfe7090e_dib7000p_config); + + return adap->fe_adap[0].fe == NULL ? -ENODEV : 0; +} + +static int tfe7790e_frontend_attach(struct dvb_usb_adapter *adap) { struct dib0700_state *st = adap->dev->priv; - /* The TFE7790P requires the dib0700 to not be in master mode */ + /* The TFE7790E requires the dib0700 to not be in master mode */ st->disable_streaming_master_mode = 1; dib0700_set_gpio(adap->dev, GPIO6, GPIO_OUT, 1); @@ -3025,25 +2988,42 @@ static int tfe7790p_frontend_attach(struct dvb_usb_adapter *adap) dib0700_set_gpio(adap->dev, GPIO0, GPIO_OUT, 1); if (dib7000p_i2c_enumeration(&adap->dev->i2c_adap, - 1, 0x10, &tfe7790p_dib7000p_config) != 0) { + 1, 0x10, &tfe7790e_dib7000p_config) != 0) { err("%s: dib7000p_i2c_enumeration failed. Cannot continue\n", __func__); return -ENODEV; } adap->fe_adap[0].fe = dvb_attach(dib7000p_attach, &adap->dev->i2c_adap, - 0x80, &tfe7790p_dib7000p_config); + 0x80, &tfe7790e_dib7000p_config); return adap->fe_adap[0].fe == NULL ? -ENODEV : 0; } -static int tfe7790p_tuner_attach(struct dvb_usb_adapter *adap) +static int tfe7790e_tuner_attach(struct dvb_usb_adapter *adap) +{ + struct dib0700_adapter_state *st = adap->priv; + struct i2c_adapter *tun_i2c = + dib7090_get_i2c_tuner(adap->fe_adap[0].fe); + + if (dvb_attach(dib0090_register, adap->fe_adap[0].fe, tun_i2c, + &tfe7790e_dib0090_config) == NULL) + return -ENODEV; + + dib7000p_set_gpio(adap->fe_adap[0].fe, 8, 0, 1); + + st->set_param_save = adap->fe_adap[0].fe->ops.tuner_ops.set_params; + adap->fe_adap[0].fe->ops.tuner_ops.set_params = dib7090_agc_startup; + return 0; +} + +static int tfe7090e_tuner_attach(struct dvb_usb_adapter *adap) { struct dib0700_adapter_state *st = adap->priv; struct i2c_adapter *tun_i2c = dib7090_get_i2c_tuner(adap->fe_adap[0].fe); if (dvb_attach(dib0090_register, adap->fe_adap[0].fe, tun_i2c, - &tfe7790p_dib0090_config) == NULL) + &tfe7090e_dib0090_config) == NULL) return -ENODEV; dib7000p_set_gpio(adap->fe_adap[0].fe, 8, 0, 1); @@ -3586,9 +3566,10 @@ struct usb_device_id dib0700_usb_id_table[] = { /* 75 */{ USB_DEVICE(USB_VID_MEDION, USB_PID_CREATIX_CTX1921) }, { USB_DEVICE(USB_VID_PINNACLE, USB_PID_PINNACLE_PCTV340E) }, { USB_DEVICE(USB_VID_PINNACLE, USB_PID_PINNACLE_PCTV340E_SE) }, - { USB_DEVICE(USB_VID_DIBCOM, USB_PID_DIBCOM_TFE7790P) }, - { USB_DEVICE(USB_VID_DIBCOM, USB_PID_DIBCOM_TFE8096P) }, -/* 80 */{ USB_DEVICE(USB_VID_ELGATO, USB_PID_ELGATO_EYETV_DTT_2) }, + { USB_DEVICE(USB_VID_DIBCOM, USB_PID_DIBCOM_TFE7090E) }, + { USB_DEVICE(USB_VID_DIBCOM, USB_PID_DIBCOM_TFE7790E) }, +/* 80 */{ USB_DEVICE(USB_VID_DIBCOM, USB_PID_DIBCOM_TFE8096P) }, + { USB_DEVICE(USB_VID_ELGATO, USB_PID_ELGATO_EYETV_DTT_2) }, { 0 } /* Terminating entry */ }; MODULE_DEVICE_TABLE(usb, dib0700_usb_id_table); @@ -3899,7 +3880,7 @@ struct dvb_usb_device_properties dib0700_devices[] = { { NULL }, }, { "Elgato EyeTV DTT rev. 2", - { &dib0700_usb_id_table[80], NULL }, + { &dib0700_usb_id_table[81], NULL }, { NULL }, }, }, @@ -4716,10 +4697,10 @@ struct dvb_usb_device_properties dib0700_devices[] = { .pid_filter_count = 32, .pid_filter = stk70x0p_pid_filter, .pid_filter_ctrl = stk70x0p_pid_filter_ctrl, - .frontend_attach = tfe7790p_frontend_attach, - .tuner_attach = tfe7790p_tuner_attach, + .frontend_attach = tfe7090e_frontend_attach, + .tuner_attach = tfe7090e_tuner_attach, - DIB0700_DEFAULT_STREAMING_CONFIG(0x03), + DIB0700_DEFAULT_STREAMING_CONFIG(0x02), } }, .size_of_priv = @@ -4729,12 +4710,52 @@ struct dvb_usb_device_properties dib0700_devices[] = { .num_device_descs = 1, .devices = { - { "DiBcom TFE7790P reference design", + { "DiBcom TFE7090E reference design", { &dib0700_usb_id_table[78], NULL }, { NULL }, }, }, + .rc.core = { + .rc_interval = DEFAULT_RC_INTERVAL, + .rc_codes = RC_MAP_DIB0700_RC5_TABLE, + .module_name = "dib0700", + .rc_query = dib0700_rc_query_old_firmware, + .allowed_protos = RC_BIT_RC5 | + RC_BIT_RC6_MCE | + RC_BIT_NEC, + .change_protocol = dib0700_change_protocol, + }, + }, { DIB0700_DEFAULT_DEVICE_PROPERTIES, + .num_adapters = 1, + .adapter = { + { + .num_frontends = 1, + .fe = {{ + .caps = DVB_USB_ADAP_HAS_PID_FILTER | + DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, + .pid_filter_count = 32, + .pid_filter = stk70x0p_pid_filter, + .pid_filter_ctrl = stk70x0p_pid_filter_ctrl, + .frontend_attach = tfe7790e_frontend_attach, + .tuner_attach = tfe7790e_tuner_attach, + + DIB0700_DEFAULT_STREAMING_CONFIG(0x03), + } }, + + .size_of_priv = + sizeof(struct dib0700_adapter_state), + }, + }, + + .num_device_descs = 1, + .devices = { + { "DiBcom TFE7790E reference design", + { &dib0700_usb_id_table[79], NULL }, + { NULL }, + }, + }, + .rc.core = { .rc_interval = DEFAULT_RC_INTERVAL, .rc_codes = RC_MAP_DIB0700_RC5_TABLE, @@ -4771,7 +4792,7 @@ struct dvb_usb_device_properties dib0700_devices[] = { .num_device_descs = 1, .devices = { { "DiBcom TFE8096P reference design", - { &dib0700_usb_id_table[79], NULL }, + { &dib0700_usb_id_table[80], NULL }, { NULL }, }, }, diff --git a/trunk/drivers/media/usb/em28xx/em28xx-cards.c b/trunk/drivers/media/usb/em28xx/em28xx-cards.c index 83bfbe4c980f..cc63f1963de5 100644 --- a/trunk/drivers/media/usb/em28xx/em28xx-cards.c +++ b/trunk/drivers/media/usb/em28xx/em28xx-cards.c @@ -2910,8 +2910,7 @@ static int em28xx_init_dev(struct em28xx *dev, struct usb_device *udev, break; case CHIP_ID_EM2820: chip_name = "em2710/2820"; - if (le16_to_cpu(dev->udev->descriptor.idVendor) - == 0xeb1a) { + if (dev->udev->descriptor.idVendor == 0xeb1a) { __le16 idProd = dev->udev->descriptor.idProduct; if (le16_to_cpu(idProd) == 0x2710) chip_name = "em2710"; @@ -3220,9 +3219,9 @@ static int em28xx_usb_probe(struct usb_interface *interface, e->bEndpointAddress; } else { if (usb_endpoint_xfer_isoc(e)) { + dev->dvb_ep_isoc = e->bEndpointAddress; if (size > dev->dvb_max_pkt_size_isoc) { has_dvb = true; /* see NOTE (~) */ - dev->dvb_ep_isoc = e->bEndpointAddress; dev->dvb_max_pkt_size_isoc = size; dev->dvb_alt_isoc = i; } diff --git a/trunk/drivers/media/v4l2-core/videobuf-dma-contig.c b/trunk/drivers/media/v4l2-core/videobuf-dma-contig.c index 67f572c3fba2..3a43ba0959bf 100644 --- a/trunk/drivers/media/v4l2-core/videobuf-dma-contig.c +++ b/trunk/drivers/media/v4l2-core/videobuf-dma-contig.c @@ -27,6 +27,7 @@ struct videobuf_dma_contig_memory { u32 magic; void *vaddr; dma_addr_t dma_handle; + bool cached; unsigned long size; }; @@ -42,8 +43,26 @@ static int __videobuf_dc_alloc(struct device *dev, unsigned long size, gfp_t flags) { mem->size = size; - mem->vaddr = dma_alloc_coherent(dev, mem->size, - &mem->dma_handle, flags); + if (mem->cached) { + mem->vaddr = alloc_pages_exact(mem->size, flags | GFP_DMA); + if (mem->vaddr) { + int err; + + mem->dma_handle = dma_map_single(dev, mem->vaddr, + mem->size, + DMA_FROM_DEVICE); + err = dma_mapping_error(dev, mem->dma_handle); + if (err) { + dev_err(dev, "dma_map_single failed\n"); + + free_pages_exact(mem->vaddr, mem->size); + mem->vaddr = NULL; + return err; + } + } + } else + mem->vaddr = dma_alloc_coherent(dev, mem->size, + &mem->dma_handle, flags); if (!mem->vaddr) { dev_err(dev, "memory alloc size %ld failed\n", mem->size); @@ -58,7 +77,14 @@ static int __videobuf_dc_alloc(struct device *dev, static void __videobuf_dc_free(struct device *dev, struct videobuf_dma_contig_memory *mem) { - dma_free_coherent(dev, mem->size, mem->vaddr, mem->dma_handle); + if (mem->cached) { + if (!mem->vaddr) + return; + dma_unmap_single(dev, mem->dma_handle, mem->size, + DMA_FROM_DEVICE); + free_pages_exact(mem->vaddr, mem->size); + } else + dma_free_coherent(dev, mem->size, mem->vaddr, mem->dma_handle); mem->vaddr = NULL; } @@ -208,7 +234,7 @@ static int videobuf_dma_contig_user_get(struct videobuf_dma_contig_memory *mem, return ret; } -static struct videobuf_buffer *__videobuf_alloc(size_t size) +static struct videobuf_buffer *__videobuf_alloc_vb(size_t size, bool cached) { struct videobuf_dma_contig_memory *mem; struct videobuf_buffer *vb; @@ -218,11 +244,22 @@ static struct videobuf_buffer *__videobuf_alloc(size_t size) vb->priv = ((char *)vb) + size; mem = vb->priv; mem->magic = MAGIC_DC_MEM; + mem->cached = cached; } return vb; } +static struct videobuf_buffer *__videobuf_alloc_uncached(size_t size) +{ + return __videobuf_alloc_vb(size, false); +} + +static struct videobuf_buffer *__videobuf_alloc_cached(size_t size) +{ + return __videobuf_alloc_vb(size, true); +} + static void *__videobuf_to_vaddr(struct videobuf_buffer *buf) { struct videobuf_dma_contig_memory *mem = buf->priv; @@ -273,6 +310,19 @@ static int __videobuf_iolock(struct videobuf_queue *q, return 0; } +static int __videobuf_sync(struct videobuf_queue *q, + struct videobuf_buffer *buf) +{ + struct videobuf_dma_contig_memory *mem = buf->priv; + BUG_ON(!mem); + MAGIC_CHECK(mem->magic, MAGIC_DC_MEM); + + dma_sync_single_for_cpu(q->dev, mem->dma_handle, mem->size, + DMA_FROM_DEVICE); + + return 0; +} + static int __videobuf_mmap_mapper(struct videobuf_queue *q, struct videobuf_buffer *buf, struct vm_area_struct *vma) @@ -281,6 +331,8 @@ static int __videobuf_mmap_mapper(struct videobuf_queue *q, struct videobuf_mapping *map; int retval; unsigned long size; + unsigned long pos, start = vma->vm_start; + struct page *page; dev_dbg(q->dev, "%s\n", __func__); @@ -307,16 +359,43 @@ static int __videobuf_mmap_mapper(struct videobuf_queue *q, size = vma->vm_end - vma->vm_start; size = (size < mem->size) ? size : mem->size; - vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); - retval = remap_pfn_range(vma, vma->vm_start, - mem->dma_handle >> PAGE_SHIFT, + if (!mem->cached) { + vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); + retval = remap_pfn_range(vma, vma->vm_start, + mem->dma_handle >> PAGE_SHIFT, size, vma->vm_page_prot); - if (retval) { - dev_err(q->dev, "mmap: remap failed with error %d. ", - retval); - dma_free_coherent(q->dev, mem->size, - mem->vaddr, mem->dma_handle); - goto error; + if (retval) { + dev_err(q->dev, "mmap: remap failed with error %d. ", + retval); + dma_free_coherent(q->dev, mem->size, + mem->vaddr, mem->dma_handle); + goto error; + } + } else { + pos = (unsigned long)mem->vaddr; + + while (size > 0) { + page = virt_to_page((void *)pos); + if (NULL == page) { + dev_err(q->dev, "mmap: virt_to_page failed\n"); + __videobuf_dc_free(q->dev, mem); + goto error; + } + retval = vm_insert_page(vma, start, page); + if (retval) { + dev_err(q->dev, "mmap: insert failed with error %d\n", + retval); + __videobuf_dc_free(q->dev, mem); + goto error; + } + start += PAGE_SIZE; + pos += PAGE_SIZE; + + if (size > PAGE_SIZE) + size -= PAGE_SIZE; + else + size = 0; + } } vma->vm_ops = &videobuf_vm_ops; @@ -338,8 +417,17 @@ static int __videobuf_mmap_mapper(struct videobuf_queue *q, static struct videobuf_qtype_ops qops = { .magic = MAGIC_QTYPE_OPS, - .alloc_vb = __videobuf_alloc, + .alloc_vb = __videobuf_alloc_uncached, + .iolock = __videobuf_iolock, + .mmap_mapper = __videobuf_mmap_mapper, + .vaddr = __videobuf_to_vaddr, +}; + +static struct videobuf_qtype_ops qops_cached = { + .magic = MAGIC_QTYPE_OPS, + .alloc_vb = __videobuf_alloc_cached, .iolock = __videobuf_iolock, + .sync = __videobuf_sync, .mmap_mapper = __videobuf_mmap_mapper, .vaddr = __videobuf_to_vaddr, }; @@ -359,6 +447,20 @@ void videobuf_queue_dma_contig_init(struct videobuf_queue *q, } EXPORT_SYMBOL_GPL(videobuf_queue_dma_contig_init); +void videobuf_queue_dma_contig_init_cached(struct videobuf_queue *q, + const struct videobuf_queue_ops *ops, + struct device *dev, + spinlock_t *irqlock, + enum v4l2_buf_type type, + enum v4l2_field field, + unsigned int msize, + void *priv, struct mutex *ext_lock) +{ + videobuf_queue_core_init(q, ops, dev, irqlock, type, field, msize, + priv, &qops_cached, ext_lock); +} +EXPORT_SYMBOL_GPL(videobuf_queue_dma_contig_init_cached); + dma_addr_t videobuf_to_dma_contig(struct videobuf_buffer *buf) { struct videobuf_dma_contig_memory *mem = buf->priv; diff --git a/trunk/drivers/media/v4l2-core/videobuf2-core.c b/trunk/drivers/media/v4l2-core/videobuf2-core.c index 7d833eefaf4e..58c17444d22f 100644 --- a/trunk/drivers/media/v4l2-core/videobuf2-core.c +++ b/trunk/drivers/media/v4l2-core/videobuf2-core.c @@ -54,15 +54,10 @@ static int __vb2_buf_mem_alloc(struct vb2_buffer *vb) void *mem_priv; int plane; - /* - * Allocate memory for all planes in this buffer - * NOTE: mmapped areas should be page aligned - */ + /* Allocate memory for all planes in this buffer */ for (plane = 0; plane < vb->num_planes; ++plane) { - unsigned long size = PAGE_ALIGN(q->plane_sizes[plane]); - mem_priv = call_memop(q, alloc, q->alloc_ctx[plane], - size, q->gfp_flags); + q->plane_sizes[plane], q->gfp_flags); if (IS_ERR_OR_NULL(mem_priv)) goto free; @@ -1857,7 +1852,6 @@ int vb2_mmap(struct vb2_queue *q, struct vm_area_struct *vma) struct vb2_buffer *vb; unsigned int buffer, plane; int ret; - unsigned long length; if (q->memory != V4L2_MEMORY_MMAP) { dprintk(1, "Queue is not currently set up for mmap\n"); @@ -1892,15 +1886,8 @@ int vb2_mmap(struct vb2_queue *q, struct vm_area_struct *vma) vb = q->bufs[buffer]; - /* - * MMAP requires page_aligned buffers. - * The buffer length was page_aligned at __vb2_buf_mem_alloc(), - * so, we need to do the same here. - */ - length = PAGE_ALIGN(vb->v4l2_planes[plane].length); - if (length < (vma->vm_end - vma->vm_start)) { - dprintk(1, - "MMAP invalid, as it would overflow buffer length\n"); + if (vb->v4l2_planes[plane].length < (vma->vm_end - vma->vm_start)) { + dprintk(1, "Invalid length\n"); return -EINVAL; } diff --git a/trunk/drivers/media/v4l2-core/videobuf2-dma-contig.c b/trunk/drivers/media/v4l2-core/videobuf2-dma-contig.c index fd56f2563201..ae35d255a430 100644 --- a/trunk/drivers/media/v4l2-core/videobuf2-dma-contig.c +++ b/trunk/drivers/media/v4l2-core/videobuf2-dma-contig.c @@ -162,6 +162,9 @@ static void *vb2_dc_alloc(void *alloc_ctx, unsigned long size, gfp_t gfp_flags) if (!buf) return ERR_PTR(-ENOMEM); + /* align image size to PAGE_SIZE */ + size = PAGE_ALIGN(size); + buf->vaddr = dma_alloc_coherent(dev, size, &buf->dma_addr, GFP_KERNEL | gfp_flags); if (!buf->vaddr) { diff --git a/trunk/drivers/media/v4l2-core/videobuf2-dma-sg.c b/trunk/drivers/media/v4l2-core/videobuf2-dma-sg.c index 16ae3dcc7e29..59522b2ebae1 100644 --- a/trunk/drivers/media/v4l2-core/videobuf2-dma-sg.c +++ b/trunk/drivers/media/v4l2-core/videobuf2-dma-sg.c @@ -55,8 +55,7 @@ static void *vb2_dma_sg_alloc(void *alloc_ctx, unsigned long size, gfp_t gfp_fla buf->write = 0; buf->offset = 0; buf->sg_desc.size = size; - /* size is already page aligned */ - buf->sg_desc.num_pages = size >> PAGE_SHIFT; + buf->sg_desc.num_pages = (size + PAGE_SIZE - 1) >> PAGE_SHIFT; buf->sg_desc.sglist = vzalloc(buf->sg_desc.num_pages * sizeof(*buf->sg_desc.sglist)); diff --git a/trunk/drivers/mfd/Kconfig b/trunk/drivers/mfd/Kconfig index c346941a2515..b6bb6d5f89e7 100644 --- a/trunk/drivers/mfd/Kconfig +++ b/trunk/drivers/mfd/Kconfig @@ -977,6 +977,19 @@ config MFD_WL1273_CORE driver connects the radio-wl1273 V4L2 module and the wl1273 audio codec. +config MFD_SI476X_CORE + tristate "Support for Silicon Laboratories 4761/64/68 AM/FM radio." + depends on I2C + select MFD_CORE + select REGMAP_I2C + help + This is the core driver for the SI476x series of AM/FM + radio. This MFD driver connects the radio-si476x V4L2 module + and the si476x audio codec. + + To compile this driver as a module, choose M here: the + module will be called si476x-core. + config MFD_OMAP_USB_HOST bool "Support OMAP USBHS core and TLL driver" depends on USB_EHCI_HCD_OMAP || USB_OHCI_HCD_OMAP3 diff --git a/trunk/drivers/mfd/Makefile b/trunk/drivers/mfd/Makefile index b90409c23664..b7aaa1e104a1 100644 --- a/trunk/drivers/mfd/Makefile +++ b/trunk/drivers/mfd/Makefile @@ -131,6 +131,10 @@ obj-$(CONFIG_MFD_JZ4740_ADC) += jz4740-adc.o obj-$(CONFIG_MFD_TPS6586X) += tps6586x.o obj-$(CONFIG_MFD_VX855) += vx855.o obj-$(CONFIG_MFD_WL1273_CORE) += wl1273-core.o + +si476x-core-y := si476x-cmd.o si476x-prop.o si476x-i2c.o +obj-$(CONFIG_MFD_SI476X_CORE) += si476x-core.o + obj-$(CONFIG_MFD_CS5535) += cs5535-mfd.o obj-$(CONFIG_MFD_OMAP_USB_HOST) += omap-usb-host.o omap-usb-tll.o obj-$(CONFIG_MFD_PM8921_CORE) += pm8921-core.o diff --git a/trunk/drivers/of/base.c b/trunk/drivers/of/base.c index 1733081eb873..321d3ef05006 100644 --- a/trunk/drivers/of/base.c +++ b/trunk/drivers/of/base.c @@ -382,7 +382,6 @@ struct device_node *of_get_next_parent(struct device_node *node) raw_spin_unlock_irqrestore(&devtree_lock, flags); return parent; } -EXPORT_SYMBOL(of_get_next_parent); /** * of_get_next_child - Iterate a node childs diff --git a/trunk/drivers/staging/media/go7007/go7007-priv.h b/trunk/drivers/staging/media/go7007/go7007-priv.h index 6e16af720504..8bde187e4f90 100644 --- a/trunk/drivers/staging/media/go7007/go7007-priv.h +++ b/trunk/drivers/staging/media/go7007/go7007-priv.h @@ -91,8 +91,8 @@ struct go7007_board_info { int num_i2c_devs; struct go_i2c { const char *type; - unsigned int is_video:1; - unsigned int is_audio:1; + int is_video:1; + int is_audio:1; int addr; u32 flags; } i2c_devs[5]; diff --git a/trunk/drivers/staging/media/solo6x10/solo6x10-v4l2-enc.c b/trunk/drivers/staging/media/solo6x10/solo6x10-v4l2-enc.c index 98e2902afd74..6c7d20f8f67c 100644 --- a/trunk/drivers/staging/media/solo6x10/solo6x10-v4l2-enc.c +++ b/trunk/drivers/staging/media/solo6x10/solo6x10-v4l2-enc.c @@ -519,15 +519,10 @@ static int solo_enc_fillbuf(struct solo_enc_dev *solo_enc, vb->v4l2_buf.flags |= V4L2_BUF_FLAG_MOTION_DETECTED; } - switch (solo_enc->fmt) { - case V4L2_PIX_FMT_MPEG4: - case V4L2_PIX_FMT_H264: + if (solo_enc->fmt == V4L2_PIX_FMT_MPEG4) ret = solo_fill_mpeg(solo_enc, vb, vh); - break; - default: /* V4L2_PIX_FMT_MJPEG */ + else ret = solo_fill_jpeg(solo_enc, vb, vh); - break; - } if (!ret) { vb->v4l2_buf.sequence = solo_enc->sequence++; @@ -785,21 +780,10 @@ static int solo_enc_get_input(struct file *file, void *priv, static int solo_enc_enum_fmt_cap(struct file *file, void *priv, struct v4l2_fmtdesc *f) { - struct solo_enc_dev *solo_enc = video_drvdata(file); - int dev_type = solo_enc->solo_dev->type; - switch (f->index) { case 0: - switch (dev_type) { - case SOLO_DEV_6010: - f->pixelformat = V4L2_PIX_FMT_MPEG4; - strcpy(f->description, "MPEG-4 part 2"); - break; - case SOLO_DEV_6110: - f->pixelformat = V4L2_PIX_FMT_H264; - strcpy(f->description, "H.264"); - break; - } + f->pixelformat = V4L2_PIX_FMT_MPEG4; + strcpy(f->description, "MPEG-4 AVC"); break; case 1: f->pixelformat = V4L2_PIX_FMT_MJPEG; @@ -814,13 +798,6 @@ static int solo_enc_enum_fmt_cap(struct file *file, void *priv, return 0; } -static inline int solo_valid_pixfmt(u32 pixfmt, int dev_type) -{ - return (pixfmt == V4L2_PIX_FMT_H264 && dev_type == SOLO_DEV_6110) - || (pixfmt == V4L2_PIX_FMT_MPEG4 && dev_type == SOLO_DEV_6010) - || pixfmt == V4L2_PIX_FMT_MJPEG ? 0 : -EINVAL; -} - static int solo_enc_try_fmt_cap(struct file *file, void *priv, struct v4l2_format *f) { @@ -828,7 +805,8 @@ static int solo_enc_try_fmt_cap(struct file *file, void *priv, struct solo_dev *solo_dev = solo_enc->solo_dev; struct v4l2_pix_format *pix = &f->fmt.pix; - if (solo_valid_pixfmt(pix->pixelformat, solo_dev->type)) + if (pix->pixelformat != V4L2_PIX_FMT_MPEG4 && + pix->pixelformat != V4L2_PIX_FMT_MJPEG) return -EINVAL; if (pix->width < solo_dev->video_hsize || @@ -894,7 +872,6 @@ static int solo_enc_set_fmt_cap(struct file *file, void *priv, if (pix->priv) solo_enc->type = SOLO_ENC_TYPE_EXT; */ - solo_update_mode(solo_enc); return 0; } @@ -941,7 +918,8 @@ static int solo_enum_framesizes(struct file *file, void *priv, struct solo_enc_dev *solo_enc = video_drvdata(file); struct solo_dev *solo_dev = solo_enc->solo_dev; - if (solo_valid_pixfmt(fsize->pixel_format, solo_dev->type)) + if (fsize->pixel_format != V4L2_PIX_FMT_MPEG4 && + fsize->pixel_format != V4L2_PIX_FMT_MJPEG) return -EINVAL; switch (fsize->index) { @@ -968,7 +946,8 @@ static int solo_enum_frameintervals(struct file *file, void *priv, struct solo_enc_dev *solo_enc = video_drvdata(file); struct solo_dev *solo_dev = solo_enc->solo_dev; - if (solo_valid_pixfmt(fintv->pixel_format, solo_dev->type)) + if (fintv->pixel_format != V4L2_PIX_FMT_MPEG4 && + fintv->pixel_format != V4L2_PIX_FMT_MJPEG) return -EINVAL; if (fintv->index) return -EINVAL; @@ -1245,8 +1224,7 @@ static struct solo_enc_dev *solo_enc_alloc(struct solo_dev *solo_dev, mutex_init(&solo_enc->lock); spin_lock_init(&solo_enc->av_lock); INIT_LIST_HEAD(&solo_enc->vidq_active); - solo_enc->fmt = (solo_dev->type == SOLO_DEV_6010) ? - V4L2_PIX_FMT_MPEG4 : V4L2_PIX_FMT_H264; + solo_enc->fmt = V4L2_PIX_FMT_MPEG4; solo_enc->type = SOLO_ENC_TYPE_STD; solo_enc->qp = SOLO_DEFAULT_QP; diff --git a/trunk/include/linux/mfd/si476x-core.h b/trunk/include/linux/mfd/si476x-core.h new file mode 100644 index 000000000000..2136b2631dc3 --- /dev/null +++ b/trunk/include/linux/mfd/si476x-core.h @@ -0,0 +1,525 @@ +/* + * include/media/si476x-core.h -- Common definitions for si476x core + * device + * + * Copyright (C) 2012 Innovative Converged Devices(ICD) + * + * Author: Andrey Smirnov + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + */ + +#ifndef SI476X_CORE_H +#define SI476X_CORE_H + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/* Command Timeouts */ +#define SI476X_DEFAULT_TIMEOUT 100000 +#define SI476X_TIMEOUT_TUNE 700000 +#define SI476X_TIMEOUT_POWER_UP 330000 +#define SI476X_STATUS_POLL_US 0 + +/* -------------------- si476x-i2c.c ----------------------- */ + +enum si476x_freq_supported_chips { + SI476X_CHIP_SI4761 = 1, + SI476X_CHIP_SI4764, + SI476X_CHIP_SI4768, +}; + +enum si476x_mfd_cells { + SI476X_RADIO_CELL = 0, + SI476X_CODEC_CELL, + SI476X_MFD_CELLS, +}; + +/** + * enum si476x_power_state - possible power state of the si476x + * device. + * + * @SI476X_POWER_DOWN: In this state all regulators are turned off + * and the reset line is pulled low. The device is completely + * inactive. + * @SI476X_POWER_UP_FULL: In this state all the power regualtors are + * turned on, reset line pulled high, IRQ line is enabled(polling is + * active for polling use scenario) and device is turned on with + * POWER_UP command. The device is ready to be used. + * @SI476X_POWER_INCONSISTENT: This state indicates that previous + * power down was inconsistent, meaning some of the regulators were + * not turned down and thus use of the device, without power-cycling + * is impossible. + */ +enum si476x_power_state { + SI476X_POWER_DOWN = 0, + SI476X_POWER_UP_FULL = 1, + SI476X_POWER_INCONSISTENT = 2, +}; + +/** + * struct si476x_core - internal data structure representing the + * underlying "core" device which all the MFD cell-devices use. + * + * @client: Actual I2C client used to transfer commands to the chip. + * @chip_id: Last digit of the chip model(E.g. "1" for SI4761) + * @cells: MFD cell devices created by this driver. + * @cmd_lock: Mutex used to serialize all the requests to the core + * device. This filed should not be used directly. Instead + * si476x_core_lock()/si476x_core_unlock() should be used to get + * exclusive access to the "core" device. + * @users: Active users counter(Used by the radio cell) + * @rds_read_queue: Wait queue used to wait for RDS data. + * @rds_fifo: FIFO in which all the RDS data received from the chip is + * placed. + * @rds_fifo_drainer: Worker that drains on-chip RDS FIFO. + * @rds_drainer_is_working: Flag used for launching only one instance + * of the @rds_fifo_drainer. + * @rds_drainer_status_lock: Lock used to guard access to the + * @rds_drainer_is_working variable. + * @command: Wait queue for wainting on the command comapletion. + * @cts: Clear To Send flag set upon receiving first status with CTS + * set. + * @tuning: Wait queue used for wainting for tune/seek comand + * completion. + * @stc: Similar to @cts, but for the STC bit of the status value. + * @power_up_parameters: Parameters used as argument for POWER_UP + * command when the device is started. + * @state: Current power state of the device. + * @supplues: Structure containing handles to all power supplies used + * by the device (NULL ones are ignored). + * @gpio_reset: GPIO pin connectet to the RSTB pin of the chip. + * @pinmux: Chip's configurable pins configuration. + * @diversity_mode: Chips role when functioning in diversity mode. + * @status_monitor: Polling worker used in polling use case scenarion + * (when IRQ is not avalible). + * @revision: Chip's running firmware revision number(Used for correct + * command set support). + */ + +struct si476x_core { + struct i2c_client *client; + struct regmap *regmap; + int chip_id; + struct mfd_cell cells[SI476X_MFD_CELLS]; + + struct mutex cmd_lock; /* for serializing fm radio operations */ + atomic_t users; + + wait_queue_head_t rds_read_queue; + struct kfifo rds_fifo; + struct work_struct rds_fifo_drainer; + bool rds_drainer_is_working; + struct mutex rds_drainer_status_lock; + + wait_queue_head_t command; + atomic_t cts; + + wait_queue_head_t tuning; + atomic_t stc; + + struct si476x_power_up_args power_up_parameters; + + enum si476x_power_state power_state; + + struct regulator_bulk_data supplies[4]; + + int gpio_reset; + + struct si476x_pinmux pinmux; + enum si476x_phase_diversity_mode diversity_mode; + + atomic_t is_alive; + + struct delayed_work status_monitor; +#define SI476X_WORK_TO_CORE(w) container_of(to_delayed_work(w), \ + struct si476x_core, \ + status_monitor) + + int revision; + + int rds_fifo_depth; +}; + +static inline struct si476x_core *i2c_mfd_cell_to_core(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev->parent); + return i2c_get_clientdata(client); +} + + +/** + * si476x_core_lock() - lock the core device to get an exclusive access + * to it. + */ +static inline void si476x_core_lock(struct si476x_core *core) +{ + mutex_lock(&core->cmd_lock); +} + +/** + * si476x_core_unlock() - unlock the core device to relinquish an + * exclusive access to it. + */ +static inline void si476x_core_unlock(struct si476x_core *core) +{ + mutex_unlock(&core->cmd_lock); +} + +/* *_TUNE_FREQ family of commands accept frequency in multiples of + 10kHz */ +static inline u16 hz_to_si476x(struct si476x_core *core, int freq) +{ + u16 result; + + switch (core->power_up_parameters.func) { + default: + case SI476X_FUNC_FM_RECEIVER: + result = freq / 10000; + break; + case SI476X_FUNC_AM_RECEIVER: + result = freq / 1000; + break; + } + + return result; +} + +static inline int si476x_to_hz(struct si476x_core *core, u16 freq) +{ + int result; + + switch (core->power_up_parameters.func) { + default: + case SI476X_FUNC_FM_RECEIVER: + result = freq * 10000; + break; + case SI476X_FUNC_AM_RECEIVER: + result = freq * 1000; + break; + } + + return result; +} + +/* Since the V4L2_TUNER_CAP_LOW flag is supplied, V4L2 subsystem + * mesures frequency in 62.5 Hz units */ + +static inline int hz_to_v4l2(int freq) +{ + return (freq * 10) / 625; +} + +static inline int v4l2_to_hz(int freq) +{ + return (freq * 625) / 10; +} + +static inline u16 v4l2_to_si476x(struct si476x_core *core, int freq) +{ + return hz_to_si476x(core, v4l2_to_hz(freq)); +} + +static inline int si476x_to_v4l2(struct si476x_core *core, u16 freq) +{ + return hz_to_v4l2(si476x_to_hz(core, freq)); +} + + + +/** + * struct si476x_func_info - structure containing result of the + * FUNC_INFO command. + * + * @firmware.major: Firmware major number. + * @firmware.minor[...]: Firmware minor numbers. + * @patch_id: + * @func: Mode tuner is working in. + */ +struct si476x_func_info { + struct { + u8 major, minor[2]; + } firmware; + u16 patch_id; + enum si476x_func func; +}; + +/** + * struct si476x_power_down_args - structure used to pass parameters + * to POWER_DOWN command + * + * @xosc: true - Power down, but leav oscillator running. + * false - Full power down. + */ +struct si476x_power_down_args { + bool xosc; +}; + +/** + * enum si476x_tunemode - enum representing possible tune modes for + * the chip. + * @SI476X_TM_VALIDATED_NORMAL_TUNE: Unconditionally stay on the new + * channel after tune, tune status is valid. + * @SI476X_TM_INVALIDATED_FAST_TUNE: Unconditionally stay in the new + * channel after tune, tune status invalid. + * @SI476X_TM_VALIDATED_AF_TUNE: Jump back to previous channel if + * metric thresholds are not met. + * @SI476X_TM_VALIDATED_AF_CHECK: Unconditionally jump back to the + * previous channel. + */ +enum si476x_tunemode { + SI476X_TM_VALIDATED_NORMAL_TUNE = 0, + SI476X_TM_INVALIDATED_FAST_TUNE = 1, + SI476X_TM_VALIDATED_AF_TUNE = 2, + SI476X_TM_VALIDATED_AF_CHECK = 3, +}; + +/** + * enum si476x_smoothmetrics - enum containing the possible setting fo + * audio transitioning of the chip + * @SI476X_SM_INITIALIZE_AUDIO: Initialize audio state to match this + * new channel + * @SI476X_SM_TRANSITION_AUDIO: Transition audio state from previous + * channel values to the new values + */ +enum si476x_smoothmetrics { + SI476X_SM_INITIALIZE_AUDIO = 0, + SI476X_SM_TRANSITION_AUDIO = 1, +}; + +/** + * struct si476x_rds_status_report - the structure representing the + * response to 'FM_RD_STATUS' command + * @rdstpptyint: Traffic program flag(TP) and/or program type(PTY) + * code has changed. + * @rdspiint: Program indentifiaction(PI) code has changed. + * @rdssyncint: RDS synchronization has changed. + * @rdsfifoint: RDS was received and the RDS FIFO has at least + * 'FM_RDS_INTERRUPT_FIFO_COUNT' elements in it. + * @tpptyvalid: TP flag and PTY code are valid falg. + * @pivalid: PI code is valid flag. + * @rdssync: RDS is currently synchronized. + * @rdsfifolost: On or more RDS groups have been lost/discarded flag. + * @tp: Current channel's TP flag. + * @pty: Current channel's PTY code. + * @pi: Current channel's PI code. + * @rdsfifoused: Number of blocks remaining in the RDS FIFO (0 if + * empty). + */ +struct si476x_rds_status_report { + bool rdstpptyint, rdspiint, rdssyncint, rdsfifoint; + bool tpptyvalid, pivalid, rdssync, rdsfifolost; + bool tp; + + u8 pty; + u16 pi; + + u8 rdsfifoused; + u8 ble[4]; + + struct v4l2_rds_data rds[4]; +}; + +struct si476x_rsq_status_args { + bool primary; + bool rsqack; + bool attune; + bool cancel; + bool stcack; +}; + +enum si476x_injside { + SI476X_INJSIDE_AUTO = 0, + SI476X_INJSIDE_LOW = 1, + SI476X_INJSIDE_HIGH = 2, +}; + +struct si476x_tune_freq_args { + bool zifsr; + bool hd; + enum si476x_injside injside; + int freq; + enum si476x_tunemode tunemode; + enum si476x_smoothmetrics smoothmetrics; + int antcap; +}; + +int si476x_core_stop(struct si476x_core *, bool); +int si476x_core_start(struct si476x_core *, bool); +int si476x_core_set_power_state(struct si476x_core *, enum si476x_power_state); +bool si476x_core_has_am(struct si476x_core *); +bool si476x_core_has_diversity(struct si476x_core *); +bool si476x_core_is_a_secondary_tuner(struct si476x_core *); +bool si476x_core_is_a_primary_tuner(struct si476x_core *); +bool si476x_core_is_in_am_receiver_mode(struct si476x_core *core); +bool si476x_core_is_powered_up(struct si476x_core *core); + +enum si476x_i2c_type { + SI476X_I2C_SEND, + SI476X_I2C_RECV +}; + +int si476x_core_i2c_xfer(struct si476x_core *, + enum si476x_i2c_type, + char *, int); + + +/* -------------------- si476x-cmd.c ----------------------- */ + +int si476x_core_cmd_func_info(struct si476x_core *, struct si476x_func_info *); +int si476x_core_cmd_set_property(struct si476x_core *, u16, u16); +int si476x_core_cmd_get_property(struct si476x_core *, u16); +int si476x_core_cmd_dig_audio_pin_cfg(struct si476x_core *, + enum si476x_dclk_config, + enum si476x_dfs_config, + enum si476x_dout_config, + enum si476x_xout_config); +int si476x_core_cmd_zif_pin_cfg(struct si476x_core *, + enum si476x_iqclk_config, + enum si476x_iqfs_config, + enum si476x_iout_config, + enum si476x_qout_config); +int si476x_core_cmd_ic_link_gpo_ctl_pin_cfg(struct si476x_core *, + enum si476x_icin_config, + enum si476x_icip_config, + enum si476x_icon_config, + enum si476x_icop_config); +int si476x_core_cmd_ana_audio_pin_cfg(struct si476x_core *, + enum si476x_lrout_config); +int si476x_core_cmd_intb_pin_cfg(struct si476x_core *, enum si476x_intb_config, + enum si476x_a1_config); +int si476x_core_cmd_fm_seek_start(struct si476x_core *, bool, bool); +int si476x_core_cmd_am_seek_start(struct si476x_core *, bool, bool); +int si476x_core_cmd_fm_rds_status(struct si476x_core *, bool, bool, bool, + struct si476x_rds_status_report *); +int si476x_core_cmd_fm_rds_blockcount(struct si476x_core *, bool, + struct si476x_rds_blockcount_report *); +int si476x_core_cmd_fm_tune_freq(struct si476x_core *, + struct si476x_tune_freq_args *); +int si476x_core_cmd_am_tune_freq(struct si476x_core *, + struct si476x_tune_freq_args *); +int si476x_core_cmd_am_rsq_status(struct si476x_core *, + struct si476x_rsq_status_args *, + struct si476x_rsq_status_report *); +int si476x_core_cmd_fm_rsq_status(struct si476x_core *, + struct si476x_rsq_status_args *, + struct si476x_rsq_status_report *); +int si476x_core_cmd_power_up(struct si476x_core *, + struct si476x_power_up_args *); +int si476x_core_cmd_power_down(struct si476x_core *, + struct si476x_power_down_args *); +int si476x_core_cmd_fm_phase_div_status(struct si476x_core *); +int si476x_core_cmd_fm_phase_diversity(struct si476x_core *, + enum si476x_phase_diversity_mode); + +int si476x_core_cmd_fm_acf_status(struct si476x_core *, + struct si476x_acf_status_report *); +int si476x_core_cmd_am_acf_status(struct si476x_core *, + struct si476x_acf_status_report *); +int si476x_core_cmd_agc_status(struct si476x_core *, + struct si476x_agc_status_report *); + +enum si476x_power_grid_type { + SI476X_POWER_GRID_50HZ = 0, + SI476X_POWER_GRID_60HZ, +}; + +/* Properties */ + +enum si476x_interrupt_flags { + SI476X_STCIEN = (1 << 0), + SI476X_ACFIEN = (1 << 1), + SI476X_RDSIEN = (1 << 2), + SI476X_RSQIEN = (1 << 3), + + SI476X_ERRIEN = (1 << 6), + SI476X_CTSIEN = (1 << 7), + + SI476X_STCREP = (1 << 8), + SI476X_ACFREP = (1 << 9), + SI476X_RDSREP = (1 << 10), + SI476X_RSQREP = (1 << 11), +}; + +enum si476x_rdsint_sources { + SI476X_RDSTPPTY = (1 << 4), + SI476X_RDSPI = (1 << 3), + SI476X_RDSSYNC = (1 << 1), + SI476X_RDSRECV = (1 << 0), +}; + +enum si476x_status_response_bits { + SI476X_CTS = (1 << 7), + SI476X_ERR = (1 << 6), + /* Status response for WB receiver */ + SI476X_WB_ASQ_INT = (1 << 4), + SI476X_RSQ_INT = (1 << 3), + /* Status response for FM receiver */ + SI476X_FM_RDS_INT = (1 << 2), + SI476X_ACF_INT = (1 << 1), + SI476X_STC_INT = (1 << 0), +}; + +/* -------------------- si476x-prop.c ----------------------- */ + +enum si476x_common_receiver_properties { + SI476X_PROP_INT_CTL_ENABLE = 0x0000, + SI476X_PROP_DIGITAL_IO_INPUT_SAMPLE_RATE = 0x0200, + SI476X_PROP_DIGITAL_IO_INPUT_FORMAT = 0x0201, + SI476X_PROP_DIGITAL_IO_OUTPUT_SAMPLE_RATE = 0x0202, + SI476X_PROP_DIGITAL_IO_OUTPUT_FORMAT = 0x0203, + + SI476X_PROP_SEEK_BAND_BOTTOM = 0x1100, + SI476X_PROP_SEEK_BAND_TOP = 0x1101, + SI476X_PROP_SEEK_FREQUENCY_SPACING = 0x1102, + + SI476X_PROP_VALID_MAX_TUNE_ERROR = 0x2000, + SI476X_PROP_VALID_SNR_THRESHOLD = 0x2003, + SI476X_PROP_VALID_RSSI_THRESHOLD = 0x2004, +}; + +enum si476x_am_receiver_properties { + SI476X_PROP_AUDIO_PWR_LINE_FILTER = 0x0303, +}; + +enum si476x_fm_receiver_properties { + SI476X_PROP_AUDIO_DEEMPHASIS = 0x0302, + + SI476X_PROP_FM_RDS_INTERRUPT_SOURCE = 0x4000, + SI476X_PROP_FM_RDS_INTERRUPT_FIFO_COUNT = 0x4001, + SI476X_PROP_FM_RDS_CONFIG = 0x4002, +}; + +enum si476x_prop_audio_pwr_line_filter_bits { + SI476X_PROP_PWR_HARMONICS_MASK = 0b0000000000011111, + SI476X_PROP_PWR_GRID_MASK = 0b0000000100000000, + SI476X_PROP_PWR_ENABLE_MASK = 0b0000001000000000, + SI476X_PROP_PWR_GRID_50HZ = 0b0000000000000000, + SI476X_PROP_PWR_GRID_60HZ = 0b0000000100000000, +}; + +enum si476x_prop_fm_rds_config_bits { + SI476X_PROP_RDSEN_MASK = 0x1, + SI476X_PROP_RDSEN = 0x1, +}; + + +struct regmap *devm_regmap_init_si476x(struct si476x_core *); + +#endif /* SI476X_CORE_H */ diff --git a/trunk/include/media/si476x.h b/trunk/include/media/si476x.h new file mode 100644 index 000000000000..beb6433d6958 --- /dev/null +++ b/trunk/include/media/si476x.h @@ -0,0 +1,426 @@ +/* + * include/media/si476x.h -- Common definitions for si476x driver + * + * Copyright (C) 2012 Innovative Converged Devices(ICD) + * Copyright (C) 2013 Andrey Smirnov + * + * Author: Andrey Smirnov + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + */ + +#ifndef SI476X_H +#define SI476X_H + +#include +#include + +struct si476x_device; + +/* It is possible to select one of the four adresses using pins A0 + * and A1 on SI476x */ +#define SI476X_I2C_ADDR_1 0x60 +#define SI476X_I2C_ADDR_2 0x61 +#define SI476X_I2C_ADDR_3 0x62 +#define SI476X_I2C_ADDR_4 0x63 + +enum si476x_iqclk_config { + SI476X_IQCLK_NOOP = 0, + SI476X_IQCLK_TRISTATE = 1, + SI476X_IQCLK_IQ = 21, +}; +enum si476x_iqfs_config { + SI476X_IQFS_NOOP = 0, + SI476X_IQFS_TRISTATE = 1, + SI476X_IQFS_IQ = 21, +}; +enum si476x_iout_config { + SI476X_IOUT_NOOP = 0, + SI476X_IOUT_TRISTATE = 1, + SI476X_IOUT_OUTPUT = 22, +}; +enum si476x_qout_config { + SI476X_QOUT_NOOP = 0, + SI476X_QOUT_TRISTATE = 1, + SI476X_QOUT_OUTPUT = 22, +}; + +enum si476x_dclk_config { + SI476X_DCLK_NOOP = 0, + SI476X_DCLK_TRISTATE = 1, + SI476X_DCLK_DAUDIO = 10, +}; + +enum si476x_dfs_config { + SI476X_DFS_NOOP = 0, + SI476X_DFS_TRISTATE = 1, + SI476X_DFS_DAUDIO = 10, +}; + +enum si476x_dout_config { + SI476X_DOUT_NOOP = 0, + SI476X_DOUT_TRISTATE = 1, + SI476X_DOUT_I2S_OUTPUT = 12, + SI476X_DOUT_I2S_INPUT = 13, +}; + +enum si476x_xout_config { + SI476X_XOUT_NOOP = 0, + SI476X_XOUT_TRISTATE = 1, + SI476X_XOUT_I2S_INPUT = 13, + SI476X_XOUT_MODE_SELECT = 23, +}; + + +enum si476x_icin_config { + SI476X_ICIN_NOOP = 0, + SI476X_ICIN_TRISTATE = 1, + SI476X_ICIN_GPO1_HIGH = 2, + SI476X_ICIN_GPO1_LOW = 3, + SI476X_ICIN_IC_LINK = 30, +}; + +enum si476x_icip_config { + SI476X_ICIP_NOOP = 0, + SI476X_ICIP_TRISTATE = 1, + SI476X_ICIP_GPO2_HIGH = 2, + SI476X_ICIP_GPO2_LOW = 3, + SI476X_ICIP_IC_LINK = 30, +}; + +enum si476x_icon_config { + SI476X_ICON_NOOP = 0, + SI476X_ICON_TRISTATE = 1, + SI476X_ICON_I2S = 10, + SI476X_ICON_IC_LINK = 30, +}; + +enum si476x_icop_config { + SI476X_ICOP_NOOP = 0, + SI476X_ICOP_TRISTATE = 1, + SI476X_ICOP_I2S = 10, + SI476X_ICOP_IC_LINK = 30, +}; + + +enum si476x_lrout_config { + SI476X_LROUT_NOOP = 0, + SI476X_LROUT_TRISTATE = 1, + SI476X_LROUT_AUDIO = 2, + SI476X_LROUT_MPX = 3, +}; + + +enum si476x_intb_config { + SI476X_INTB_NOOP = 0, + SI476X_INTB_TRISTATE = 1, + SI476X_INTB_DAUDIO = 10, + SI476X_INTB_IRQ = 40, +}; + +enum si476x_a1_config { + SI476X_A1_NOOP = 0, + SI476X_A1_TRISTATE = 1, + SI476X_A1_IRQ = 40, +}; + +enum si476x_part_revisions { + SI476X_REVISION_A10 = 0, + SI476X_REVISION_A20 = 1, + SI476X_REVISION_A30 = 2, +}; + +struct si476x_pinmux { + enum si476x_dclk_config dclk; + enum si476x_dfs_config dfs; + enum si476x_dout_config dout; + enum si476x_xout_config xout; + + enum si476x_iqclk_config iqclk; + enum si476x_iqfs_config iqfs; + enum si476x_iout_config iout; + enum si476x_qout_config qout; + + enum si476x_icin_config icin; + enum si476x_icip_config icip; + enum si476x_icon_config icon; + enum si476x_icop_config icop; + + enum si476x_lrout_config lrout; + + enum si476x_intb_config intb; + enum si476x_a1_config a1; +}; + +/** + * enum si476x_phase_diversity_mode - possbile phase diversity modes + * for SI4764/5/6/7 chips. + * + * @SI476X_PHDIV_DISABLED: Phase diversity feature is + * disabled. + * @SI476X_PHDIV_PRIMARY_COMBINING: Tuner works as a primary tuner + * in combination with a + * secondary one. + * @SI476X_PHDIV_PRIMARY_ANTENNA: Tuner works as a primary tuner + * using only its own antenna. + * @SI476X_PHDIV_SECONDARY_ANTENNA: Tuner works as a primary tuner + * usning seconary tuner's antenna. + * @SI476X_PHDIV_SECONDARY_COMBINING: Tuner works as a secondary + * tuner in combination with the + * primary one. + */ +enum si476x_phase_diversity_mode { + SI476X_PHDIV_DISABLED = 0, + SI476X_PHDIV_PRIMARY_COMBINING = 1, + SI476X_PHDIV_PRIMARY_ANTENNA = 2, + SI476X_PHDIV_SECONDARY_ANTENNA = 3, + SI476X_PHDIV_SECONDARY_COMBINING = 5, +}; + +enum si476x_ibias6x { + SI476X_IBIAS6X_OTHER = 0, + SI476X_IBIAS6X_RCVR1_NON_4MHZ_CLK = 1, +}; + +enum si476x_xstart { + SI476X_XSTART_MULTIPLE_TUNER = 0x11, + SI476X_XSTART_NORMAL = 0x77, +}; + +enum si476x_freq { + SI476X_FREQ_4_MHZ = 0, + SI476X_FREQ_37P209375_MHZ = 1, + SI476X_FREQ_36P4_MHZ = 2, + SI476X_FREQ_37P8_MHZ = 3, +}; + +enum si476x_xmode { + SI476X_XMODE_CRYSTAL_RCVR1 = 1, + SI476X_XMODE_EXT_CLOCK = 2, + SI476X_XMODE_CRYSTAL_RCVR2_3 = 3, +}; + +enum si476x_xbiashc { + SI476X_XBIASHC_SINGLE_RECEIVER = 0, + SI476X_XBIASHC_MULTIPLE_RECEIVER = 1, +}; + +enum si476x_xbias { + SI476X_XBIAS_RCVR2_3 = 0, + SI476X_XBIAS_4MHZ_RCVR1 = 3, + SI476X_XBIAS_RCVR1 = 7, +}; + +enum si476x_func { + SI476X_FUNC_BOOTLOADER = 0, + SI476X_FUNC_FM_RECEIVER = 1, + SI476X_FUNC_AM_RECEIVER = 2, + SI476X_FUNC_WB_RECEIVER = 3, +}; + + +/** + * @xcload: Selects the amount of additional on-chip capacitance to + * be connected between XTAL1 and gnd and between XTAL2 and + * GND. One half of the capacitance value shown here is the + * additional load capacitance presented to the xtal. The + * minimum step size is 0.277 pF. Recommended value is 0x28 + * but it will be layout dependent. Range is 0–0x3F i.e. + * (0–16.33 pF) + * @ctsien: enable CTSINT(interrupt request when CTS condition + * arises) when set + * @intsel: when set A1 pin becomes the interrupt pin; otherwise, + * INTB is the interrupt pin + * @func: selects the boot function of the device. I.e. + * SI476X_BOOTLOADER - Boot loader + * SI476X_FM_RECEIVER - FM receiver + * SI476X_AM_RECEIVER - AM receiver + * SI476X_WB_RECEIVER - Weatherband receiver + * @freq: oscillator's crystal frequency: + * SI476X_XTAL_37P209375_MHZ - 37.209375 Mhz + * SI476X_XTAL_36P4_MHZ - 36.4 Mhz + * SI476X_XTAL_37P8_MHZ - 37.8 Mhz + */ +struct si476x_power_up_args { + enum si476x_ibias6x ibias6x; + enum si476x_xstart xstart; + u8 xcload; + bool fastboot; + enum si476x_xbiashc xbiashc; + enum si476x_xbias xbias; + enum si476x_func func; + enum si476x_freq freq; + enum si476x_xmode xmode; +}; + + +enum si476x_ctrl_id { + V4L2_CID_SI476X_RSSI_THRESHOLD = (V4L2_CID_USER_SI476X_BASE + 1), + V4L2_CID_SI476X_SNR_THRESHOLD = (V4L2_CID_USER_SI476X_BASE + 2), + V4L2_CID_SI476X_MAX_TUNE_ERROR = (V4L2_CID_USER_SI476X_BASE + 3), + V4L2_CID_SI476X_HARMONICS_COUNT = (V4L2_CID_USER_SI476X_BASE + 4), + V4L2_CID_SI476X_DIVERSITY_MODE = (V4L2_CID_USER_SI476X_BASE + 5), + V4L2_CID_SI476X_INTERCHIP_LINK = (V4L2_CID_USER_SI476X_BASE + 6), +}; + +/* + * Platform dependent definition + */ +struct si476x_platform_data { + int gpio_reset; /* < 0 if not used */ + + struct si476x_power_up_args power_up_parameters; + enum si476x_phase_diversity_mode diversity_mode; + + struct si476x_pinmux pinmux; +}; + +/** + * struct si476x_rsq_status - structure containing received signal + * quality + * @multhint: Multipath Detect High. + * true - Indicatedes that the value is below + * FM_RSQ_MULTIPATH_HIGH_THRESHOLD + * false - Indicatedes that the value is above + * FM_RSQ_MULTIPATH_HIGH_THRESHOLD + * @multlint: Multipath Detect Low. + * true - Indicatedes that the value is below + * FM_RSQ_MULTIPATH_LOW_THRESHOLD + * false - Indicatedes that the value is above + * FM_RSQ_MULTIPATH_LOW_THRESHOLD + * @snrhint: SNR Detect High. + * true - Indicatedes that the value is below + * FM_RSQ_SNR_HIGH_THRESHOLD + * false - Indicatedes that the value is above + * FM_RSQ_SNR_HIGH_THRESHOLD + * @snrlint: SNR Detect Low. + * true - Indicatedes that the value is below + * FM_RSQ_SNR_LOW_THRESHOLD + * false - Indicatedes that the value is above + * FM_RSQ_SNR_LOW_THRESHOLD + * @rssihint: RSSI Detect High. + * true - Indicatedes that the value is below + * FM_RSQ_RSSI_HIGH_THRESHOLD + * false - Indicatedes that the value is above + * FM_RSQ_RSSI_HIGH_THRESHOLD + * @rssilint: RSSI Detect Low. + * true - Indicatedes that the value is below + * FM_RSQ_RSSI_LOW_THRESHOLD + * false - Indicatedes that the value is above + * FM_RSQ_RSSI_LOW_THRESHOLD + * @bltf: Band Limit. + * Set if seek command hits the band limit or wrapped to + * the original frequency. + * @snr_ready: SNR measurement in progress. + * @rssiready: RSSI measurement in progress. + * @afcrl: Set if FREQOFF >= MAX_TUNE_ERROR + * @valid: Set if the channel is valid + * rssi < FM_VALID_RSSI_THRESHOLD + * snr < FM_VALID_SNR_THRESHOLD + * tune_error < FM_VALID_MAX_TUNE_ERROR + * @readfreq: Current tuned frequency. + * @freqoff: Signed frequency offset. + * @rssi: Received Signal Strength Indicator(dBuV). + * @snr: RF SNR Indicator(dB). + * @lassi: + * @hassi: Low/High side Adjacent(100 kHz) Channel Strength Indicator + * @mult: Multipath indicator + * @dev: Who knows? But values may vary. + * @readantcap: Antenna tuning capacity value. + * @assi: Adjacent Channel(+/- 200kHz) Strength Indicator + * @usn: Ultrasonic Noise Inticator in -DBFS + */ +struct si476x_rsq_status_report { + __u8 multhint, multlint; + __u8 snrhint, snrlint; + __u8 rssihint, rssilint; + __u8 bltf; + __u8 snr_ready; + __u8 rssiready; + __u8 injside; + __u8 afcrl; + __u8 valid; + + __u16 readfreq; + __s8 freqoff; + __s8 rssi; + __s8 snr; + __s8 issi; + __s8 lassi, hassi; + __s8 mult; + __u8 dev; + __u16 readantcap; + __s8 assi; + __s8 usn; + + __u8 pilotdev; + __u8 rdsdev; + __u8 assidev; + __u8 strongdev; + __u16 rdspi; +} __packed; + +/** + * si476x_acf_status_report - ACF report results + * + * @blend_int: If set, indicates that stereo separation has crossed + * below the blend threshold as set by FM_ACF_BLEND_THRESHOLD + * @hblend_int: If set, indicates that HiBlend cutoff frequency is + * lower than threshold as set by FM_ACF_HBLEND_THRESHOLD + * @hicut_int: If set, indicates that HiCut cutoff frequency is lower + * than the threshold set by ACF_ + + */ +struct si476x_acf_status_report { + __u8 blend_int; + __u8 hblend_int; + __u8 hicut_int; + __u8 chbw_int; + __u8 softmute_int; + __u8 smute; + __u8 smattn; + __u8 chbw; + __u8 hicut; + __u8 hiblend; + __u8 pilot; + __u8 stblend; +} __packed; + +enum si476x_fmagc { + SI476X_FMAGC_10K_OHM = 0, + SI476X_FMAGC_800_OHM = 1, + SI476X_FMAGC_400_OHM = 2, + SI476X_FMAGC_200_OHM = 4, + SI476X_FMAGC_100_OHM = 8, + SI476X_FMAGC_50_OHM = 16, + SI476X_FMAGC_25_OHM = 32, + SI476X_FMAGC_12P5_OHM = 64, + SI476X_FMAGC_6P25_OHM = 128, +}; + +struct si476x_agc_status_report { + __u8 mxhi; + __u8 mxlo; + __u8 lnahi; + __u8 lnalo; + __u8 fmagc1; + __u8 fmagc2; + __u8 pgagain; + __u8 fmwblang; +} __packed; + +struct si476x_rds_blockcount_report { + __u16 expected; + __u16 received; + __u16 uncorrectable; +} __packed; + +#endif /* SI476X_H*/ diff --git a/trunk/include/media/videobuf-dma-contig.h b/trunk/include/media/videobuf-dma-contig.h index f0ed82543d9f..f473aeb86d3f 100644 --- a/trunk/include/media/videobuf-dma-contig.h +++ b/trunk/include/media/videobuf-dma-contig.h @@ -26,6 +26,16 @@ void videobuf_queue_dma_contig_init(struct videobuf_queue *q, void *priv, struct mutex *ext_lock); +void videobuf_queue_dma_contig_init_cached(struct videobuf_queue *q, + const struct videobuf_queue_ops *ops, + struct device *dev, + spinlock_t *irqlock, + enum v4l2_buf_type type, + enum v4l2_field field, + unsigned int msize, + void *priv, + struct mutex *ext_lock); + dma_addr_t videobuf_to_dma_contig(struct videobuf_buffer *buf); void videobuf_dma_contig_free(struct videobuf_queue *q, struct videobuf_buffer *buf);