diff options
Diffstat (limited to 'linux/drivers')
34 files changed, 2495 insertions, 454 deletions
diff --git a/linux/drivers/media/common/ir-keymaps.c b/linux/drivers/media/common/ir-keymaps.c index 64d3df831..e750bfee1 100644 --- a/linux/drivers/media/common/ir-keymaps.c +++ b/linux/drivers/media/common/ir-keymaps.c @@ -2093,6 +2093,76 @@ IR_KEYTAB_TYPE ir_codes_behold[IR_KEYTAB_SIZE] = { EXPORT_SYMBOL_GPL(ir_codes_behold); +/* Beholder Intl. Ltd. 2008 + * Dmitry Belimov d.belimov@google.com + * Keytable is used by BeholdTV Columbus + * The "ascii-art picture" below (in comments, first row + * is the keycode in hex, and subsequent row(s) shows + * the button labels (several variants when appropriate) + * helps to descide which keycodes to assign to the buttons. + */ +IR_KEYTAB_TYPE ir_codes_behold_columbus[IR_KEYTAB_SIZE] = { + + /* 0x13 0x11 0x1C 0x12 * + * Mute Source TV/FM Power * + * */ + + [0x13] = KEY_MUTE, + [0x11] = KEY_PROPS, + [0x1C] = KEY_TUNER, /* KEY_TV/KEY_RADIO */ + [0x12] = KEY_POWER, + + /* 0x01 0x02 0x03 0x0D * + * 1 2 3 Stereo * + * * + * 0x04 0x05 0x06 0x19 * + * 4 5 6 Snapshot * + * * + * 0x07 0x08 0x09 0x10 * + * 7 8 9 Zoom * + * */ + [0x01] = KEY_1, + [0x02] = KEY_2, + [0x03] = KEY_3, + [0x0D] = KEY_SETUP, /* Setup key */ + [0x04] = KEY_4, + [0x05] = KEY_5, + [0x06] = KEY_6, + [0x19] = KEY_BOOKMARKS, /* Snapshot key */ + [0x07] = KEY_7, + [0x08] = KEY_8, + [0x09] = KEY_9, + [0x10] = KEY_ZOOM, + + /* 0x0A 0x00 0x0B 0x0C * + * RECALL 0 ChannelUp VolumeUp * + * */ + [0x0A] = KEY_AGAIN, + [0x00] = KEY_0, + [0x0B] = KEY_CHANNELUP, + [0x0C] = KEY_VOLUMEUP, + + /* 0x1B 0x1D 0x15 0x18 * + * Timeshift Record ChannelDown VolumeDown * + * */ + + [0x1B] = KEY_REWIND, + [0x1D] = KEY_RECORD, + [0x15] = KEY_CHANNELDOWN, + [0x18] = KEY_VOLUMEDOWN, + + /* 0x0E 0x1E 0x0F 0x1A * + * Stop Pause Previouse Next * + * */ + + [0x0E] = KEY_STOP, + [0x1E] = KEY_PAUSE, + [0x0F] = KEY_PREVIOUS, + [0x1A] = KEY_NEXT, + +}; +EXPORT_SYMBOL_GPL(ir_codes_behold_columbus); + /* * Remote control for the Genius TVGO A11MCE * Adrian Pardini <pardo.bsso@gmail.com> diff --git a/linux/drivers/media/dvb/frontends/dib7000p.c b/linux/drivers/media/dvb/frontends/dib7000p.c index 9b6702b70..c74d373b6 100644 --- a/linux/drivers/media/dvb/frontends/dib7000p.c +++ b/linux/drivers/media/dvb/frontends/dib7000p.c @@ -1194,7 +1194,7 @@ static int dib7000p_set_frontend(struct dvb_frontend* fe, ret = dib7000p_tune(fe, fep); /* make this a config parameter */ - dib7000p_set_output_mode(state, OUTMODE_MPEG2_FIFO); + dib7000p_set_output_mode(state, state->cfg.output_mode); return ret; } @@ -1356,6 +1356,12 @@ struct dvb_frontend * dib7000p_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr, st->gpio_val = cfg->gpio_val; st->gpio_dir = cfg->gpio_dir; + /* Ensure the output mode remains at the previous default if it's + * not specifically set by the caller. + */ + if (st->cfg.output_mode != OUTMODE_MPEG2_SERIAL) + st->cfg.output_mode = OUTMODE_MPEG2_FIFO; + demod = &st->demod; demod->demodulator_priv = st; memcpy(&st->demod.ops, &dib7000p_ops, sizeof(struct dvb_frontend_ops)); diff --git a/linux/drivers/media/dvb/frontends/dib7000p.h b/linux/drivers/media/dvb/frontends/dib7000p.h index eefcac8b5..081bd81f3 100644 --- a/linux/drivers/media/dvb/frontends/dib7000p.h +++ b/linux/drivers/media/dvb/frontends/dib7000p.h @@ -31,6 +31,8 @@ struct dib7000p_config { u8 spur_protect; int (*agc_control) (struct dvb_frontend *, u8 before); + + u8 output_mode; }; #define DEFAULT_DIB7000P_I2C_ADDRESS 18 diff --git a/linux/drivers/media/dvb/frontends/tda10048.c b/linux/drivers/media/dvb/frontends/tda10048.c index 2b9b1d93c..e82814250 100644 --- a/linux/drivers/media/dvb/frontends/tda10048.c +++ b/linux/drivers/media/dvb/frontends/tda10048.c @@ -26,6 +26,7 @@ #include <linux/slab.h> #include <linux/delay.h> #include "dvb_frontend.h" +#include "dvb_math.h" #include "tda10048.h" #define TDA10048_DEFAULT_FIRMWARE "dvb-fe-tda10048-1.0.fw" @@ -563,31 +564,167 @@ static int tda10048_read_signal_strength(struct dvb_frontend *fe, u16 *signal_strength) { struct tda10048_state *state = fe->demodulator_priv; - u16 v; + u8 v; dprintk(1, "%s()\n", __func__); + *signal_strength = 65535; + v = tda10048_readreg(state, TDA10048_NP_OUT); - if (v == 0) - *signal_strength = 100; - else { - /* TODO: Apply .db math for correct values */ - *signal_strength = v; - } + if (v > 0) + *signal_strength -= (v << 8) | v; return 0; } +/* SNR lookup table */ +static struct snr_tab { + u8 val; + u8 data; +} snr_tab[] = { + { 0, 0 }, + { 1, 246 }, + { 2, 215 }, + { 3, 198 }, + { 4, 185 }, + { 5, 176 }, + { 6, 168 }, + { 7, 161 }, + { 8, 155 }, + { 9, 150 }, + { 10, 146 }, + { 11, 141 }, + { 12, 138 }, + { 13, 134 }, + { 14, 131 }, + { 15, 128 }, + { 16, 125 }, + { 17, 122 }, + { 18, 120 }, + { 19, 118 }, + { 20, 115 }, + { 21, 113 }, + { 22, 111 }, + { 23, 109 }, + { 24, 107 }, + { 25, 106 }, + { 26, 104 }, + { 27, 102 }, + { 28, 101 }, + { 29, 99 }, + { 30, 98 }, + { 31, 96 }, + { 32, 95 }, + { 33, 94 }, + { 34, 92 }, + { 35, 91 }, + { 36, 90 }, + { 37, 89 }, + { 38, 88 }, + { 39, 86 }, + { 40, 85 }, + { 41, 84 }, + { 42, 83 }, + { 43, 82 }, + { 44, 81 }, + { 45, 80 }, + { 46, 79 }, + { 47, 78 }, + { 48, 77 }, + { 49, 76 }, + { 50, 76 }, + { 51, 75 }, + { 52, 74 }, + { 53, 73 }, + { 54, 72 }, + { 56, 71 }, + { 57, 70 }, + { 58, 69 }, + { 60, 68 }, + { 61, 67 }, + { 63, 66 }, + { 64, 65 }, + { 66, 64 }, + { 67, 63 }, + { 68, 62 }, + { 69, 62 }, + { 70, 61 }, + { 72, 60 }, + { 74, 59 }, + { 75, 58 }, + { 77, 57 }, + { 79, 56 }, + { 81, 55 }, + { 83, 54 }, + { 85, 53 }, + { 87, 52 }, + { 89, 51 }, + { 91, 50 }, + { 93, 49 }, + { 95, 48 }, + { 97, 47 }, + { 100, 46 }, + { 102, 45 }, + { 104, 44 }, + { 107, 43 }, + { 109, 42 }, + { 112, 41 }, + { 114, 40 }, + { 117, 39 }, + { 120, 38 }, + { 123, 37 }, + { 125, 36 }, + { 128, 35 }, + { 131, 34 }, + { 134, 33 }, + { 138, 32 }, + { 141, 31 }, + { 144, 30 }, + { 147, 29 }, + { 151, 28 }, + { 154, 27 }, + { 158, 26 }, + { 162, 25 }, + { 165, 24 }, + { 169, 23 }, + { 173, 22 }, + { 177, 21 }, + { 181, 20 }, + { 186, 19 }, + { 190, 18 }, + { 194, 17 }, + { 199, 16 }, + { 204, 15 }, + { 208, 14 }, + { 213, 13 }, + { 218, 12 }, + { 223, 11 }, + { 229, 10 }, + { 234, 9 }, + { 239, 8 }, + { 245, 7 }, + { 251, 6 }, + { 255, 5 }, +}; + static int tda10048_read_snr(struct dvb_frontend *fe, u16 *snr) { struct tda10048_state *state = fe->demodulator_priv; + u8 v; + int i, ret = -EINVAL; dprintk(1, "%s()\n", __func__); - /* TODO: This result should be the same as signal strength */ - *snr = tda10048_readreg(state, TDA10048_NP_OUT); + v = tda10048_readreg(state, TDA10048_NP_OUT); + for (i = 0; i < ARRAY_SIZE(snr_tab); i++) { + if (v <= snr_tab[i].val) { + *snr = snr_tab[i].data; + ret = 0; + break; + } + } - return 0; + return ret; } static int tda10048_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) diff --git a/linux/drivers/media/video/cx23885/cx23885-cards.c b/linux/drivers/media/video/cx23885/cx23885-cards.c index c0527801d..26f2f185d 100644 --- a/linux/drivers/media/video/cx23885/cx23885-cards.c +++ b/linux/drivers/media/video/cx23885/cx23885-cards.c @@ -140,6 +140,10 @@ struct cx23885_board cx23885_boards[] = { .name = "Hauppauge WinTV-HVR1700", .portc = CX23885_MPEG_DVB, }, + [CX23885_BOARD_HAUPPAUGE_HVR1400] = { + .name = "Hauppauge WinTV-HVR1400", + .portc = CX23885_MPEG_DVB, + }, }; const unsigned int cx23885_bcount = ARRAY_SIZE(cx23885_boards); @@ -199,6 +203,10 @@ struct cx23885_subid cx23885_subids[] = { .subvendor = 0x0070, .subdevice = 0x8101, .card = CX23885_BOARD_HAUPPAUGE_HVR1700, + }, { + .subvendor = 0x0070, + .subdevice = 0x8010, + .card = CX23885_BOARD_HAUPPAUGE_HVR1400, }, }; const unsigned int cx23885_idcount = ARRAY_SIZE(cx23885_subids); @@ -253,6 +261,9 @@ static void hauppauge_eeprom(struct cx23885_dev *dev, u8 *eeprom_data) case 79561: /* WinTV-HVR1250 (PCIe, OEM, No IR, half height, ATSC and Basic analog */ case 79571: /* WinTV-HVR1250 (PCIe, OEM, No IR, full height, ATSC and Basic analog */ case 79671: /* WinTV-HVR1250 (PCIe, OEM, No IR, half height, ATSC and Basic analog */ + case 80019: + /* WinTV-HVR1400 (Express Card, Retail, IR, + * DVB-T and Basic analog */ case 81519: /* WinTV-HVR1700 (PCIe, Retail, No IR, half height, * DVB-T and MPEG2 HW Encoder */ @@ -371,6 +382,18 @@ void cx23885_gpio_setup(struct cx23885_dev *dev) mdelay(20); cx_set(GP0_IO, 0x00050005); break; + case CX23885_BOARD_HAUPPAUGE_HVR1400: + /* GPIO-0 Dibcom7000p demodulator reset */ + /* GPIO-2 xc3028L tuner reset */ + /* GPIO-13 LED */ + + /* Put the parts into reset and back */ + cx_set(GP0_IO, 0x00050000); + mdelay(20); + cx_clear(GP0_IO, 0x00000005); + mdelay(20); + cx_set(GP0_IO, 0x00050005); + break; } } @@ -382,6 +405,7 @@ int cx23885_ir_init(struct cx23885_dev *dev) case CX23885_BOARD_HAUPPAUGE_HVR1500Q: case CX23885_BOARD_HAUPPAUGE_HVR1800: case CX23885_BOARD_HAUPPAUGE_HVR1200: + case CX23885_BOARD_HAUPPAUGE_HVR1400: /* FIXME: Implement me */ break; } @@ -406,6 +430,7 @@ void cx23885_card_setup(struct cx23885_dev *dev) case CX23885_BOARD_HAUPPAUGE_HVR1250: case CX23885_BOARD_HAUPPAUGE_HVR1500: case CX23885_BOARD_HAUPPAUGE_HVR1500Q: + case CX23885_BOARD_HAUPPAUGE_HVR1400: if (dev->i2c_bus[0].i2c_rc == 0) hauppauge_eeprom(dev, eeprom+0x80); break; @@ -446,6 +471,7 @@ void cx23885_card_setup(struct cx23885_dev *dev) case CX23885_BOARD_HAUPPAUGE_HVR1800lp: case CX23885_BOARD_HAUPPAUGE_HVR1200: case CX23885_BOARD_HAUPPAUGE_HVR1700: + case CX23885_BOARD_HAUPPAUGE_HVR1400: default: ts2->gen_ctrl_val = 0xc; /* Serial bus + punctured clock */ ts2->ts_clk_en_val = 0x1; /* Enable TS_CLK */ diff --git a/linux/drivers/media/video/cx23885/cx23885-dvb.c b/linux/drivers/media/video/cx23885/cx23885-dvb.c index 5923f214e..e0c898076 100644 --- a/linux/drivers/media/video/cx23885/cx23885-dvb.c +++ b/linux/drivers/media/video/cx23885/cx23885-dvb.c @@ -41,6 +41,8 @@ #include "dvb-pll.h" #include "tuner-xc2028.h" #include "tuner-simple.h" +#include "dib7000p.h" +#include "dibx000_common.h" static unsigned int debug; @@ -190,6 +192,92 @@ static struct tda18271_config hauppauge_hvr1200_tuner_config = { .gate = TDA18271_GATE_ANALOG, }; +struct dibx000_agc_config xc3028_agc_config = { + BAND_VHF | BAND_UHF, /* band_caps */ + + /* P_agc_use_sd_mod1=0, P_agc_use_sd_mod2=0, P_agc_freq_pwm_div=0, + * P_agc_inv_pwm1=0, P_agc_inv_pwm2=0, + * P_agc_inh_dc_rv_est=0, P_agc_time_est=3, P_agc_freeze=0, + * P_agc_nb_est=2, P_agc_write=0 + */ + (0 << 15) | (0 << 14) | (0 << 11) | (0 << 10) | (0 << 9) | (0 << 8) | + (3 << 5) | (0 << 4) | (2 << 1) | (0 << 0), /* setup */ + + 712, /* inv_gain */ + 21, /* time_stabiliz */ + + 0, /* alpha_level */ + 118, /* thlock */ + + 0, /* wbd_inv */ + 2867, /* wbd_ref */ + 0, /* wbd_sel */ + 2, /* wbd_alpha */ + + 0, /* agc1_max */ + 0, /* agc1_min */ + 39718, /* agc2_max */ + 9930, /* agc2_min */ + 0, /* agc1_pt1 */ + 0, /* agc1_pt2 */ + 0, /* agc1_pt3 */ + 0, /* agc1_slope1 */ + 0, /* agc1_slope2 */ + 0, /* agc2_pt1 */ + 128, /* agc2_pt2 */ + 29, /* agc2_slope1 */ + 29, /* agc2_slope2 */ + + 17, /* alpha_mant */ + 27, /* alpha_exp */ + 23, /* beta_mant */ + 51, /* beta_exp */ + + 1, /* perform_agc_softsplit */ +}; + +/* PLL Configuration for COFDM BW_MHz = 8.000000 + * With external clock = 30.000000 */ +struct dibx000_bandwidth_config xc3028_bw_config = { + 60000, /* internal */ + 30000, /* sampling */ + 1, /* pll_cfg: prediv */ + 8, /* pll_cfg: ratio */ + 3, /* pll_cfg: range */ + 1, /* pll_cfg: reset */ + 0, /* pll_cfg: bypass */ + 0, /* misc: refdiv */ + 0, /* misc: bypclk_div */ + 1, /* misc: IO_CLK_en_core */ + 1, /* misc: ADClkSrc */ + 0, /* misc: modulo */ + (3 << 14) | (1 << 12) | (524 << 0), /* sad_cfg: refsel, sel, freq_15k */ + (1 << 25) | 5816102, /* ifreq = 5.200000 MHz */ + 20452225, /* timf */ + 30000000 /* xtal_hz */ +}; + +static struct dib7000p_config hauppauge_hvr1400_dib7000_config = { + .output_mpeg2_in_188_bytes = 1, + .hostbus_diversity = 1, + .tuner_is_baseband = 0, + .update_lna = NULL, + + .agc_config_count = 1, + .agc = &xc3028_agc_config, + .bw = &xc3028_bw_config, + + .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 = NULL, + .spur_protect = 0, + + .output_mode = OUTMODE_MPEG2_SERIAL, +}; + static int cx23885_hvr1500_xc3028_callback(void *ptr, int command, int arg) { struct cx23885_tsport *port = ptr; @@ -344,6 +432,31 @@ static int dvb_register(struct cx23885_tsport *port) &hauppauge_hvr1200_tuner_config); } break; + case CX23885_BOARD_HAUPPAUGE_HVR1400: + i2c_bus = &dev->i2c_bus[0]; + port->dvb.frontend = dvb_attach(dib7000p_attach, + &i2c_bus->i2c_adap, + 0x12, &hauppauge_hvr1400_dib7000_config); + if (port->dvb.frontend != NULL) { + struct dvb_frontend *fe; + struct xc2028_config cfg = { + .i2c_adap = &dev->i2c_bus[1].i2c_adap, + .i2c_addr = 0x64, + .callback = cx23885_hvr1500_xc3028_callback, + }; + static struct xc2028_ctrl ctl = { + .fname = "xc3028L-v36.fw", + .max_len = 64, + .demod = 5000, + .d2633 = 1 + }; + + fe = dvb_attach(xc2028_attach, + port->dvb.frontend, &cfg); + if (fe != NULL && fe->ops.tuner_ops.set_config != NULL) + fe->ops.tuner_ops.set_config(fe, &ctl); + } + break; default: printk("%s: The frontend of your DVB/ATSC card isn't supported yet\n", dev->name); diff --git a/linux/drivers/media/video/cx23885/cx23885-i2c.c b/linux/drivers/media/video/cx23885/cx23885-i2c.c index 5513b3b39..ba8e27ca9 100644 --- a/linux/drivers/media/video/cx23885/cx23885-i2c.c +++ b/linux/drivers/media/video/cx23885/cx23885-i2c.c @@ -375,6 +375,7 @@ static struct i2c_client cx23885_i2c_client_template = { static char *i2c_devs[128] = { [0x10 >> 1] = "tda10048", + [0x12 >> 1] = "dib7000pc", [ 0x1c >> 1 ] = "lgdt3303", [ 0x86 >> 1 ] = "tda9887", [ 0x32 >> 1 ] = "cx24227", @@ -382,7 +383,8 @@ static char *i2c_devs[128] = { [ 0x84 >> 1 ] = "tda8295", [ 0xa0 >> 1 ] = "eeprom", [ 0xc0 >> 1 ] = "tuner/mt2131/tda8275", - [ 0xc2 >> 1 ] = "tuner/mt2131/tda8275/xc5000", + [0xc2 >> 1] = "tuner/mt2131/tda8275/xc5000/xc3028", + [0xc8 >> 1] = "tuner/xc3028L", }; static void do_i2c_scan(char *name, struct i2c_client *c) diff --git a/linux/drivers/media/video/cx23885/cx23885.h b/linux/drivers/media/video/cx23885/cx23885.h index a23c8d002..4d65080d3 100644 --- a/linux/drivers/media/video/cx23885/cx23885.h +++ b/linux/drivers/media/video/cx23885/cx23885.h @@ -65,6 +65,7 @@ #define CX23885_BOARD_HAUPPAUGE_HVR1500 6 #define CX23885_BOARD_HAUPPAUGE_HVR1200 7 #define CX23885_BOARD_HAUPPAUGE_HVR1700 8 +#define CX23885_BOARD_HAUPPAUGE_HVR1400 9 /* Currently unsupported by the driver: PAL/H, NTSC/Kr, SECAM B/G/H/LC */ #define CX23885_NORMS (\ diff --git a/linux/drivers/media/video/pvrusb2/Kconfig b/linux/drivers/media/video/pvrusb2/Kconfig index 6fc1b8be1..a8da90f69 100644 --- a/linux/drivers/media/video/pvrusb2/Kconfig +++ b/linux/drivers/media/video/pvrusb2/Kconfig @@ -58,6 +58,30 @@ config VIDEO_PVRUSB2_SYSFS Note: This feature is experimental and subject to change. +config VIDEO_PVRUSB2_DVB + bool "pvrusb2 DVB support (EXPERIMENTAL)" + default n + depends on VIDEO_PVRUSB2 && DVB_CORE && EXPERIMENTAL + select DVB_LGDT330X if !DVB_FE_CUSTOMISE + select DVB_S5H1409 if !DVB_FE_CUSTOMISE + select DVB_TDA10048 if !DVB_FE_CUSTOMIZE + select DVB_TDA18271 if !DVB_FE_CUSTOMIZE + select TUNER_SIMPLE if !DVB_FE_CUSTOMISE + select TUNER_TDA8290 if !DVB_FE_CUSTOMIZE + ---help--- + + This option enables compilation of a DVB interface for the + pvrusb2 driver. Currently this is very very experimental. + It is also limiting - the DVB interface can only access the + digital side of hybrid devices, and there are going to be + issues if you attempt to mess with the V4L side at the same + time. Don't turn this on unless you know what you are + doing. + + If you are in doubt, say N. + + Note: This feature is very experimental and might break + config VIDEO_PVRUSB2_DEBUGIFC bool "pvrusb2 debug interface" depends on VIDEO_PVRUSB2_SYSFS diff --git a/linux/drivers/media/video/pvrusb2/Makefile b/linux/drivers/media/video/pvrusb2/Makefile index 47284e558..5b3083c89 100644 --- a/linux/drivers/media/video/pvrusb2/Makefile +++ b/linux/drivers/media/video/pvrusb2/Makefile @@ -1,5 +1,6 @@ obj-pvrusb2-sysfs-$(CONFIG_VIDEO_PVRUSB2_SYSFS) := pvrusb2-sysfs.o obj-pvrusb2-debugifc-$(CONFIG_VIDEO_PVRUSB2_DEBUGIFC) := pvrusb2-debugifc.o +obj-pvrusb2-dvb-$(CONFIG_VIDEO_PVRUSB2_DVB) := pvrusb2-dvb.o pvrusb2-objs := pvrusb2-i2c-core.o pvrusb2-i2c-cmd-v4l2.o \ pvrusb2-audio.o pvrusb2-i2c-chips-v4l2.o \ @@ -9,6 +10,11 @@ pvrusb2-objs := pvrusb2-i2c-core.o pvrusb2-i2c-cmd-v4l2.o \ pvrusb2-ctrl.o pvrusb2-std.o pvrusb2-devattr.o \ pvrusb2-context.o pvrusb2-io.o pvrusb2-ioread.o \ pvrusb2-cx2584x-v4l.o pvrusb2-wm8775.o \ + $(obj-pvrusb2-dvb-y) \ $(obj-pvrusb2-sysfs-y) $(obj-pvrusb2-debugifc-y) obj-$(CONFIG_VIDEO_PVRUSB2) += pvrusb2.o + +EXTRA_CFLAGS += -Idrivers/media/video +EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core +EXTRA_CFLAGS += -Idrivers/media/dvb/frontends diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-context.c b/linux/drivers/media/video/pvrusb2/pvrusb2-context.c index b1b4f3a12..1a2485f5d 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-context.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-context.c @@ -23,6 +23,7 @@ #include "pvrusb2-ioread.h" #include "pvrusb2-hdw.h" #include "pvrusb2-debug.h" +#include <linux/wait.h> #include <linux/kthread.h> #include <linux/errno.h> #include <linux/string.h> @@ -30,100 +31,238 @@ #include <asm/semaphore.h> #include "compat.h" +static struct pvr2_context *pvr2_context_exist_first; +static struct pvr2_context *pvr2_context_exist_last; +static struct pvr2_context *pvr2_context_notify_first; +static struct pvr2_context *pvr2_context_notify_last; +static DEFINE_MUTEX(pvr2_context_mutex); +static DECLARE_WAIT_QUEUE_HEAD(pvr2_context_sync_data); +static DECLARE_WAIT_QUEUE_HEAD(pvr2_context_cleanup_data); +static int pvr2_context_cleanup_flag; +static int pvr2_context_cleaned_flag; +static struct task_struct *pvr2_context_thread_ptr; + + +static void pvr2_context_set_notify(struct pvr2_context *mp, int fl) +{ + int signal_flag = 0; + mutex_lock(&pvr2_context_mutex); + if (fl) { + if (!mp->notify_flag) { + signal_flag = (pvr2_context_notify_first == NULL); + mp->notify_prev = pvr2_context_notify_last; + mp->notify_next = NULL; + pvr2_context_notify_last = mp; + if (mp->notify_prev) { + mp->notify_prev->notify_next = mp; + } else { + pvr2_context_notify_first = mp; + } + mp->notify_flag = !0; + } + } else { + if (mp->notify_flag) { + mp->notify_flag = 0; + if (mp->notify_next) { + mp->notify_next->notify_prev = mp->notify_prev; + } else { + pvr2_context_notify_last = mp->notify_prev; + } + if (mp->notify_prev) { + mp->notify_prev->notify_next = mp->notify_next; + } else { + pvr2_context_notify_first = mp->notify_next; + } + } + } + mutex_unlock(&pvr2_context_mutex); + if (signal_flag) wake_up(&pvr2_context_sync_data); +} + static void pvr2_context_destroy(struct pvr2_context *mp) { pvr2_trace(PVR2_TRACE_CTXT,"pvr2_context %p (destroy)",mp); if (mp->hdw) pvr2_hdw_destroy(mp->hdw); + pvr2_context_set_notify(mp, 0); + mutex_lock(&pvr2_context_mutex); + if (mp->exist_next) { + mp->exist_next->exist_prev = mp->exist_prev; + } else { + pvr2_context_exist_last = mp->exist_prev; + } + if (mp->exist_prev) { + mp->exist_prev->exist_next = mp->exist_next; + } else { + pvr2_context_exist_first = mp->exist_next; + } + if (!pvr2_context_exist_first) { + /* Trigger wakeup on control thread in case it is waiting + for an exit condition. */ + wake_up(&pvr2_context_sync_data); + } + mutex_unlock(&pvr2_context_mutex); kfree(mp); } static void pvr2_context_notify(struct pvr2_context *mp) { - mp->notify_flag = !0; - wake_up(&mp->wait_data); + pvr2_context_set_notify(mp,!0); } -static int pvr2_context_thread(void *_mp) +static void pvr2_context_check(struct pvr2_context *mp) { - struct pvr2_channel *ch1,*ch2; - struct pvr2_context *mp = _mp; - pvr2_trace(PVR2_TRACE_CTXT,"pvr2_context %p (thread start)",mp); - - /* Finish hardware initialization */ - if (pvr2_hdw_initialize(mp->hdw, - (void (*)(void *))pvr2_context_notify,mp)) { - mp->video_stream.stream = - pvr2_hdw_get_video_stream(mp->hdw); - /* Trigger interface initialization. By doing this here - initialization runs in our own safe and cozy thread - context. */ - if (mp->setup_func) mp->setup_func(mp); - } else { + struct pvr2_channel *ch1, *ch2; + pvr2_trace(PVR2_TRACE_CTXT, + "pvr2_context %p (notify)", mp); + if (!mp->initialized_flag && !mp->disconnect_flag) { + mp->initialized_flag = !0; pvr2_trace(PVR2_TRACE_CTXT, - "pvr2_context %p (thread skipping setup)",mp); - /* Even though initialization did not succeed, we're still - going to enter the wait loop anyway. We need to do this - in order to await the expected disconnect (which we will - detect in the normal course of operation). */ - } - - /* Now just issue callbacks whenever hardware state changes or if - there is a disconnect. If there is a disconnect and there are - no channels left, then there's no reason to stick around anymore - so we'll self-destruct - tearing down the rest of this driver - instance along the way. */ - pvr2_trace(PVR2_TRACE_CTXT,"pvr2_context %p (thread enter loop)",mp); - while (!mp->disconnect_flag || mp->mc_first) { - if (mp->notify_flag) { - mp->notify_flag = 0; + "pvr2_context %p (initialize)", mp); + /* Finish hardware initialization */ + if (pvr2_hdw_initialize(mp->hdw, + (void (*)(void *))pvr2_context_notify, + mp)) { + mp->video_stream.stream = + pvr2_hdw_get_video_stream(mp->hdw); + /* Trigger interface initialization. By doing this + here initialization runs in our own safe and + cozy thread context. */ + if (mp->setup_func) mp->setup_func(mp); + } else { pvr2_trace(PVR2_TRACE_CTXT, - "pvr2_context %p (thread notify)",mp); - for (ch1 = mp->mc_first; ch1; ch1 = ch2) { - ch2 = ch1->mc_next; - if (ch1->check_func) ch1->check_func(ch1); - } + "pvr2_context %p (thread skipping setup)", + mp); + /* Even though initialization did not succeed, + we're still going to continue anyway. We need + to do this in order to await the expected + disconnect (which we will detect in the normal + course of operation). */ } - wait_event_interruptible(mp->wait_data, mp->notify_flag); } - pvr2_trace(PVR2_TRACE_CTXT,"pvr2_context %p (thread end)",mp); - pvr2_context_destroy(mp); + + for (ch1 = mp->mc_first; ch1; ch1 = ch2) { + ch2 = ch1->mc_next; + if (ch1->check_func) ch1->check_func(ch1); + } + + if (mp->disconnect_flag && !mp->mc_first) { + /* Go away... */ + pvr2_context_destroy(mp); + return; + } +} + + +static int pvr2_context_shutok(void) +{ + return pvr2_context_cleanup_flag && (pvr2_context_exist_first == NULL); +} + + +static int pvr2_context_thread_func(void *foo) +{ + struct pvr2_context *mp; + + pvr2_trace(PVR2_TRACE_CTXT,"pvr2_context thread start"); + + do { + while ((mp = pvr2_context_notify_first) != NULL) { + pvr2_context_set_notify(mp, 0); + pvr2_context_check(mp); + } + wait_event_interruptible( + pvr2_context_sync_data, + ((pvr2_context_notify_first != NULL) || + pvr2_context_shutok())); + } while (!pvr2_context_shutok()); + + pvr2_context_cleaned_flag = !0; + wake_up(&pvr2_context_cleanup_data); + + pvr2_trace(PVR2_TRACE_CTXT,"pvr2_context thread cleaned up"); + + wait_event_interruptible( + pvr2_context_sync_data, + kthread_should_stop()); + + pvr2_trace(PVR2_TRACE_CTXT,"pvr2_context thread end"); + return 0; } +int pvr2_context_global_init(void) +{ + pvr2_context_thread_ptr = kthread_run(pvr2_context_thread_func, + 0, + "pvrusb2-context"); + return (pvr2_context_thread_ptr ? 0 : -ENOMEM); +} + + +void pvr2_context_global_done(void) +{ + pvr2_context_cleanup_flag = !0; + wake_up(&pvr2_context_sync_data); + wait_event_interruptible( + pvr2_context_cleanup_data, + pvr2_context_cleaned_flag); + kthread_stop(pvr2_context_thread_ptr); +} + + struct pvr2_context *pvr2_context_create( struct usb_interface *intf, const struct usb_device_id *devid, void (*setup_func)(struct pvr2_context *)) { - struct task_struct *thread; struct pvr2_context *mp = NULL; mp = kzalloc(sizeof(*mp),GFP_KERNEL); if (!mp) goto done; pvr2_trace(PVR2_TRACE_CTXT,"pvr2_context %p (create)",mp); - init_waitqueue_head(&mp->wait_data); mp->setup_func = setup_func; mutex_init(&mp->mutex); + mutex_lock(&pvr2_context_mutex); + mp->exist_prev = pvr2_context_exist_last; + mp->exist_next = NULL; + pvr2_context_exist_last = mp; + if (mp->exist_prev) { + mp->exist_prev->exist_next = mp; + } else { + pvr2_context_exist_first = mp; + } + mutex_unlock(&pvr2_context_mutex); mp->hdw = pvr2_hdw_create(intf,devid); if (!mp->hdw) { pvr2_context_destroy(mp); mp = NULL; goto done; } - thread = kthread_run(pvr2_context_thread, mp, "pvrusb2-context"); - if (!thread) { - pvr2_context_destroy(mp); - mp = NULL; - goto done; - } + pvr2_context_set_notify(mp, !0); done: return mp; } +static void pvr2_context_reset_input_limits(struct pvr2_context *mp) +{ + unsigned int tmsk,mmsk; + struct pvr2_channel *cp; + struct pvr2_hdw *hdw = mp->hdw; + mmsk = pvr2_hdw_get_input_available(hdw); + tmsk = mmsk; + for (cp = mp->mc_first; cp; cp = cp->mc_next) { + if (!cp->input_mask) continue; + tmsk &= cp->input_mask; + } + pvr2_hdw_set_input_allowed(hdw,mmsk,tmsk); + pvr2_hdw_commit_ctl(hdw); +} + + static void pvr2_context_enter(struct pvr2_context *mp) { mutex_lock(&mp->mutex); @@ -179,7 +318,9 @@ void pvr2_channel_done(struct pvr2_channel *cp) { struct pvr2_context *mp = cp->mc_head; pvr2_context_enter(mp); + cp->input_mask = 0; pvr2_channel_disclaim_stream(cp); + pvr2_context_reset_input_limits(mp); if (cp->mc_next) { cp->mc_next->mc_prev = cp->mc_prev; } else { @@ -195,6 +336,57 @@ void pvr2_channel_done(struct pvr2_channel *cp) } +int pvr2_channel_limit_inputs(struct pvr2_channel *cp,unsigned int cmsk) +{ + unsigned int tmsk,mmsk; + int ret = 0; + struct pvr2_channel *p2; + struct pvr2_hdw *hdw = cp->hdw; + + mmsk = pvr2_hdw_get_input_available(hdw); + cmsk &= mmsk; + if (cmsk == cp->input_mask) { + /* No change; nothing to do */ + return 0; + } + + pvr2_context_enter(cp->mc_head); + do { + if (!cmsk) { + cp->input_mask = 0; + pvr2_context_reset_input_limits(cp->mc_head); + break; + } + tmsk = mmsk; + for (p2 = cp->mc_head->mc_first; p2; p2 = p2->mc_next) { + if (p2 == cp) continue; + if (!p2->input_mask) continue; + tmsk &= p2->input_mask; + } + if (!(tmsk & cmsk)) { + ret = -EPERM; + break; + } + tmsk &= cmsk; + if ((ret = pvr2_hdw_set_input_allowed(hdw,mmsk,tmsk)) != 0) { + /* Internal failure changing allowed list; probably + should not happen, but react if it does. */ + break; + } + cp->input_mask = cmsk; + pvr2_hdw_commit_ctl(hdw); + } while (0); + pvr2_context_exit(cp->mc_head); + return ret; +} + + +unsigned int pvr2_channel_get_limited_inputs(struct pvr2_channel *cp) +{ + return cp->input_mask; +} + + int pvr2_channel_claim_stream(struct pvr2_channel *cp, struct pvr2_context_stream *sp) { diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-context.h b/linux/drivers/media/video/pvrusb2/pvrusb2-context.h index 1df66ac81..c6e25e154 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-context.h +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-context.h @@ -44,6 +44,10 @@ struct pvr2_context_stream { struct pvr2_context { struct pvr2_channel *mc_first; struct pvr2_channel *mc_last; + struct pvr2_context *exist_next; + struct pvr2_context *exist_prev; + struct pvr2_context *notify_next; + struct pvr2_context *notify_prev; struct pvr2_hdw *hdw; struct pvr2_context_stream video_stream; #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) @@ -52,10 +56,9 @@ struct pvr2_context { struct semaphore mutex; #endif int notify_flag; + int initialized_flag; int disconnect_flag; - wait_queue_head_t wait_data; - /* Called after pvr2_context initialization is complete */ void (*setup_func)(struct pvr2_context *); @@ -67,6 +70,7 @@ struct pvr2_channel { struct pvr2_channel *mc_prev; struct pvr2_context_stream *stream; struct pvr2_hdw *hdw; + unsigned int input_mask; void (*check_func)(struct pvr2_channel *); }; @@ -77,11 +81,15 @@ void pvr2_context_disconnect(struct pvr2_context *); void pvr2_channel_init(struct pvr2_channel *,struct pvr2_context *); void pvr2_channel_done(struct pvr2_channel *); +int pvr2_channel_limit_inputs(struct pvr2_channel *,unsigned int); +unsigned int pvr2_channel_get_limited_inputs(struct pvr2_channel *); int pvr2_channel_claim_stream(struct pvr2_channel *, struct pvr2_context_stream *); struct pvr2_ioread *pvr2_channel_create_mpeg_stream( struct pvr2_context_stream *); +int pvr2_context_global_init(void); +void pvr2_context_global_done(void); #endif /* __PVRUSB2_CONTEXT_H */ /* diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c b/linux/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c index c64bd6078..dc81dfca9 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c @@ -85,7 +85,9 @@ static const struct routing_scheme_item routing_schemegv[] = { .vid = CX25840_COMPOSITE2, .aud = CX25840_AUDIO5, }, - [PVR2_CVAL_INPUT_RADIO] = { /* Treat the same as composite */ + [PVR2_CVAL_INPUT_RADIO] = { + /* line-in is used for radio and composite. A GPIO is + used to switch between the two choices. */ .vid = CX25840_COMPOSITE1, .aud = CX25840_AUDIO_SERIAL, }, diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-debugifc.c b/linux/drivers/media/video/pvrusb2/pvrusb2-debugifc.c index b0687430f..b53121c78 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-debugifc.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-debugifc.c @@ -164,6 +164,8 @@ int pvr2_debugifc_print_status(struct pvr2_hdw *hdw, int ccnt; int ret; u32 gpio_dir,gpio_in,gpio_out; + struct pvr2_stream_stats stats; + struct pvr2_stream *sp; ret = pvr2_hdw_is_hsm(hdw); ccnt = scnprintf(buf,acnt,"USB link speed: %s\n", @@ -182,6 +184,24 @@ int pvr2_debugifc_print_status(struct pvr2_hdw *hdw, pvr2_hdw_get_streaming(hdw) ? "on" : "off"); bcnt += ccnt; acnt -= ccnt; buf += ccnt; + + sp = pvr2_hdw_get_video_stream(hdw); + if (sp) { + pvr2_stream_get_stats(sp, &stats, 0); + ccnt = scnprintf( + buf,acnt, + "Bytes streamed=%u" + " URBs: queued=%u idle=%u ready=%u" + " processed=%u failed=%u\n", + stats.bytes_processed, + stats.buffers_in_queue, + stats.buffers_in_idle, + stats.buffers_in_ready, + stats.buffers_processed, + stats.buffers_failed); + bcnt += ccnt; acnt -= ccnt; buf += ccnt; + } + return bcnt; } @@ -220,6 +240,10 @@ static int pvr2_debugifc_do1cmd(struct pvr2_hdw *hdw,const char *buf, return pvr2_hdw_cmd_decoder_reset(hdw); } else if (debugifc_match_keyword(wptr,wlen,"worker")) { return pvr2_hdw_untrip(hdw); + } else if (debugifc_match_keyword(wptr,wlen,"usbstats")) { + pvr2_stream_get_stats(pvr2_hdw_get_video_stream(hdw), + NULL, !0); + return 0; } return -EINVAL; } else if (debugifc_match_keyword(wptr,wlen,"cpufw")) { diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-devattr.c b/linux/drivers/media/video/pvrusb2/pvrusb2-devattr.c index 3434b0e6d..2dd06a90a 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-devattr.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-devattr.c @@ -32,7 +32,15 @@ pvr2_device_desc structures. /* This is needed in order to pull in tuner type ids... */ #include <linux/i2c.h> #include <media/tuner.h> - +#ifdef CONFIG_VIDEO_PVRUSB2_DVB +#include "pvrusb2-hdw-internal.h" +#include "lgdt330x.h" +#include "s5h1409.h" +#include "tda10048.h" +#include "tda18271.h" +#include "tda8290.h" +#include "tuner-simple.h" +#endif /*------------------------------------------------------------------------*/ @@ -49,7 +57,7 @@ static const char *pvr2_fw1_names_29xxx[] = { }; static const struct pvr2_device_desc pvr2_device_29xxx = { - .description = "WinTV PVR USB2 Model Category 29xxxx", + .description = "WinTV PVR USB2 Model Category 29xxx", .shortname = "29xxx", .client_modules.lst = pvr2_client_29xxx, .client_modules.cnt = ARRAY_SIZE(pvr2_client_29xxx), @@ -80,7 +88,7 @@ static const char *pvr2_fw1_names_24xxx[] = { }; static const struct pvr2_device_desc pvr2_device_24xxx = { - .description = "WinTV PVR USB2 Model Category 24xxxx", + .description = "WinTV PVR USB2 Model Category 24xxx", .shortname = "24xxx", .client_modules.lst = pvr2_client_24xxx, .client_modules.cnt = ARRAY_SIZE(pvr2_client_24xxx), @@ -116,6 +124,7 @@ static const struct pvr2_device_desc pvr2_device_gotview_2 = { .flag_has_cx25840 = !0, .default_tuner_type = TUNER_PHILIPS_FM1216ME_MK3, .flag_has_analogtuner = !0, + .flag_has_fmradio = !0, .flag_has_composite = !0, .flag_has_svideo = !0, .signal_routing_scheme = PVR2_ROUTING_SCHEME_GOTVIEW, @@ -147,6 +156,38 @@ static const struct pvr2_device_desc pvr2_device_gotview_2d = { /*------------------------------------------------------------------------*/ /* OnAir Creator */ +#ifdef CONFIG_VIDEO_PVRUSB2_DVB +static struct lgdt330x_config pvr2_lgdt3303_config = { + .demod_address = 0x0e, + .demod_chip = LGDT3303, + .clock_polarity_flip = 1, +}; + +static int pvr2_lgdt3303_attach(struct pvr2_dvb_adapter *adap) +{ + adap->fe = dvb_attach(lgdt330x_attach, &pvr2_lgdt3303_config, + &adap->channel.hdw->i2c_adap); + if (adap->fe) + return 0; + + return -EIO; +} + +static int pvr2_lgh06xf_attach(struct pvr2_dvb_adapter *adap) +{ + dvb_attach(simple_tuner_attach, adap->fe, + &adap->channel.hdw->i2c_adap, 0x61, + TUNER_LG_TDVS_H06XF); + + return 0; +} + +struct pvr2_dvb_props pvr2_onair_creator_fe_props = { + .frontend_attach = pvr2_lgdt3303_attach, + .tuner_attach = pvr2_lgh06xf_attach, +}; +#endif + static const char *pvr2_client_onair_creator[] = { "saa7115", "tuner", @@ -162,9 +203,13 @@ static const struct pvr2_device_desc pvr2_device_onair_creator = { .flag_has_analogtuner = !0, .flag_has_composite = !0, .flag_has_svideo = !0, + .flag_digital_requires_cx23416 = !0, .signal_routing_scheme = PVR2_ROUTING_SCHEME_HAUPPAUGE, .digital_control_scheme = PVR2_DIGITAL_SCHEME_ONAIR, .default_std_mask = V4L2_STD_NTSC_M, +#ifdef CONFIG_VIDEO_PVRUSB2_DVB + .dvb_props = &pvr2_onair_creator_fe_props, +#endif }; #endif @@ -174,6 +219,37 @@ static const struct pvr2_device_desc pvr2_device_onair_creator = { /*------------------------------------------------------------------------*/ /* OnAir USB 2.0 */ +#ifdef CONFIG_VIDEO_PVRUSB2_DVB +static struct lgdt330x_config pvr2_lgdt3302_config = { + .demod_address = 0x0e, + .demod_chip = LGDT3302, +}; + +static int pvr2_lgdt3302_attach(struct pvr2_dvb_adapter *adap) +{ + adap->fe = dvb_attach(lgdt330x_attach, &pvr2_lgdt3302_config, + &adap->channel.hdw->i2c_adap); + if (adap->fe) + return 0; + + return -EIO; +} + +static int pvr2_fcv1236d_attach(struct pvr2_dvb_adapter *adap) +{ + dvb_attach(simple_tuner_attach, adap->fe, + &adap->channel.hdw->i2c_adap, 0x61, + TUNER_PHILIPS_FCV1236D); + + return 0; +} + +struct pvr2_dvb_props pvr2_onair_usb2_fe_props = { + .frontend_attach = pvr2_lgdt3302_attach, + .tuner_attach = pvr2_fcv1236d_attach, +}; +#endif + static const char *pvr2_client_onair_usb2[] = { "saa7115", "tuner", @@ -189,9 +265,13 @@ static const struct pvr2_device_desc pvr2_device_onair_usb2 = { .flag_has_analogtuner = !0, .flag_has_composite = !0, .flag_has_svideo = !0, + .flag_digital_requires_cx23416 = !0, .signal_routing_scheme = PVR2_ROUTING_SCHEME_HAUPPAUGE, .digital_control_scheme = PVR2_DIGITAL_SCHEME_ONAIR, .default_std_mask = V4L2_STD_NTSC_M, +#ifdef CONFIG_VIDEO_PVRUSB2_DVB + .dvb_props = &pvr2_onair_usb2_fe_props, +#endif }; #endif @@ -200,6 +280,50 @@ static const struct pvr2_device_desc pvr2_device_onair_usb2 = { /*------------------------------------------------------------------------*/ /* Hauppauge PVR-USB2 Model 73xxx */ +#ifdef CONFIG_VIDEO_PVRUSB2_DVB +static struct tda10048_config hauppauge_tda10048_config = { + .demod_address = 0x10 >> 1, + .output_mode = TDA10048_PARALLEL_OUTPUT, + .fwbulkwritelen = TDA10048_BULKWRITE_50, + .inversion = TDA10048_INVERSION_ON, +}; + +static struct tda829x_config tda829x_no_probe = { + .probe_tuner = TDA829X_DONT_PROBE, +}; + +static struct tda18271_config hauppauge_tda18271_dvb_config = { + .gate = TDA18271_GATE_ANALOG, +}; + +static int pvr2_tda10048_attach(struct pvr2_dvb_adapter *adap) +{ + adap->fe = dvb_attach(tda10048_attach, &hauppauge_tda10048_config, + &adap->channel.hdw->i2c_adap); + if (adap->fe) + return 0; + + return -EIO; +} + +static int pvr2_73xxx_tda18271_8295_attach(struct pvr2_dvb_adapter *adap) +{ + dvb_attach(tda829x_attach, adap->fe, + &adap->channel.hdw->i2c_adap, 0x42, + &tda829x_no_probe); + dvb_attach(tda18271_attach, adap->fe, 0x60, + &adap->channel.hdw->i2c_adap, + &hauppauge_tda18271_dvb_config); + + return 0; +} + +struct pvr2_dvb_props pvr2_73xxx_dvb_props = { + .frontend_attach = pvr2_tda10048_attach, + .tuner_attach = pvr2_73xxx_tda18271_8295_attach, +}; +#endif + static const char *pvr2_client_73xxx[] = { "cx25840", "tuner", @@ -210,7 +334,7 @@ static const char *pvr2_fw1_names_73xxx[] = { }; static const struct pvr2_device_desc pvr2_device_73xxx = { - .description = "WinTV PVR USB2 Model Category 73xxxx", + .description = "WinTV PVR USB2 Model Category 73xxx", .shortname = "73xxx", .client_modules.lst = pvr2_client_73xxx, .client_modules.cnt = ARRAY_SIZE(pvr2_client_73xxx), @@ -224,6 +348,9 @@ static const struct pvr2_device_desc pvr2_device_73xxx = { .signal_routing_scheme = PVR2_ROUTING_SCHEME_HAUPPAUGE, .digital_control_scheme = PVR2_DIGITAL_SCHEME_HAUPPAUGE, .led_scheme = PVR2_LED_SCHEME_HAUPPAUGE, +#ifdef CONFIG_VIDEO_PVRUSB2_DVB + .dvb_props = &pvr2_73xxx_dvb_props, +#endif }; @@ -231,6 +358,56 @@ static const struct pvr2_device_desc pvr2_device_73xxx = { /*------------------------------------------------------------------------*/ /* Hauppauge PVR-USB2 Model 75xxx */ +#ifdef CONFIG_VIDEO_PVRUSB2_DVB +static struct s5h1409_config pvr2_s5h1409_config = { + .demod_address = 0x32 >> 1, + .output_mode = S5H1409_PARALLEL_OUTPUT, + .gpio = S5H1409_GPIO_OFF, + .qam_if = 4000, + .inversion = S5H1409_INVERSION_ON, + .status_mode = S5H1409_DEMODLOCKING, +}; + +static struct tda18271_std_map hauppauge_tda18271_std_map = { + .atsc_6 = { .if_freq = 5380, .agc_mode = 3, .std = 3, + .if_lvl = 6, .rfagc_top = 0x37, }, + .qam_6 = { .if_freq = 4000, .agc_mode = 3, .std = 0, + .if_lvl = 6, .rfagc_top = 0x37, }, +}; + +static struct tda18271_config hauppauge_tda18271_config = { + .std_map = &hauppauge_tda18271_std_map, + .gate = TDA18271_GATE_ANALOG, +}; + +static int pvr2_s5h1409_attach(struct pvr2_dvb_adapter *adap) +{ + adap->fe = dvb_attach(s5h1409_attach, &pvr2_s5h1409_config, + &adap->channel.hdw->i2c_adap); + if (adap->fe) + return 0; + + return -EIO; +} + +static int pvr2_tda18271_8295_attach(struct pvr2_dvb_adapter *adap) +{ + dvb_attach(tda829x_attach, adap->fe, + &adap->channel.hdw->i2c_adap, 0x42, + &tda829x_no_probe); + dvb_attach(tda18271_attach, adap->fe, 0x60, + &adap->channel.hdw->i2c_adap, + &hauppauge_tda18271_config); + + return 0; +} + +struct pvr2_dvb_props pvr2_750xx_dvb_props = { + .frontend_attach = pvr2_s5h1409_attach, + .tuner_attach = pvr2_tda18271_8295_attach, +}; +#endif + static const char *pvr2_client_75xxx[] = { "cx25840", "tuner", @@ -240,9 +417,30 @@ static const char *pvr2_fw1_names_75xxx[] = { "v4l-pvrusb2-73xxx-01.fw", }; -static const struct pvr2_device_desc pvr2_device_75xxx = { - .description = "WinTV PVR USB2 Model Category 75xxxx", - .shortname = "75xxx", +static const struct pvr2_device_desc pvr2_device_750xx = { + .description = "WinTV PVR USB2 Model Category 750xx", + .shortname = "750xx", + .client_modules.lst = pvr2_client_75xxx, + .client_modules.cnt = ARRAY_SIZE(pvr2_client_75xxx), + .fx2_firmware.lst = pvr2_fw1_names_75xxx, + .fx2_firmware.cnt = ARRAY_SIZE(pvr2_fw1_names_75xxx), + .flag_has_cx25840 = !0, + .flag_has_hauppauge_rom = !0, + .flag_has_analogtuner = !0, + .flag_has_composite = !0, + .flag_has_svideo = !0, + .signal_routing_scheme = PVR2_ROUTING_SCHEME_HAUPPAUGE, + .digital_control_scheme = PVR2_DIGITAL_SCHEME_HAUPPAUGE, + .default_std_mask = V4L2_STD_NTSC_M, + .led_scheme = PVR2_LED_SCHEME_HAUPPAUGE, +#ifdef CONFIG_VIDEO_PVRUSB2_DVB + .dvb_props = &pvr2_750xx_dvb_props, +#endif +}; + +static const struct pvr2_device_desc pvr2_device_751xx = { + .description = "WinTV PVR USB2 Model Category 751xx", + .shortname = "751xx", .client_modules.lst = pvr2_client_75xxx, .client_modules.cnt = ARRAY_SIZE(pvr2_client_75xxx), .fx2_firmware.lst = pvr2_fw1_names_75xxx, @@ -282,9 +480,9 @@ struct usb_device_id pvr2_device_table[] = { { USB_DEVICE(0x2040, 0x7300), .driver_info = (kernel_ulong_t)&pvr2_device_73xxx}, { USB_DEVICE(0x2040, 0x7500), - .driver_info = (kernel_ulong_t)&pvr2_device_75xxx}, + .driver_info = (kernel_ulong_t)&pvr2_device_750xx}, { USB_DEVICE(0x2040, 0x7501), - .driver_info = (kernel_ulong_t)&pvr2_device_75xxx}, + .driver_info = (kernel_ulong_t)&pvr2_device_751xx}, { } }; diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-devattr.h b/linux/drivers/media/video/pvrusb2/pvrusb2-devattr.h index fb5f5d17e..c2e2b06fe 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-devattr.h +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-devattr.h @@ -23,6 +23,9 @@ #include <linux/mod_devicetable.h> #include <linux/videodev2.h> +#ifdef CONFIG_VIDEO_PVRUSB2_DVB +#include "pvrusb2-dvb.h" +#endif /* @@ -65,6 +68,11 @@ struct pvr2_device_desc { was initialized from internal ROM. */ struct pvr2_string_table fx2_firmware; +#ifdef CONFIG_VIDEO_PVRUSB2_DVB + /* callback functions to handle attachment of digital tuner & demod */ + struct pvr2_dvb_props *dvb_props; + +#endif /* Initial standard bits to use for this device, if not zero. Anything set here is also implied as an available standard. Note: This is ignored if overridden on the module load line via @@ -98,6 +106,13 @@ struct pvr2_device_desc { /* If set, we don't bother trying to load cx23416 firmware. */ int flag_skip_cx23416_firmware:1; + /* If set, the encoder must be healthy in order for digital mode to + work (otherwise we assume that digital streaming will work even + if we fail to locate firmware for the encoder). If the device + doesn't support digital streaming then this flag has no + effect. */ + int flag_digital_requires_cx23416:1; + /* Device has a hauppauge eeprom which we can interrogate. */ int flag_has_hauppauge_rom:1; diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-dvb.c b/linux/drivers/media/video/pvrusb2/pvrusb2-dvb.c new file mode 100644 index 000000000..3df569d7a --- /dev/null +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-dvb.c @@ -0,0 +1,430 @@ +/* + * pvrusb2-dvb.c - linux-dvb api interface to the pvrusb2 driver. + * + * Copyright (C) 2007, 2008 Michael Krufky <mkrufky@linuxtv.org> + * + * 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 + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include <linux/kthread.h> +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) +#include <linux/suspend.h> +#else +#include <linux/freezer.h> +#endif +#include "compat.h" +#include "dvbdev.h" +#include "pvrusb2-hdw-internal.h" +#include "pvrusb2-hdw.h" +#include "pvrusb2-io.h" +#include "pvrusb2-dvb.h" + +DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); + +static int pvr2_dvb_feed_func(struct pvr2_dvb_adapter *adap) +{ + int ret; + unsigned int count; + struct pvr2_buffer *bp; + struct pvr2_stream *stream; + + printk(KERN_DEBUG "dvb thread started\n"); + set_freezable(); + + stream = adap->channel.stream->stream; + + for (;;) { + if (kthread_should_stop()) break; + + /* Not sure about this... */ + try_to_freeze(); + + bp = pvr2_stream_get_ready_buffer(stream); + if (bp != NULL) { + count = pvr2_buffer_get_count(bp); + if (count) { + dvb_dmx_swfilter( + &adap->demux, + adap->buffer_storage[ + pvr2_buffer_get_id(bp)], + count); + } else { + ret = pvr2_buffer_get_status(bp); + if (ret < 0) break; + } + ret = pvr2_buffer_queue(bp); + if (ret < 0) break; + + /* Since we know we did something to a buffer, + just go back and try again. No point in + blocking unless we really ran out of + buffers to process. */ + continue; + } + + + /* Wait until more buffers become available or we're + told not to wait any longer. */ + ret = wait_event_interruptible( + adap->buffer_wait_data, + (pvr2_stream_get_ready_count(stream) > 0) || + kthread_should_stop()); + if (ret < 0) break; + } + + /* If we get here and ret is < 0, then an error has occurred. + Probably would be a good idea to communicate that to DVB core... */ + + printk(KERN_DEBUG "dvb thread stopped\n"); + + return 0; +} + +static int pvr2_dvb_feed_thread(void *data) +{ + int stat = pvr2_dvb_feed_func(data); + /* from videobuf-dvb.c: */ + while (!kthread_should_stop()) { + set_current_state(TASK_INTERRUPTIBLE); + schedule(); + } + return stat; +} + +static void pvr2_dvb_notify(struct pvr2_dvb_adapter *adap) +{ + wake_up(&adap->buffer_wait_data); +} + +static void pvr2_dvb_stream_end(struct pvr2_dvb_adapter *adap) +{ + unsigned int idx; + struct pvr2_stream *stream; + + if (adap->thread) { + kthread_stop(adap->thread); + adap->thread = NULL; + } + + if (adap->channel.stream) { + stream = adap->channel.stream->stream; + } else { + stream = NULL; + } + if (stream) { + pvr2_hdw_set_streaming(adap->channel.hdw, 0); + pvr2_stream_set_callback(stream, NULL, NULL); + pvr2_stream_kill(stream); + pvr2_stream_set_buffer_count(stream, 0); + pvr2_channel_claim_stream(&adap->channel, NULL); + } + + if (adap->stream_run) { + for (idx = 0; idx < PVR2_DVB_BUFFER_COUNT; idx++) { + if (!(adap->buffer_storage[idx])) continue; + kfree(adap->buffer_storage[idx]); + adap->buffer_storage[idx] = 0; + } + adap->stream_run = 0; + } +} + +static int pvr2_dvb_stream_do_start(struct pvr2_dvb_adapter *adap) +{ + struct pvr2_context *pvr = adap->channel.mc_head; + unsigned int idx; + int ret; + struct pvr2_buffer *bp; + struct pvr2_stream *stream = 0; + + if (adap->stream_run) return -EIO; + + ret = pvr2_channel_claim_stream(&adap->channel, &pvr->video_stream); + /* somebody else already has the stream */ + if (ret < 0) return ret; + + stream = adap->channel.stream->stream; + + for (idx = 0; idx < PVR2_DVB_BUFFER_COUNT; idx++) { + adap->buffer_storage[idx] = kmalloc(PVR2_DVB_BUFFER_SIZE, + GFP_KERNEL); + if (!(adap->buffer_storage[idx])) return -ENOMEM; + } + + pvr2_stream_set_callback(pvr->video_stream.stream, + (pvr2_stream_callback) pvr2_dvb_notify, adap); + + ret = pvr2_stream_set_buffer_count(stream, PVR2_DVB_BUFFER_COUNT); + if (ret < 0) return ret; + + for (idx = 0; idx < PVR2_DVB_BUFFER_COUNT; idx++) { + bp = pvr2_stream_get_buffer(stream, idx); + pvr2_buffer_set_buffer(bp, + adap->buffer_storage[idx], + PVR2_DVB_BUFFER_SIZE); + } + + ret = pvr2_hdw_set_streaming(adap->channel.hdw, 1); + if (ret < 0) return ret; + + while ((bp = pvr2_stream_get_idle_buffer(stream)) != 0) { + ret = pvr2_buffer_queue(bp); + if (ret < 0) return ret; + } + + adap->thread = kthread_run(pvr2_dvb_feed_thread, adap, "pvrusb2-dvb"); + + if (IS_ERR(adap->thread)) { + ret = PTR_ERR(adap->thread); + adap->thread = NULL; + return ret; + } + + adap->stream_run = !0; + + return 0; +} + +static int pvr2_dvb_stream_start(struct pvr2_dvb_adapter *adap) +{ + int ret = pvr2_dvb_stream_do_start(adap); + if (ret < 0) pvr2_dvb_stream_end(adap); + return ret; +} + +static int pvr2_dvb_ctrl_feed(struct dvb_demux_feed *dvbdmxfeed, int onoff) +{ + struct pvr2_dvb_adapter *adap = dvbdmxfeed->demux->priv; + int ret = 0; + + if (adap == NULL) return -ENODEV; + + mutex_lock(&adap->lock); + do { + if (onoff) { + if (!adap->feedcount) { + printk(KERN_DEBUG "start feeding\n"); + ret = pvr2_dvb_stream_start(adap); + if (ret < 0) break; + } + (adap->feedcount)++; + } else if (adap->feedcount > 0) { + (adap->feedcount)--; + if (!adap->feedcount) { + printk(KERN_DEBUG "stop feeding\n"); + pvr2_dvb_stream_end(adap); + } + } + } while (0); + mutex_unlock(&adap->lock); + + return ret; +} + +static int pvr2_dvb_start_feed(struct dvb_demux_feed *dvbdmxfeed) +{ + printk(KERN_DEBUG "start pid: 0x%04x, feedtype: %d\n", + dvbdmxfeed->pid, dvbdmxfeed->type); + return pvr2_dvb_ctrl_feed(dvbdmxfeed, 1); +} + +static int pvr2_dvb_stop_feed(struct dvb_demux_feed *dvbdmxfeed) +{ + printk(KERN_DEBUG "stop pid: 0x%04x, feedtype: %d\n", + dvbdmxfeed->pid, dvbdmxfeed->type); + return pvr2_dvb_ctrl_feed(dvbdmxfeed, 0); +} + +static int pvr2_dvb_bus_ctrl(struct dvb_frontend *fe, int acquire) +{ + struct pvr2_dvb_adapter *adap = fe->dvb->priv; + return pvr2_channel_limit_inputs( + &adap->channel, + (acquire ? (1 << PVR2_CVAL_INPUT_DTV) : 0)); +} + +static int pvr2_dvb_adapter_init(struct pvr2_dvb_adapter *adap) +{ + int ret; + + ret = dvb_register_adapter(&adap->dvb_adap, "pvrusb2-dvb", + THIS_MODULE/*&hdw->usb_dev->owner*/, + &adap->channel.hdw->usb_dev->dev, + adapter_nr); + if (ret < 0) { + err("dvb_register_adapter failed: error %d", ret); + goto err; + } + adap->dvb_adap.priv = adap; + + adap->demux.dmx.capabilities = DMX_TS_FILTERING | + DMX_SECTION_FILTERING | + DMX_MEMORY_BASED_FILTERING; + adap->demux.priv = adap; + adap->demux.filternum = 256; + adap->demux.feednum = 256; + adap->demux.start_feed = pvr2_dvb_start_feed; + adap->demux.stop_feed = pvr2_dvb_stop_feed; + adap->demux.write_to_decoder = NULL; + + ret = dvb_dmx_init(&adap->demux); + if (ret < 0) { + err("dvb_dmx_init failed: error %d", ret); + goto err_dmx; + } + + adap->dmxdev.filternum = adap->demux.filternum; + adap->dmxdev.demux = &adap->demux.dmx; + adap->dmxdev.capabilities = 0; + + ret = dvb_dmxdev_init(&adap->dmxdev, &adap->dvb_adap); + if (ret < 0) { + err("dvb_dmxdev_init failed: error %d", ret); + goto err_dmx_dev; + } + + dvb_net_init(&adap->dvb_adap, &adap->dvb_net, &adap->demux.dmx); + + return 0; + +err_dmx_dev: + dvb_dmx_release(&adap->demux); +err_dmx: + dvb_unregister_adapter(&adap->dvb_adap); +err: + return ret; +} + +static int pvr2_dvb_adapter_exit(struct pvr2_dvb_adapter *adap) +{ + printk(KERN_DEBUG "unregistering DVB devices\n"); + dvb_net_release(&adap->dvb_net); + adap->demux.dmx.close(&adap->demux.dmx); + dvb_dmxdev_release(&adap->dmxdev); + dvb_dmx_release(&adap->demux); + dvb_unregister_adapter(&adap->dvb_adap); + return 0; +} + +static int pvr2_dvb_frontend_init(struct pvr2_dvb_adapter *adap) +{ + struct pvr2_hdw *hdw = adap->channel.hdw; + struct pvr2_dvb_props *dvb_props = hdw->hdw_desc->dvb_props; + int ret = 0; + + if (dvb_props == NULL) { + err("fe_props not defined!"); + return -EINVAL; + } + + ret = pvr2_channel_limit_inputs( + &adap->channel, + (1 << PVR2_CVAL_INPUT_DTV)); + if (ret) { + err("failed to grab control of dtv input (code=%d)", + ret); + return ret; + } + + if (dvb_props->frontend_attach == NULL) { + err("frontend_attach not defined!"); + ret = -EINVAL; + goto done; + } + + if ((dvb_props->frontend_attach(adap) == 0) && (adap->fe)) { + + if (dvb_register_frontend(&adap->dvb_adap, adap->fe)) { + err("frontend registration failed!"); + dvb_frontend_detach(adap->fe); + adap->fe = NULL; + ret = -ENODEV; + goto done; + } + + if (dvb_props->tuner_attach) + dvb_props->tuner_attach(adap); + + if (adap->fe->ops.analog_ops.standby) + adap->fe->ops.analog_ops.standby(adap->fe); + + /* Ensure all frontends negotiate bus access */ + adap->fe->ops.ts_bus_ctrl = pvr2_dvb_bus_ctrl; + + } else { + err("no frontend was attached!"); + ret = -ENODEV; + return ret; + } + + done: + pvr2_channel_limit_inputs(&adap->channel, 0); + return ret; +} + +static int pvr2_dvb_frontend_exit(struct pvr2_dvb_adapter *adap) +{ + if (adap->fe != NULL) { + dvb_unregister_frontend(adap->fe); + dvb_frontend_detach(adap->fe); + } + return 0; +} + +static void pvr2_dvb_destroy(struct pvr2_dvb_adapter *adap) +{ + pvr2_dvb_stream_end(adap); + pvr2_dvb_frontend_exit(adap); + pvr2_dvb_adapter_exit(adap); + pvr2_channel_done(&adap->channel); + kfree(adap); +} + +static void pvr2_dvb_internal_check(struct pvr2_channel *chp) +{ + struct pvr2_dvb_adapter *adap; + adap = container_of(chp, struct pvr2_dvb_adapter, channel); + if (!adap->channel.mc_head->disconnect_flag) return; + pvr2_dvb_destroy(adap); +} + +struct pvr2_dvb_adapter *pvr2_dvb_create(struct pvr2_context *pvr) +{ + int ret = 0; + struct pvr2_dvb_adapter *adap; + if (!pvr->hdw->hdw_desc->dvb_props) { + /* Device lacks a digital interface so don't set up + the DVB side of the driver either. For now. */ + return NULL; + } + adap = kzalloc(sizeof(*adap), GFP_KERNEL); + if (!adap) return adap; + pvr2_channel_init(&adap->channel, pvr); + adap->channel.check_func = pvr2_dvb_internal_check; + init_waitqueue_head(&adap->buffer_wait_data); + mutex_init(&adap->lock); + ret = pvr2_dvb_adapter_init(adap); + if (ret < 0) goto fail1; + ret = pvr2_dvb_frontend_init(adap); + if (ret < 0) goto fail2; + return adap; + +fail2: + pvr2_dvb_adapter_exit(adap); +fail1: + pvr2_channel_done(&adap->channel); + return NULL; +} + diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-dvb.h b/linux/drivers/media/video/pvrusb2/pvrusb2-dvb.h new file mode 100644 index 000000000..04209db87 --- /dev/null +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-dvb.h @@ -0,0 +1,46 @@ +#ifndef __PVRUSB2_DVB_H__ +#define __PVRUSB2_DVB_H__ + +#include "dvb_frontend.h" +#include "dvb_demux.h" +#include "dvb_net.h" +#include "dmxdev.h" +#include "pvrusb2-context.h" + +#define PVR2_DVB_BUFFER_COUNT 32 +#define PVR2_DVB_BUFFER_SIZE PAGE_ALIGN(0x4000) + +struct pvr2_dvb_adapter { + struct pvr2_channel channel; + + struct dvb_adapter dvb_adap; + struct dmxdev dmxdev; + struct dvb_demux demux; + struct dvb_net dvb_net; + struct dvb_frontend *fe; + + int feedcount; + int max_feed_count; + + struct task_struct *thread; +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,15) + struct mutex lock; +#else + struct semaphore lock; +#endif + + unsigned int stream_run:1; + + wait_queue_head_t buffer_wait_data; + char *buffer_storage[PVR2_DVB_BUFFER_COUNT]; +}; + +struct pvr2_dvb_props { + int (*frontend_attach) (struct pvr2_dvb_adapter *); + int (*tuner_attach) (struct pvr2_dvb_adapter *); +}; + +struct pvr2_dvb_adapter *pvr2_dvb_create(struct pvr2_context *pvr); + +#endif /* __PVRUSB2_DVB_H__ */ + diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-encoder.c b/linux/drivers/media/video/pvrusb2/pvrusb2-encoder.c index 70a2bc251..c388a341f 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-encoder.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-encoder.c @@ -300,11 +300,20 @@ static int pvr2_encoder_cmd(void *ctxt, ret = -EBUSY; } if (ret) { + del_timer_sync(&hdw->encoder_run_timer); hdw->state_encoder_ok = 0; pvr2_trace(PVR2_TRACE_STBITS, "State bit %s <-- %s", "state_encoder_ok", (hdw->state_encoder_ok ? "true" : "false")); + if (hdw->state_encoder_runok) { + hdw->state_encoder_runok = 0; + pvr2_trace(PVR2_TRACE_STBITS, + "State bit %s <-- %s", + "state_encoder_runok", + (hdw->state_encoder_runok ? + "true" : "false")); + } pvr2_trace( PVR2_TRACE_ERROR_LEGS, "Giving up on command." diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-fx2-cmd.h b/linux/drivers/media/video/pvrusb2/pvrusb2-fx2-cmd.h index a866c9492..abaada31e 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-fx2-cmd.h +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-fx2-cmd.h @@ -22,41 +22,41 @@ #ifndef _PVRUSB2_FX2_CMD_H_ #define _PVRUSB2_FX2_CMD_H_ -#define FX2CMD_MEM_WRITE_DWORD 0x01 -#define FX2CMD_MEM_READ_DWORD 0x02 +#define FX2CMD_MEM_WRITE_DWORD 0x01u +#define FX2CMD_MEM_READ_DWORD 0x02u -#define FX2CMD_MEM_READ_64BYTES 0x28 +#define FX2CMD_MEM_READ_64BYTES 0x28u -#define FX2CMD_REG_WRITE 0x04 -#define FX2CMD_REG_READ 0x05 -#define FX2CMD_MEMSEL 0x06 +#define FX2CMD_REG_WRITE 0x04u +#define FX2CMD_REG_READ 0x05u +#define FX2CMD_MEMSEL 0x06u -#define FX2CMD_I2C_WRITE 0x08 -#define FX2CMD_I2C_READ 0x09 +#define FX2CMD_I2C_WRITE 0x08u +#define FX2CMD_I2C_READ 0x09u -#define FX2CMD_GET_USB_SPEED 0x0b +#define FX2CMD_GET_USB_SPEED 0x0bu -#define FX2CMD_STREAMING_ON 0x36 -#define FX2CMD_STREAMING_OFF 0x37 +#define FX2CMD_STREAMING_ON 0x36u +#define FX2CMD_STREAMING_OFF 0x37u -#define FX2CMD_FWPOST1 0x52 +#define FX2CMD_FWPOST1 0x52u -#define FX2CMD_POWER_OFF 0xdc -#define FX2CMD_POWER_ON 0xde +#define FX2CMD_POWER_OFF 0xdcu +#define FX2CMD_POWER_ON 0xdeu -#define FX2CMD_DEEP_RESET 0xdd +#define FX2CMD_DEEP_RESET 0xddu -#define FX2CMD_GET_EEPROM_ADDR 0xeb -#define FX2CMD_GET_IR_CODE 0xec +#define FX2CMD_GET_EEPROM_ADDR 0xebu +#define FX2CMD_GET_IR_CODE 0xecu -#define FX2CMD_HCW_DEMOD_RESETIN 0xf0 -#define FX2CMD_HCW_DTV_STREAMING_ON 0xf1 -#define FX2CMD_HCW_DTV_STREAMING_OFF 0xf2 +#define FX2CMD_HCW_DEMOD_RESETIN 0xf0u +#define FX2CMD_HCW_DTV_STREAMING_ON 0xf1u +#define FX2CMD_HCW_DTV_STREAMING_OFF 0xf2u -#define FX2CMD_ONAIR_DTV_STREAMING_ON 0xa0 -#define FX2CMD_ONAIR_DTV_STREAMING_OFF 0xa1 -#define FX2CMD_ONAIR_DTV_POWER_ON 0xa2 -#define FX2CMD_ONAIR_DTV_POWER_OFF 0xa3 +#define FX2CMD_ONAIR_DTV_STREAMING_ON 0xa0u +#define FX2CMD_ONAIR_DTV_STREAMING_OFF 0xa1u +#define FX2CMD_ONAIR_DTV_POWER_ON 0xa2u +#define FX2CMD_ONAIR_DTV_POWER_OFF 0xa3u #endif /* _PVRUSB2_FX2_CMD_H_ */ diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h index 631973652..44e77355f 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h @@ -254,6 +254,7 @@ struct pvr2_hdw { int state_encoder_run; /* Encoder is running */ int state_encoder_config; /* Encoder is configured */ int state_encoder_waitok; /* Encoder pre-wait done */ + int state_encoder_runok; /* Encoder has run for >= .25 sec */ int state_decoder_run; /* Decoder is running */ int state_usbstream_run; /* FX2 is streaming */ int state_decoder_quiescent; /* Decoder idle for > 50msec */ @@ -283,6 +284,9 @@ struct pvr2_hdw { /* Timer for measuring encoder pre-wait time */ struct timer_list encoder_wait_timer; + /* Timer for measuring encoder minimum run time */ + struct timer_list encoder_run_timer; + /* Place to block while waiting for state changes */ wait_queue_head_t state_wait_data; @@ -348,8 +352,10 @@ struct pvr2_hdw { int v4l_minor_number_vbi; int v4l_minor_number_radio; - /* Bit mask of PVR2_CVAL_INPUT choices which are valid */ + /* Bit mask of PVR2_CVAL_INPUT choices which are valid for the hardware */ unsigned int input_avail_mask; + /* Bit mask of PVR2_CVAL_INPUT choices which are currenly allowed */ + unsigned int input_allowed_mask; /* Location of eeprom or a negative number if none */ int eeprom_addr; diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c index 8356bc0e4..5bcf548f9 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c @@ -218,6 +218,40 @@ static const char *pvr2_state_names[] = { }; +struct pvr2_fx2cmd_descdef { + unsigned char id; + unsigned char *desc; +}; + +static const struct pvr2_fx2cmd_descdef pvr2_fx2cmd_desc[] = { + {FX2CMD_MEM_WRITE_DWORD, "write encoder dword"}, + {FX2CMD_MEM_READ_DWORD, "read encoder dword"}, + {FX2CMD_MEM_READ_64BYTES, "read encoder 64bytes"}, + {FX2CMD_REG_WRITE, "write encoder register"}, + {FX2CMD_REG_READ, "read encoder register"}, + {FX2CMD_MEMSEL, "encoder memsel"}, + {FX2CMD_I2C_WRITE, "i2c write"}, + {FX2CMD_I2C_READ, "i2c read"}, + {FX2CMD_GET_USB_SPEED, "get USB speed"}, + {FX2CMD_STREAMING_ON, "stream on"}, + {FX2CMD_STREAMING_OFF, "stream off"}, + {FX2CMD_FWPOST1, "fwpost1"}, + {FX2CMD_POWER_OFF, "power off"}, + {FX2CMD_POWER_ON, "power on"}, + {FX2CMD_DEEP_RESET, "deep reset"}, + {FX2CMD_GET_EEPROM_ADDR, "get rom addr"}, + {FX2CMD_GET_IR_CODE, "get IR code"}, + {FX2CMD_HCW_DEMOD_RESETIN, "hcw demod resetin"}, + {FX2CMD_HCW_DTV_STREAMING_ON, "hcw dtv stream on"}, + {FX2CMD_HCW_DTV_STREAMING_OFF, "hcw dtv stream off"}, + {FX2CMD_ONAIR_DTV_STREAMING_ON, "onair dtv stream on"}, + {FX2CMD_ONAIR_DTV_STREAMING_OFF, "onair dtv stream off"}, + {FX2CMD_ONAIR_DTV_POWER_ON, "onair dtv power on"}, + {FX2CMD_ONAIR_DTV_POWER_OFF, "onair dtv power off"}, +}; + + +static int pvr2_hdw_set_input(struct pvr2_hdw *hdw,int v); static void pvr2_hdw_state_sched(struct pvr2_hdw *); static int pvr2_hdw_state_eval(struct pvr2_hdw *); static void pvr2_hdw_set_cur_freq(struct pvr2_hdw *,unsigned long); @@ -233,6 +267,8 @@ static void pvr2_hdw_internal_find_stdenum(struct pvr2_hdw *hdw); static void pvr2_hdw_internal_set_std_avail(struct pvr2_hdw *hdw); static void pvr2_hdw_quiescent_timeout(unsigned long); static void pvr2_hdw_encoder_wait_timeout(unsigned long); +static void pvr2_hdw_encoder_run_timeout(unsigned long); +static int pvr2_issue_simple_cmd(struct pvr2_hdw *,u32); static int pvr2_send_request_ex(struct pvr2_hdw *hdw, unsigned int timeout,int probe_fl, void *write_data,unsigned int write_len, @@ -371,30 +407,12 @@ static int ctrl_get_input(struct pvr2_ctrl *cptr,int *vp) static int ctrl_check_input(struct pvr2_ctrl *cptr,int v) { - return ((1 << v) & cptr->hdw->input_avail_mask) != 0; + return ((1 << v) & cptr->hdw->input_allowed_mask) != 0; } static int ctrl_set_input(struct pvr2_ctrl *cptr,int m,int v) { - struct pvr2_hdw *hdw = cptr->hdw; - - if (hdw->input_val != v) { - hdw->input_val = v; - hdw->input_dirty = !0; - } - - /* Handle side effects - if we switch to a mode that needs the RF - tuner, then select the right frequency choice as well and mark - it dirty. */ - if (hdw->input_val == PVR2_CVAL_INPUT_RADIO) { - hdw->freqSelector = 0; - hdw->freqDirty = !0; - } else if ((hdw->input_val == PVR2_CVAL_INPUT_TV) || - (hdw->input_val == PVR2_CVAL_INPUT_DTV)) { - hdw->freqSelector = 1; - hdw->freqDirty = !0; - } - return 0; + return pvr2_hdw_set_input(cptr->hdw,v); } static int ctrl_isdirty_input(struct pvr2_ctrl *cptr) @@ -991,7 +1009,7 @@ unsigned long pvr2_hdw_get_cur_freq(struct pvr2_hdw *hdw) /* Set the currently tuned frequency and account for all possible driver-core side effects of this action. */ -void pvr2_hdw_set_cur_freq(struct pvr2_hdw *hdw,unsigned long val) +static void pvr2_hdw_set_cur_freq(struct pvr2_hdw *hdw,unsigned long val) { if (hdw->input_val == PVR2_CVAL_INPUT_RADIO) { if (hdw->freqSelector) { @@ -1213,6 +1231,14 @@ int pvr2_upload_firmware2(struct pvr2_hdw *hdw) time we configure the encoder, then we'll fully configure it. */ hdw->enc_cur_valid = 0; + /* Encoder is about to be reset so note that as far as we're + concerned now, the encoder has never been run. */ + del_timer_sync(&hdw->encoder_run_timer); + if (hdw->state_encoder_runok) { + hdw->state_encoder_runok = 0; + trace_stbit("state_encoder_runok",hdw->state_encoder_runok); + } + /* First prepare firmware loading */ ret |= pvr2_write_register(hdw, 0x0048, 0xffffffff); /*interrupt mask*/ ret |= pvr2_hdw_gpio_chg_dir(hdw,0xffffffff,0x00000088); /*gpio dir*/ @@ -1230,19 +1256,14 @@ int pvr2_upload_firmware2(struct pvr2_hdw *hdw) ret |= pvr2_write_register(hdw, 0xaa04, 0x00057810); /*unknown*/ ret |= pvr2_write_register(hdw, 0xaa10, 0x00148500); /*unknown*/ ret |= pvr2_write_register(hdw, 0xaa18, 0x00840000); /*unknown*/ - LOCK_TAKE(hdw->ctl_lock); do { - hdw->cmd_buffer[0] = FX2CMD_FWPOST1; - ret |= pvr2_send_request(hdw,hdw->cmd_buffer,1,NULL,0); - hdw->cmd_buffer[0] = FX2CMD_MEMSEL; - hdw->cmd_buffer[1] = 0; - ret |= pvr2_send_request(hdw,hdw->cmd_buffer,2,NULL,0); - } while (0); LOCK_GIVE(hdw->ctl_lock); + ret |= pvr2_issue_simple_cmd(hdw,FX2CMD_FWPOST1); + ret |= pvr2_issue_simple_cmd(hdw,FX2CMD_MEMSEL | (1 << 8) | (0 << 16)); if (ret) { pvr2_trace(PVR2_TRACE_ERROR_LEGS, "firmware2 upload prep failed, ret=%d",ret); release_firmware(fw_entry); - return ret; + goto done; } /* Now send firmware */ @@ -1255,7 +1276,8 @@ int pvr2_upload_firmware2(struct pvr2_hdw *hdw) " must be a multiple of %zu bytes", fw_files[fwidx],sizeof(u32)); release_firmware(fw_entry); - return -1; + ret = -EINVAL; + goto done; } fw_ptr = kmalloc(FIRMWARE_CHUNK_SIZE, GFP_KERNEL); @@ -1263,7 +1285,8 @@ int pvr2_upload_firmware2(struct pvr2_hdw *hdw) release_firmware(fw_entry); pvr2_trace(PVR2_TRACE_ERROR_LEGS, "failed to allocate memory for firmware2 upload"); - return -ENOMEM; + ret = -ENOMEM; + goto done; } pipe = usb_sndbulkpipe(hdw->usb_dev, PVR2_FIRMWARE_ENDPOINT); @@ -1294,23 +1317,27 @@ int pvr2_upload_firmware2(struct pvr2_hdw *hdw) if (ret) { pvr2_trace(PVR2_TRACE_ERROR_LEGS, "firmware2 upload transfer failure"); - return ret; + goto done; } /* Finish upload */ ret |= pvr2_write_register(hdw, 0x9054, 0xffffffff); /*reset hw blocks*/ ret |= pvr2_write_register(hdw, 0x9058, 0xffffffe8); /*VPU ctrl*/ - LOCK_TAKE(hdw->ctl_lock); do { - hdw->cmd_buffer[0] = FX2CMD_MEMSEL; - hdw->cmd_buffer[1] = 0; - ret |= pvr2_send_request(hdw,hdw->cmd_buffer,2,NULL,0); - } while (0); LOCK_GIVE(hdw->ctl_lock); + ret |= pvr2_issue_simple_cmd(hdw,FX2CMD_MEMSEL | (1 << 8) | (0 << 16)); if (ret) { pvr2_trace(PVR2_TRACE_ERROR_LEGS, "firmware2 upload post-proc failure"); } + + done: + if (hdw->hdw_desc->signal_routing_scheme == + PVR2_ROUTING_SCHEME_GOTVIEW) { + /* Ensure that GPIO 11 is set to output for GOTVIEW + hardware. */ + pvr2_hdw_gpio_chg_dir(hdw,(1 << 11),~0); + } return ret; } @@ -1382,11 +1409,13 @@ int pvr2_hdw_untrip(struct pvr2_hdw *hdw) } +#if 0 const char *pvr2_hdw_get_state_name(unsigned int id) { if (id >= ARRAY_SIZE(pvr2_state_names)) return NULL; return pvr2_state_names[id]; } +#endif /* 0 */ int pvr2_hdw_get_streaming(struct pvr2_hdw *hdw) @@ -1730,6 +1759,13 @@ static void pvr2_hdw_setup_low(struct pvr2_hdw *hdw) if (!pvr2_hdw_dev_ok(hdw)) return; + if (hdw->hdw_desc->signal_routing_scheme == + PVR2_ROUTING_SCHEME_GOTVIEW) { + /* Ensure that GPIO 11 is set to output for GOTVIEW + hardware. */ + pvr2_hdw_gpio_chg_dir(hdw,(1 << 11),~0); + } + pvr2_hdw_commit_setup(hdw); hdw->vid_stream = pvr2_stream_create(); @@ -1831,10 +1867,19 @@ int pvr2_hdw_initialize(struct pvr2_hdw *hdw, void *callback_data) { LOCK_TAKE(hdw->big_lock); do { + if (hdw->flag_disconnected) { + /* Handle a race here: If we're already + disconnected by this point, then give up. If we + get past this then we'll remain connected for + the duration of initialization since the entire + initialization sequence is now protected by the + big_lock. */ + break; + } hdw->state_data = callback_data; hdw->state_func = callback_func; + pvr2_hdw_setup(hdw); } while (0); LOCK_GIVE(hdw->big_lock); - pvr2_hdw_setup(hdw); return hdw->flag_init_ok; } @@ -1868,6 +1913,10 @@ struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf, hdw->encoder_wait_timer.data = (unsigned long)hdw; hdw->encoder_wait_timer.function = pvr2_hdw_encoder_wait_timeout; + init_timer(&hdw->encoder_run_timer); + hdw->encoder_run_timer.data = (unsigned long)hdw; + hdw->encoder_run_timer.function = pvr2_hdw_encoder_run_timeout; + hdw->master_state = PVR2_STATE_DEAD; init_waitqueue_head(&hdw->state_wait_data); @@ -1885,6 +1934,7 @@ struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf, if (hdw_desc->flag_has_composite) m |= 1 << PVR2_CVAL_INPUT_COMPOSITE; if (hdw_desc->flag_has_fmradio) m |= 1 << PVR2_CVAL_INPUT_RADIO; hdw->input_avail_mask = m; + hdw->input_allowed_mask = hdw->input_avail_mask; /* If not a hybrid device, pathway_state never changes. So initialize it here to what it should forever be. */ @@ -2075,6 +2125,7 @@ struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf, fail: if (hdw) { del_timer_sync(&hdw->quiescent_timer); + del_timer_sync(&hdw->encoder_run_timer); del_timer_sync(&hdw->encoder_wait_timer); if (hdw->workqueue) { flush_workqueue(hdw->workqueue); @@ -2137,6 +2188,7 @@ void pvr2_hdw_destroy(struct pvr2_hdw *hdw) hdw->workqueue = NULL; } del_timer_sync(&hdw->quiescent_timer); + del_timer_sync(&hdw->encoder_run_timer); del_timer_sync(&hdw->encoder_wait_timer); if (hdw->fw_buffer) { kfree(hdw->fw_buffer); @@ -2419,7 +2471,7 @@ static int pvr2_hdw_commit_execute(struct pvr2_hdw *hdw) } } - if (hdw->input_dirty && + if (hdw->input_dirty && hdw->state_pathway_ok && (((hdw->input_val == PVR2_CVAL_INPUT_DTV) ? PVR2_PATHWAY_DIGITAL : PVR2_PATHWAY_ANALOG) != hdw->pathway_state)) { @@ -2484,6 +2536,20 @@ static int pvr2_hdw_commit_execute(struct pvr2_hdw *hdw) hdw->active_stream_type = hdw->desired_stream_type; } + if (hdw->hdw_desc->signal_routing_scheme == + PVR2_ROUTING_SCHEME_GOTVIEW) { + u32 b; + /* Handle GOTVIEW audio switching */ + pvr2_hdw_gpio_get_out(hdw,&b); + if (hdw->input_val == PVR2_CVAL_INPUT_RADIO) { + /* Set GPIO 11 */ + pvr2_hdw_gpio_chg_out(hdw,(1 << 11),~0); + } else { + /* Clear GPIO 11 */ + pvr2_hdw_gpio_chg_out(hdw,(1 << 11),0); + } + } + /* Now execute i2c core update */ pvr2_i2c_core_sync(hdw); @@ -3185,6 +3251,67 @@ int pvr2_send_request(struct pvr2_hdw *hdw, read_data,read_len); } + +static int pvr2_issue_simple_cmd(struct pvr2_hdw *hdw,u32 cmdcode) +{ + int ret; + unsigned int cnt = 1; + unsigned int args = 0; + LOCK_TAKE(hdw->ctl_lock); + hdw->cmd_buffer[0] = cmdcode & 0xffu; + args = (cmdcode >> 8) & 0xffu; + args = (args > 2) ? 2 : args; + if (args) { + cnt += args; + hdw->cmd_buffer[1] = (cmdcode >> 16) & 0xffu; + if (args > 1) { + hdw->cmd_buffer[2] = (cmdcode >> 24) & 0xffu; + } + } + if (pvrusb2_debug & PVR2_TRACE_INIT) { + unsigned int idx; + unsigned int ccnt,bcnt; + char tbuf[50]; + cmdcode &= 0xffu; + bcnt = 0; + ccnt = scnprintf(tbuf+bcnt, + sizeof(tbuf)-bcnt, + "Sending FX2 command 0x%x",cmdcode); + bcnt += ccnt; + for (idx = 0; idx < ARRAY_SIZE(pvr2_fx2cmd_desc); idx++) { + if (pvr2_fx2cmd_desc[idx].id == cmdcode) { + ccnt = scnprintf(tbuf+bcnt, + sizeof(tbuf)-bcnt, + " \"%s\"", + pvr2_fx2cmd_desc[idx].desc); + bcnt += ccnt; + break; + } + } + if (args) { + ccnt = scnprintf(tbuf+bcnt, + sizeof(tbuf)-bcnt, + " (%u",hdw->cmd_buffer[1]); + bcnt += ccnt; + if (args > 1) { + ccnt = scnprintf(tbuf+bcnt, + sizeof(tbuf)-bcnt, + ",%u",hdw->cmd_buffer[2]); + bcnt += ccnt; + } + ccnt = scnprintf(tbuf+bcnt, + sizeof(tbuf)-bcnt, + ")"); + bcnt += ccnt; + } + pvr2_trace(PVR2_TRACE_INIT,"%.*s",bcnt,tbuf); + } + ret = pvr2_send_request(hdw,hdw->cmd_buffer,cnt,NULL,0); + LOCK_GIVE(hdw->ctl_lock); + return ret; +} + + int pvr2_write_register(struct pvr2_hdw *hdw, u16 reg, u32 data) { int ret; @@ -3292,40 +3419,19 @@ void pvr2_hdw_cpureset_assert(struct pvr2_hdw *hdw,int val) int pvr2_hdw_cmd_deep_reset(struct pvr2_hdw *hdw) { - int status; - LOCK_TAKE(hdw->ctl_lock); do { - pvr2_trace(PVR2_TRACE_INIT,"Requesting uproc hard reset"); - hdw->cmd_buffer[0] = FX2CMD_DEEP_RESET; - status = pvr2_send_request(hdw,hdw->cmd_buffer,1,NULL,0); - } while (0); LOCK_GIVE(hdw->ctl_lock); - return status; + return pvr2_issue_simple_cmd(hdw,FX2CMD_DEEP_RESET); } -static int pvr2_hdw_cmd_power_ctrl(struct pvr2_hdw *hdw, int onoff) -{ - int status; - LOCK_TAKE(hdw->ctl_lock); do { - if (onoff) { - pvr2_trace(PVR2_TRACE_INIT, "Requesting powerup"); - hdw->cmd_buffer[0] = FX2CMD_POWER_ON; - } else { - pvr2_trace(PVR2_TRACE_INIT, "Requesting powerdown"); - hdw->cmd_buffer[0] = FX2CMD_POWER_OFF; - } - status = pvr2_send_request(hdw, hdw->cmd_buffer, 1, NULL, 0); - } while (0); LOCK_GIVE(hdw->ctl_lock); - return status; -} - int pvr2_hdw_cmd_powerup(struct pvr2_hdw *hdw) { - return pvr2_hdw_cmd_power_ctrl(hdw, 1); + return pvr2_issue_simple_cmd(hdw,FX2CMD_POWER_ON); } + int pvr2_hdw_cmd_powerdown(struct pvr2_hdw *hdw) { - return pvr2_hdw_cmd_power_ctrl(hdw, 0); + return pvr2_issue_simple_cmd(hdw,FX2CMD_POWER_OFF); } @@ -3352,55 +3458,29 @@ int pvr2_hdw_cmd_decoder_reset(struct pvr2_hdw *hdw) static int pvr2_hdw_cmd_hcw_demod_reset(struct pvr2_hdw *hdw, int onoff) { - int status; - - LOCK_TAKE(hdw->ctl_lock); do { - pvr2_trace(PVR2_TRACE_INIT, - "Issuing fe demod wake command (%s)", - (onoff ? "on" : "off")); - hdw->flag_ok = !0; - hdw->cmd_buffer[0] = FX2CMD_HCW_DEMOD_RESETIN; - hdw->cmd_buffer[1] = onoff; - status = pvr2_send_request(hdw, hdw->cmd_buffer, 2, NULL, 0); - } while (0); LOCK_GIVE(hdw->ctl_lock); - - return status; + hdw->flag_ok = !0; + return pvr2_issue_simple_cmd(hdw, + FX2CMD_HCW_DEMOD_RESETIN | + (1 << 8) | + ((onoff ? 1 : 0) << 16)); } static int pvr2_hdw_cmd_onair_fe_power_ctrl(struct pvr2_hdw *hdw, int onoff) { - int status; - - LOCK_TAKE(hdw->ctl_lock); do { - pvr2_trace(PVR2_TRACE_INIT, - "Issuing fe power command to CPLD (%s)", - (onoff ? "on" : "off")); - hdw->flag_ok = !0; - hdw->cmd_buffer[0] = - (onoff ? FX2CMD_ONAIR_DTV_POWER_ON : - FX2CMD_ONAIR_DTV_POWER_OFF); - status = pvr2_send_request(hdw, hdw->cmd_buffer, 1, NULL, 0); - } while (0); LOCK_GIVE(hdw->ctl_lock); - - return status; + hdw->flag_ok = !0; + return pvr2_issue_simple_cmd(hdw,(onoff ? + FX2CMD_ONAIR_DTV_POWER_ON : + FX2CMD_ONAIR_DTV_POWER_OFF)); } static int pvr2_hdw_cmd_onair_digital_path_ctrl(struct pvr2_hdw *hdw, int onoff) { - int status; - LOCK_TAKE(hdw->ctl_lock); do { - pvr2_trace(PVR2_TRACE_INIT, - "Issuing onair digital setup command (%s)", - (onoff ? "on" : "off")); - hdw->cmd_buffer[0] = - (onoff ? FX2CMD_ONAIR_DTV_STREAMING_ON : - FX2CMD_ONAIR_DTV_STREAMING_OFF); - status = pvr2_send_request(hdw, hdw->cmd_buffer, 1, NULL, 0); - } while (0); LOCK_GIVE(hdw->ctl_lock); - return status; + return pvr2_issue_simple_cmd(hdw,(onoff ? + FX2CMD_ONAIR_DTV_STREAMING_ON : + FX2CMD_ONAIR_DTV_STREAMING_OFF)); } @@ -3430,9 +3510,7 @@ static void pvr2_hdw_cmd_modeswitch(struct pvr2_hdw *hdw,int digitalFl) /* Supposedly we should always have the power on whether in digital or analog mode. But for now do what appears to work... */ - if (digitalFl) pvr2_hdw_cmd_onair_fe_power_ctrl(hdw,!0); - pvr2_hdw_cmd_onair_digital_path_ctrl(hdw,digitalFl); - if (!digitalFl) pvr2_hdw_cmd_onair_fe_power_ctrl(hdw,0); + pvr2_hdw_cmd_onair_fe_power_ctrl(hdw,digitalFl); break; default: break; } @@ -3490,24 +3568,43 @@ static void pvr2_led_ctrl(struct pvr2_hdw *hdw,int onoff) /* Stop / start video stream transport */ static int pvr2_hdw_cmd_usbstream(struct pvr2_hdw *hdw,int runFl) { - int status,cc; - if ((hdw->pathway_state == PVR2_PATHWAY_DIGITAL) && - hdw->hdw_desc->digital_control_scheme == - PVR2_DIGITAL_SCHEME_HAUPPAUGE) { - cc = (runFl ? - FX2CMD_HCW_DTV_STREAMING_ON : - FX2CMD_HCW_DTV_STREAMING_OFF); - } else { - cc = (runFl ? - FX2CMD_STREAMING_ON : - FX2CMD_STREAMING_OFF); + int ret; + + /* If we're in analog mode, then just issue the usual analog + command. */ + if (hdw->pathway_state == PVR2_PATHWAY_ANALOG) { + return pvr2_issue_simple_cmd(hdw, + (runFl ? + FX2CMD_STREAMING_ON : + FX2CMD_STREAMING_OFF)); + /*Note: Not reached */ } - LOCK_TAKE(hdw->ctl_lock); do { - hdw->cmd_buffer[0] = cc; - status = pvr2_send_request(hdw,hdw->cmd_buffer,1,NULL,0); - } while (0); LOCK_GIVE(hdw->ctl_lock); - return status; + if (hdw->pathway_state != PVR2_PATHWAY_DIGITAL) { + /* Whoops, we don't know what mode we're in... */ + return -EINVAL; + } + + /* To get here we have to be in digital mode. The mechanism here + is unfortunately different for different vendors. So we switch + on the device's digital scheme attribute in order to figure out + what to do. */ + switch (hdw->hdw_desc->digital_control_scheme) { + case PVR2_DIGITAL_SCHEME_HAUPPAUGE: + return pvr2_issue_simple_cmd(hdw, + (runFl ? + FX2CMD_HCW_DTV_STREAMING_ON : + FX2CMD_HCW_DTV_STREAMING_OFF)); + case PVR2_DIGITAL_SCHEME_ONAIR: + ret = pvr2_issue_simple_cmd(hdw, + (runFl ? + FX2CMD_STREAMING_ON : + FX2CMD_STREAMING_OFF)); + if (ret) return ret; + return pvr2_hdw_cmd_onair_digital_path_ctrl(hdw,runFl); + default: + return -EINVAL; + } } @@ -3538,7 +3635,12 @@ static int state_eval_encoder_ok(struct pvr2_hdw *hdw) if (hdw->state_encoder_config) return 0; if (hdw->state_decoder_run) return 0; if (hdw->state_usbstream_run) return 0; - if (hdw->pathway_state != PVR2_PATHWAY_ANALOG) return 0; + if (hdw->pathway_state == PVR2_PATHWAY_DIGITAL) { + if (!hdw->hdw_desc->flag_digital_requires_cx23416) return 0; + } else if (hdw->pathway_state != PVR2_PATHWAY_ANALOG) { + return 0; + } + if (pvr2_upload_firmware2(hdw) < 0) { hdw->flag_tripped = !0; trace_stbit("flag_tripped",hdw->flag_tripped); @@ -3615,23 +3717,116 @@ static int state_eval_encoder_config(struct pvr2_hdw *hdw) } +/* Return true if the encoder should not be running. */ +static int state_check_disable_encoder_run(struct pvr2_hdw *hdw) +{ + if (!hdw->state_encoder_ok) { + /* Encoder isn't healthy at the moment, so stop it. */ + return !0; + } + if (!hdw->state_pathway_ok) { + /* Mode is not understood at the moment (i.e. it wants to + change), so encoder must be stopped. */ + return !0; + } + + switch (hdw->pathway_state) { + case PVR2_PATHWAY_ANALOG: + if (!hdw->state_decoder_run) { + /* We're in analog mode and the decoder is not + running; thus the encoder should be stopped as + well. */ + return !0; + } + break; + case PVR2_PATHWAY_DIGITAL: + if (hdw->state_encoder_runok) { + /* This is a funny case. We're in digital mode so + really the encoder should be stopped. However + if it really is running, only kill it after + runok has been set. This gives a chance for the + onair quirk to function (encoder must run + briefly first, at least once, before onair + digital streaming can work). */ + return !0; + } + break; + default: + /* Unknown mode; so encoder should be stopped. */ + return !0; + } + + /* If we get here, we haven't found a reason to stop the + encoder. */ + return 0; +} + + +/* Return true if the encoder should be running. */ +static int state_check_enable_encoder_run(struct pvr2_hdw *hdw) +{ + if (!hdw->state_encoder_ok) { + /* Don't run the encoder if it isn't healthy... */ + return 0; + } + if (!hdw->state_pathway_ok) { + /* Don't run the encoder if we don't (yet) know what mode + we need to be in... */ + return 0; + } + + switch (hdw->pathway_state) { + case PVR2_PATHWAY_ANALOG: + if (hdw->state_decoder_run) { + /* In analog mode, if the decoder is running, then + run the encoder. */ + return !0; + } + break; + case PVR2_PATHWAY_DIGITAL: + if ((hdw->hdw_desc->digital_control_scheme == + PVR2_DIGITAL_SCHEME_ONAIR) && + !hdw->state_encoder_runok) { + /* This is a quirk. OnAir hardware won't stream + digital until the encoder has been run at least + once, for a minimal period of time (empiricially + measured to be 1/4 second). So if we're on + OnAir hardware and the encoder has never been + run at all, then start the encoder. Normal + state machine logic in the driver will + automatically handle the remaining bits. */ + return !0; + } + break; + default: + /* For completeness (unknown mode; encoder won't run ever) */ + break; + } + /* If we get here, then we haven't found any reason to run the + encoder, so don't run it. */ + return 0; +} + + /* Evaluate whether or not state_encoder_run can change */ static int state_eval_encoder_run(struct pvr2_hdw *hdw) { if (hdw->state_encoder_run) { + if (!state_check_disable_encoder_run(hdw)) return 0; if (hdw->state_encoder_ok) { - if (hdw->state_decoder_run && - hdw->state_pathway_ok) return 0; + del_timer_sync(&hdw->encoder_run_timer); if (pvr2_encoder_stop(hdw) < 0) return !0; } hdw->state_encoder_run = 0; } else { - if (!hdw->state_encoder_ok) return 0; - if (!hdw->state_decoder_run) return 0; - if (!hdw->state_pathway_ok) return 0; - if (hdw->pathway_state != PVR2_PATHWAY_ANALOG) return 0; + if (!state_check_enable_encoder_run(hdw)) return 0; if (pvr2_encoder_start(hdw) < 0) return !0; hdw->state_encoder_run = !0; + if (!hdw->state_encoder_runok) { + hdw->encoder_run_timer.expires = + jiffies + (HZ*250/1000); + add_timer(&hdw->encoder_run_timer); + } } trace_stbit("state_encoder_run",hdw->state_encoder_run); return !0; @@ -3660,6 +3855,19 @@ static void pvr2_hdw_encoder_wait_timeout(unsigned long data) } +/* Timeout function for encoder run timer. */ +static void pvr2_hdw_encoder_run_timeout(unsigned long data) +{ + struct pvr2_hdw *hdw = (struct pvr2_hdw *)data; + if (!hdw->state_encoder_runok) { + hdw->state_encoder_runok = !0; + trace_stbit("state_encoder_runok",hdw->state_encoder_runok); + hdw->state_stale = !0; + queue_work(hdw->workqueue,&hdw->workpoll); + } +} + + /* Evaluate whether or not state_decoder_run can change */ static int state_eval_decoder_run(struct pvr2_hdw *hdw) { @@ -3723,14 +3931,19 @@ static int state_eval_decoder_run(struct pvr2_hdw *hdw) static int state_eval_usbstream_run(struct pvr2_hdw *hdw) { if (hdw->state_usbstream_run) { + int fl = !0; if (hdw->pathway_state == PVR2_PATHWAY_ANALOG) { - if (hdw->state_encoder_ok && - hdw->state_encoder_run && - hdw->state_pathway_ok) return 0; - } else { - if (hdw->state_pipeline_req && - !hdw->state_pipeline_pause && - hdw->state_pathway_ok) return 0; + fl = (hdw->state_encoder_ok && + hdw->state_encoder_run); + } else if ((hdw->pathway_state == PVR2_PATHWAY_DIGITAL) && + (hdw->hdw_desc->flag_digital_requires_cx23416)) { + fl = hdw->state_encoder_ok; + } + if (fl && + hdw->state_pipeline_req && + !hdw->state_pipeline_pause && + hdw->state_pathway_ok) { + return 0; } pvr2_hdw_cmd_usbstream(hdw,0); hdw->state_usbstream_run = 0; @@ -3741,6 +3954,19 @@ static int state_eval_usbstream_run(struct pvr2_hdw *hdw) if (hdw->pathway_state == PVR2_PATHWAY_ANALOG) { if (!hdw->state_encoder_ok || !hdw->state_encoder_run) return 0; + } else if ((hdw->pathway_state == PVR2_PATHWAY_DIGITAL) && + (hdw->hdw_desc->flag_digital_requires_cx23416)) { + if (!hdw->state_encoder_ok) return 0; + if (hdw->state_encoder_run) return 0; + if (hdw->hdw_desc->digital_control_scheme == + PVR2_DIGITAL_SCHEME_ONAIR) { + /* OnAir digital receivers won't stream + unless the analog encoder has run first. + Why? I have no idea. But don't even + try until we know the analog side is + known to have run. */ + if (!hdw->state_encoder_runok) return 0; + } } if (pvr2_hdw_cmd_usbstream(hdw,!0) < 0) return 0; hdw->state_usbstream_run = !0; @@ -3836,6 +4062,24 @@ static int pvr2_hdw_state_update(struct pvr2_hdw *hdw) } +static unsigned int print_input_mask(unsigned int msk, + char *buf,unsigned int acnt) +{ + unsigned int idx,ccnt; + unsigned int tcnt = 0; + for (idx = 0; idx < ARRAY_SIZE(control_values_input); idx++) { + if (!((1 << idx) & msk)) continue; + ccnt = scnprintf(buf+tcnt, + acnt-tcnt, + "%s%s", + (tcnt ? ", " : ""), + control_values_input[idx]); + tcnt += ccnt; + } + return tcnt; +} + + static const char *pvr2_pathway_state_name(int id) { switch (id) { @@ -3884,22 +4128,65 @@ static unsigned int pvr2_hdw_report_unlocked(struct pvr2_hdw *hdw,int which, (hdw->state_encoder_ok ? "" : " <encode:init>"), (hdw->state_encoder_run ? - " <encode:run>" : " <encode:stop>"), + (hdw->state_encoder_runok ? + " <encode:run>" : + " <encode:firstrun>") : + (hdw->state_encoder_runok ? + " <encode:stop>" : + " <encode:virgin>")), (hdw->state_encoder_config ? " <encode:configok>" : (hdw->state_encoder_waitok ? - "" : " <encode:wait>")), + "" : " <encode:waitok>")), (hdw->state_usbstream_run ? " <usb:run>" : " <usb:stop>"), (hdw->state_pathway_ok ? " <pathway:ok>" : "")); - break; case 3: return scnprintf( buf,acnt, "state: %s", pvr2_get_state_name(hdw->master_state)); - break; + case 4: { + unsigned int tcnt = 0; + unsigned int ccnt; + + ccnt = scnprintf(buf, + acnt, + "Hardware supported inputs: "); + tcnt += ccnt; + tcnt += print_input_mask(hdw->input_avail_mask, + buf+tcnt, + acnt-tcnt); + if (hdw->input_avail_mask != hdw->input_allowed_mask) { + ccnt = scnprintf(buf+tcnt, + acnt-tcnt, + "; allowed inputs: "); + tcnt += ccnt; + tcnt += print_input_mask(hdw->input_allowed_mask, + buf+tcnt, + acnt-tcnt); + } + return tcnt; + } + case 5: { + struct pvr2_stream_stats stats; + if (!hdw->vid_stream) break; + pvr2_stream_get_stats(hdw->vid_stream, + &stats, + 0); + return scnprintf( + buf,acnt, + "Bytes streamed=%u" + " URBs: queued=%u idle=%u ready=%u" + " processed=%u failed=%u", + stats.bytes_processed, + stats.buffers_in_queue, + stats.buffers_in_idle, + stats.buffers_in_ready, + stats.buffers_processed, + stats.buffers_failed); + } default: break; } return 0; @@ -3963,7 +4250,9 @@ static int pvr2_hdw_state_eval(struct pvr2_hdw *hdw) st = PVR2_STATE_DEAD; } else if (hdw->fw1_state != FW1_STATE_OK) { st = PVR2_STATE_COLD; - } else if (analog_mode && !hdw->state_encoder_ok) { + } else if ((analog_mode || + hdw->hdw_desc->flag_digital_requires_cx23416) && + !hdw->state_encoder_ok) { st = PVR2_STATE_WARM; } else if (hdw->flag_tripped || (analog_mode && hdw->flag_decoder_missed)) { @@ -4009,6 +4298,7 @@ static void pvr2_hdw_state_sched(struct pvr2_hdw *hdw) queue_work(hdw->workqueue,&hdw->workpoll); } +#if 0 void pvr2_hdw_get_debug_info_unlocked(const struct pvr2_hdw *hdw, struct pvr2_hdw_debug_info *ptr) @@ -4050,6 +4340,7 @@ void pvr2_hdw_get_debug_info_locked(struct pvr2_hdw *hdw, } while(0); LOCK_GIVE(hdw->ctl_lock); } +#endif /* 0 */ int pvr2_hdw_gpio_get_dir(struct pvr2_hdw *hdw,u32 *dp) { @@ -4116,6 +4407,74 @@ unsigned int pvr2_hdw_get_input_available(struct pvr2_hdw *hdw) } +unsigned int pvr2_hdw_get_input_allowed(struct pvr2_hdw *hdw) +{ + return hdw->input_allowed_mask; +} + + +static int pvr2_hdw_set_input(struct pvr2_hdw *hdw,int v) +{ + if (hdw->input_val != v) { + hdw->input_val = v; + hdw->input_dirty = !0; + } + + /* Handle side effects - if we switch to a mode that needs the RF + tuner, then select the right frequency choice as well and mark + it dirty. */ + if (hdw->input_val == PVR2_CVAL_INPUT_RADIO) { + hdw->freqSelector = 0; + hdw->freqDirty = !0; + } else if ((hdw->input_val == PVR2_CVAL_INPUT_TV) || + (hdw->input_val == PVR2_CVAL_INPUT_DTV)) { + hdw->freqSelector = 1; + hdw->freqDirty = !0; + } + return 0; +} + + +int pvr2_hdw_set_input_allowed(struct pvr2_hdw *hdw, + unsigned int change_mask, + unsigned int change_val) +{ + int ret = 0; + unsigned int nv,m,idx; + LOCK_TAKE(hdw->big_lock); + do { + nv = hdw->input_allowed_mask & ~change_mask; + nv |= (change_val & change_mask); + nv &= hdw->input_avail_mask; + if (!nv) { + /* No legal modes left; return error instead. */ + ret = -EPERM; + break; + } + hdw->input_allowed_mask = nv; + if ((1 << hdw->input_val) & hdw->input_allowed_mask) { + /* Current mode is still in the allowed mask, so + we're done. */ + break; + } + /* Select and switch to a mode that is still in the allowed + mask */ + if (!hdw->input_allowed_mask) { + /* Nothing legal; give up */ + break; + } + m = hdw->input_allowed_mask; + for (idx = 0; idx < (sizeof(m) << 3); idx++) { + if (!((1 << idx) & m)) continue; + pvr2_hdw_set_input(hdw,idx); + break; + } + } while (0); + LOCK_GIVE(hdw->big_lock); + return ret; +} + + /* Find I2C address of eeprom */ static int pvr2_hdw_get_eeprom_addr(struct pvr2_hdw *hdw) { diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.h b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.h index e3e965546..97512a96f 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.h +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.h @@ -92,9 +92,6 @@ enum pvr2_v4l_type { /* Translate configuration enum to a string label */ const char *pvr2_config_get_name(enum pvr2_config); -/* Translate a master state enum to a string label */ -const char *pvr2_hdw_get_state_name(unsigned int); - struct pvr2_hdw; /* Create and return a structure for interacting with the underlying @@ -158,6 +155,19 @@ int pvr2_hdw_commit_ctl(struct pvr2_hdw *); * will be according to PVR_CVAL_INPUT_xxxx definitions. */ unsigned int pvr2_hdw_get_input_available(struct pvr2_hdw *); +/* Return a bit mask of allowed input selections for this device. Mask bits + * will be according to PVR_CVAL_INPUT_xxxx definitions. */ +unsigned int pvr2_hdw_get_input_allowed(struct pvr2_hdw *); + +/* Change the set of allowed input selections for this device. Both + change_mask and change_valu are mask bits according to + PVR_CVAL_INPUT_xxxx definitions. The change_mask parameter indicate + which settings are being changed and the change_val parameter indicates + whether corresponding settings are being set or cleared. */ +int pvr2_hdw_set_input_allowed(struct pvr2_hdw *, + unsigned int change_mask, + unsigned int change_val); + /* Return name for this driver instance */ const char *pvr2_hdw_get_driver_name(struct pvr2_hdw *); diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-io.c b/linux/drivers/media/video/pvrusb2/pvrusb2-io.c index e4aae2c97..e0b525e2a 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-io.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-io.c @@ -100,6 +100,10 @@ struct pvr2_stream { /* Tracking state for tolerating errors */ unsigned int fail_count; unsigned int fail_tolerance; + + unsigned int buffers_processed; + unsigned int buffers_failed; + unsigned int bytes_processed; }; struct pvr2_buffer { @@ -470,6 +474,8 @@ static void buffer_complete(struct urb *urb) (urb->status == -ENOENT) || (urb->status == -ECONNRESET) || (urb->status == -ESHUTDOWN)) { + (sp->buffers_processed)++; + sp->bytes_processed += urb->actual_length; bp->used_count = urb->actual_length; if (sp->fail_count) { pvr2_trace(PVR2_TRACE_TOLERANCE, @@ -481,11 +487,13 @@ static void buffer_complete(struct urb *urb) // We can tolerate this error, because we're below the // threshold... (sp->fail_count)++; + (sp->buffers_failed)++; pvr2_trace(PVR2_TRACE_TOLERANCE, "stream %p ignoring error %d" " - fail count increased to %u", sp,urb->status,sp->fail_count); } else { + (sp->buffers_failed)++; bp->status = urb->status; } spin_unlock_irqrestore(&sp->list_lock,irq_flags); @@ -539,6 +547,28 @@ void pvr2_stream_set_callback(struct pvr2_stream *sp, } while(0); mutex_unlock(&sp->mutex); } +void pvr2_stream_get_stats(struct pvr2_stream *sp, + struct pvr2_stream_stats *stats, + int zero_counts) +{ + unsigned long irq_flags; + spin_lock_irqsave(&sp->list_lock,irq_flags); + if (stats) { + stats->buffers_in_queue = sp->q_count; + stats->buffers_in_idle = sp->i_count; + stats->buffers_in_ready = sp->r_count; + stats->buffers_processed = sp->buffers_processed; + stats->buffers_failed = sp->buffers_failed; + stats->bytes_processed = sp->bytes_processed; + } + if (zero_counts) { + sp->buffers_processed = 0; + sp->buffers_failed = 0; + sp->bytes_processed = 0; + } + spin_unlock_irqrestore(&sp->list_lock,irq_flags); +} + /* Query / set the nominal buffer count */ int pvr2_stream_get_buffer_count(struct pvr2_stream *sp) { diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-io.h b/linux/drivers/media/video/pvrusb2/pvrusb2-io.h index b43e5f4cf..cd56a2b7a 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-io.h +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-io.h @@ -36,6 +36,15 @@ enum pvr2_buffer_state { struct pvr2_stream; struct pvr2_buffer; +struct pvr2_stream_stats { + unsigned int buffers_in_queue; + unsigned int buffers_in_idle; + unsigned int buffers_in_ready; + unsigned int buffers_processed; + unsigned int buffers_failed; + unsigned int bytes_processed; +}; + /* Initialize / tear down stream structure */ struct pvr2_stream *pvr2_stream_create(void); void pvr2_stream_destroy(struct pvr2_stream *); @@ -45,6 +54,9 @@ void pvr2_stream_setup(struct pvr2_stream *, void pvr2_stream_set_callback(struct pvr2_stream *, pvr2_stream_callback func, void *data); +void pvr2_stream_get_stats(struct pvr2_stream *, + struct pvr2_stream_stats *, + int zero_counts); /* Query / set the nominal buffer count */ int pvr2_stream_get_buffer_count(struct pvr2_stream *); diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-main.c b/linux/drivers/media/video/pvrusb2/pvrusb2-main.c index 59f8d7298..b5b6d2420 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-main.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-main.c @@ -61,6 +61,10 @@ static void pvr_setup_attach(struct pvr2_context *pvr) { /* Create association with v4l layer */ pvr2_v4l2_create(pvr); +#ifdef CONFIG_VIDEO_PVRUSB2_DVB + /* Create association with dvb layer */ + pvr2_dvb_create(pvr); +#endif #ifdef CONFIG_VIDEO_PVRUSB2_SYSFS pvr2_sysfs_create(pvr,class_ptr); #endif /* CONFIG_VIDEO_PVRUSB2_SYSFS */ @@ -125,6 +129,12 @@ static int __init pvr_init(void) pvr2_trace(PVR2_TRACE_INIT,"pvr_init"); + ret = pvr2_context_global_init(); + if (ret != 0) { + pvr2_trace(PVR2_TRACE_INIT,"pvr_init failure code=%d",ret); + return ret; + } + #ifdef CONFIG_VIDEO_PVRUSB2_SYSFS class_ptr = pvr2_sysfs_class_create(); #endif /* CONFIG_VIDEO_PVRUSB2_SYSFS */ @@ -136,6 +146,8 @@ static int __init pvr_init(void) if (pvrusb2_debug) info("Debug mask is %d (0x%x)", pvrusb2_debug,pvrusb2_debug); + pvr2_trace(PVR2_TRACE_INIT,"pvr_init complete"); + return ret; } @@ -148,6 +160,10 @@ static void __exit pvr_exit(void) #ifdef CONFIG_VIDEO_PVRUSB2_SYSFS pvr2_sysfs_class_destroy(class_ptr); #endif /* CONFIG_VIDEO_PVRUSB2_SYSFS */ + + pvr2_context_global_done(); + + pvr2_trace(PVR2_TRACE_INIT,"pvr_exit complete"); } module_init(pvr_init); diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-sysfs.c b/linux/drivers/media/video/pvrusb2/pvrusb2-sysfs.c index f672473dd..59dbd173f 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-sysfs.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-sysfs.c @@ -610,8 +610,9 @@ static void pvr2_sysfs_add_control(struct pvr2_sysfs *sfp,int ctl_id) ret = sysfs_create_group(&sfp->class_dev->kobj,&cip->grp); if (ret) { - printk(KERN_WARNING "%s: sysfs_create_group error: %d\n", - __FUNCTION__, ret); + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "sysfs_create_group error: %d", + ret); return; } cip->created_ok = !0; @@ -642,15 +643,17 @@ static void pvr2_sysfs_add_debugifc(struct pvr2_sysfs *sfp) sfp->debugifc = dip; ret = device_create_file(sfp->class_dev,&dip->attr_debugcmd); if (ret < 0) { - printk(KERN_WARNING "%s: device_create_file error: %d\n", - __FUNCTION__, ret); + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "device_create_file error: %d", + ret); } else { dip->debugcmd_created_ok = !0; } ret = device_create_file(sfp->class_dev,&dip->attr_debuginfo); if (ret < 0) { - printk(KERN_WARNING "%s: device_create_file error: %d\n", - __FUNCTION__, ret); + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "device_create_file error: %d", + ret); } else { dip->debuginfo_created_ok = !0; } @@ -861,8 +864,8 @@ static void class_dev_create(struct pvr2_sysfs *sfp, class_dev->driver_data = sfp; ret = device_register(class_dev); if (ret) { - printk(KERN_ERR "%s: device_register failed\n", - __FUNCTION__); + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "device_register failed"); kfree(class_dev); return; } @@ -874,8 +877,9 @@ static void class_dev_create(struct pvr2_sysfs *sfp, ret = device_create_file(sfp->class_dev, &sfp->attr_v4l_minor_number); if (ret < 0) { - printk(KERN_WARNING "%s: device_create_file error: %d\n", - __FUNCTION__, ret); + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "device_create_file error: %d", + ret); } else { sfp->v4l_minor_number_created_ok = !0; } @@ -887,8 +891,9 @@ static void class_dev_create(struct pvr2_sysfs *sfp, ret = device_create_file(sfp->class_dev, &sfp->attr_v4l_radio_minor_number); if (ret < 0) { - printk(KERN_WARNING "%s: device_create_file error: %d\n", - __FUNCTION__, ret); + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "device_create_file error: %d", + ret); } else { sfp->v4l_radio_minor_number_created_ok = !0; } @@ -899,8 +904,9 @@ static void class_dev_create(struct pvr2_sysfs *sfp, sfp->attr_unit_number.store = NULL; ret = device_create_file(sfp->class_dev,&sfp->attr_unit_number); if (ret < 0) { - printk(KERN_WARNING "%s: device_create_file error: %d\n", - __FUNCTION__, ret); + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "device_create_file error: %d", + ret); } else { sfp->unit_number_created_ok = !0; } @@ -912,8 +918,9 @@ static void class_dev_create(struct pvr2_sysfs *sfp, ret = device_create_file(sfp->class_dev, &sfp->attr_bus_info); if (ret < 0) { - printk(KERN_WARNING "%s: device_create_file error: %d\n", - __FUNCTION__, ret); + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "device_create_file error: %d", + ret); } else { sfp->bus_info_created_ok = !0; } @@ -925,8 +932,9 @@ static void class_dev_create(struct pvr2_sysfs *sfp, ret = device_create_file(sfp->class_dev, &sfp->attr_hdw_name); if (ret < 0) { - printk(KERN_WARNING "%s: device_create_file error: %d\n", - __FUNCTION__, ret); + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "device_create_file error: %d", + ret); } else { sfp->hdw_name_created_ok = !0; } @@ -938,8 +946,9 @@ static void class_dev_create(struct pvr2_sysfs *sfp, ret = device_create_file(sfp->class_dev, &sfp->attr_hdw_desc); if (ret < 0) { - printk(KERN_WARNING "%s: device_create_file error: %d\n", - __FUNCTION__, ret); + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "device_create_file error: %d", + ret); } else { sfp->hdw_desc_created_ok = !0; } diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-v4l2.c b/linux/drivers/media/video/pvrusb2/pvrusb2-v4l2.c index 38b0ff0f5..398157cee 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-v4l2.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-v4l2.c @@ -58,7 +58,9 @@ struct pvr2_v4l2_fh { struct pvr2_v4l2_fh *vprev; wait_queue_head_t wait_data; int fw_mode_flag; - int prev_input_val; + /* Map contiguous ordinal value to input id */ + unsigned char *input_map; + unsigned int input_cnt; }; struct pvr2_v4l2 { @@ -68,10 +70,6 @@ struct pvr2_v4l2 { struct v4l2_prio_state prio; - /* Map contiguous ordinal value to input id */ - unsigned char *input_map; - unsigned int input_cnt; - /* streams - Note that these must be separately, individually, * allocated pointers. This is because the v4l core is going to * manage their deletion - separately, individually... */ @@ -271,11 +269,11 @@ static int pvr2_v4l2_do_ioctl(struct inode *inode, struct file *file, memset(&tmp,0,sizeof(tmp)); tmp.index = vi->index; ret = 0; - if ((vi->index < 0) || (vi->index >= vp->input_cnt)) { + if ((vi->index < 0) || (vi->index >= fh->input_cnt)) { ret = -EINVAL; break; } - val = vp->input_map[vi->index]; + val = fh->input_map[vi->index]; switch (val) { case PVR2_CVAL_INPUT_TV: case PVR2_CVAL_INPUT_DTV: @@ -323,8 +321,8 @@ static int pvr2_v4l2_do_ioctl(struct inode *inode, struct file *file, val = 0; ret = pvr2_ctrl_get_value(cptr,&val); vi->index = 0; - for (idx = 0; idx < vp->input_cnt; idx++) { - if (vp->input_map[idx] == val) { + for (idx = 0; idx < fh->input_cnt; idx++) { + if (fh->input_map[idx] == val) { vi->index = idx; break; } @@ -335,13 +333,13 @@ static int pvr2_v4l2_do_ioctl(struct inode *inode, struct file *file, case VIDIOC_S_INPUT: { struct v4l2_input *vi = (struct v4l2_input *)arg; - if ((vi->index < 0) || (vi->index >= vp->input_cnt)) { + if ((vi->index < 0) || (vi->index >= fh->input_cnt)) { ret = -ERANGE; break; } ret = pvr2_ctrl_set_value( pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_INPUT), - vp->input_map[vi->index]); + fh->input_map[vi->index]); break; } @@ -840,10 +838,6 @@ static void pvr2_v4l2_destroy_no_lock(struct pvr2_v4l2 *vp) pvr2_v4l2_dev_destroy(vp->dev_radio); vp->dev_radio = NULL; } - if (vp->input_map) { - kfree(vp->input_map); - vp->input_map = NULL; - } pvr2_trace(PVR2_TRACE_STRUCT,"Destroying pvr2_v4l2 id=%p",vp); pvr2_channel_done(&vp->channel); @@ -901,20 +895,6 @@ static int pvr2_v4l2_release(struct inode *inode, struct file *file) v4l2_prio_close(&vp->prio, &fhp->prio); file->private_data = NULL; - /* Restore the previous input selection, if it makes sense - to do so. */ - if (fhp->dev_info->v4l_type == VFL_TYPE_RADIO) { - struct pvr2_ctrl *cp; - int pval; - cp = pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_INPUT); - pvr2_ctrl_get_value(cp,&pval); - /* Only restore if we're still selecting the radio */ - if (pval == PVR2_CVAL_INPUT_RADIO) { - pvr2_ctrl_set_value(cp,fhp->prev_input_val); - pvr2_hdw_commit_ctl(hdw); - } - } - if (fhp->vnext) { fhp->vnext->vprev = fhp->vprev; } else { @@ -931,6 +911,10 @@ static int pvr2_v4l2_release(struct inode *inode, struct file *file) pvr2_channel_done(&fhp->channel); pvr2_trace(PVR2_TRACE_STRUCT, "Destroying pvr_v4l2_fh id=%p",fhp); + if (fhp->input_map) { + kfree(fhp->input_map); + fhp->input_map = NULL; + } kfree(fhp); if (vp->channel.mc_head->disconnect_flag && !vp->vfirst) { pvr2_v4l2_destroy_no_lock(vp); @@ -945,6 +929,9 @@ static int pvr2_v4l2_open(struct inode *inode, struct file *file) struct pvr2_v4l2_fh *fhp; struct pvr2_v4l2 *vp; struct pvr2_hdw *hdw; + unsigned int input_mask = 0; + unsigned int input_cnt,idx; + int ret = 0; dip = container_of(video_devdata(file),struct pvr2_v4l2_dev,devbase); @@ -970,6 +957,50 @@ static int pvr2_v4l2_open(struct inode *inode, struct file *file) pvr2_trace(PVR2_TRACE_STRUCT,"Creating pvr_v4l2_fh id=%p",fhp); pvr2_channel_init(&fhp->channel,vp->channel.mc_head); + if (dip->v4l_type == VFL_TYPE_RADIO) { + /* Opening device as a radio, legal input selection subset + is just the radio. */ + input_mask = (1 << PVR2_CVAL_INPUT_RADIO); + } else { + /* Opening the main V4L device, legal input selection + subset includes all analog inputs. */ + input_mask = ((1 << PVR2_CVAL_INPUT_RADIO) | + (1 << PVR2_CVAL_INPUT_TV) | + (1 << PVR2_CVAL_INPUT_COMPOSITE) | + (1 << PVR2_CVAL_INPUT_SVIDEO)); + } + ret = pvr2_channel_limit_inputs(&fhp->channel,input_mask); + if (ret) { + pvr2_channel_done(&fhp->channel); + pvr2_trace(PVR2_TRACE_STRUCT, + "Destroying pvr_v4l2_fh id=%p (input mask error)", + fhp); + + kfree(fhp); + return ret; + } + + input_mask &= pvr2_hdw_get_input_available(hdw); + input_cnt = 0; + for (idx = 0; idx < (sizeof(input_mask) << 3); idx++) { + if (input_mask & (1 << idx)) input_cnt++; + } + fhp->input_cnt = input_cnt; + fhp->input_map = kzalloc(input_cnt,GFP_KERNEL); + if (!fhp->input_map) { + pvr2_channel_done(&fhp->channel); + pvr2_trace(PVR2_TRACE_STRUCT, + "Destroying pvr_v4l2_fh id=%p (input map failure)", + fhp); + kfree(fhp); + return -ENOMEM; + } + input_cnt = 0; + for (idx = 0; idx < (sizeof(input_mask) << 3); idx++) { + if (!(input_mask & (1 << idx))) continue; + fhp->input_map[input_cnt++] = idx; + } + fhp->vnext = NULL; fhp->vprev = vp->vlast; if (vp->vlast) { @@ -980,18 +1011,6 @@ static int pvr2_v4l2_open(struct inode *inode, struct file *file) vp->vlast = fhp; fhp->vhead = vp; - /* Opening the /dev/radioX device implies a mode switch. - So execute that here. Note that you can get the - IDENTICAL effect merely by opening the normal video - device and setting the input appropriately. */ - if (dip->v4l_type == VFL_TYPE_RADIO) { - struct pvr2_ctrl *cp; - cp = pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_INPUT); - pvr2_ctrl_get_value(cp,&fhp->prev_input_val); - pvr2_ctrl_set_value(cp,PVR2_CVAL_INPUT_RADIO); - pvr2_hdw_commit_ctl(hdw); - } - fhp->file = file; file->private_data = fhp; v4l2_prio_open(&vp->prio,&fhp->prio); @@ -1226,8 +1245,6 @@ static void pvr2_v4l2_dev_init(struct pvr2_v4l2_dev *dip, struct pvr2_v4l2 *pvr2_v4l2_create(struct pvr2_context *mnp) { struct pvr2_v4l2 *vp; - struct pvr2_hdw *hdw; - unsigned int input_mask,input_cnt,idx; vp = kzalloc(sizeof(*vp),GFP_KERNEL); if (!vp) return vp; @@ -1236,26 +1253,12 @@ struct pvr2_v4l2 *pvr2_v4l2_create(struct pvr2_context *mnp) vp->channel.check_func = pvr2_v4l2_internal_check; - hdw = vp->channel.mc_head->hdw; - input_mask = pvr2_hdw_get_input_available(hdw); - input_cnt = 0; - for (idx = 0; idx < (sizeof(input_mask) << 3); idx++) { - if (input_mask & (1 << idx)) input_cnt++; - } - vp->input_cnt = input_cnt; - vp->input_map = kzalloc(input_cnt,GFP_KERNEL); - if (!vp->input_map) goto fail; - input_cnt = 0; - for (idx = 0; idx < (sizeof(input_mask) << 3); idx++) { - if (!(input_mask & (1 << idx))) continue; - vp->input_map[input_cnt++] = idx; - } - /* register streams */ vp->dev_video = kzalloc(sizeof(*vp->dev_video),GFP_KERNEL); if (!vp->dev_video) goto fail; pvr2_v4l2_dev_init(vp->dev_video,vp,VFL_TYPE_GRABBER); - if (input_mask & (1 << PVR2_CVAL_INPUT_RADIO)) { + if (pvr2_hdw_get_input_available(vp->channel.mc_head->hdw) & + (1 << PVR2_CVAL_INPUT_RADIO)) { vp->dev_radio = kzalloc(sizeof(*vp->dev_radio),GFP_KERNEL); if (!vp->dev_radio) goto fail; pvr2_v4l2_dev_init(vp->dev_radio,vp,VFL_TYPE_RADIO); diff --git a/linux/drivers/media/video/pxa_camera.c b/linux/drivers/media/video/pxa_camera.c index 936db67a5..7cc8e9b19 100644 --- a/linux/drivers/media/video/pxa_camera.c +++ b/linux/drivers/media/video/pxa_camera.c @@ -49,6 +49,9 @@ #define CICR1_DW_VAL(x) ((x) & CICR1_DW) /* Data bus width */ #define CICR1_PPL_VAL(x) (((x) << 15) & CICR1_PPL) /* Pixels per line */ +#define CICR1_COLOR_SP_VAL(x) (((x) << 3) & CICR1_COLOR_SP) /* color space */ +#define CICR1_RGB_BPP_VAL(x) (((x) << 7) & CICR1_RGB_BPP) /* bpp for rgb */ +#define CICR1_RGBT_CONV_VAL(x) (((x) << 29) & CICR1_RGBT_CONV) /* rgbt conv */ #define CICR2_BLW_VAL(x) (((x) << 24) & CICR2_BLW) /* Beginning-of-line pixel clock wait count */ #define CICR2_ELW_VAL(x) (((x) << 16) & CICR2_ELW) /* End-of-line pixel clock wait count */ @@ -70,6 +73,19 @@ static DEFINE_MUTEX(camera_lock); /* * Structures */ +enum pxa_camera_active_dma { + DMA_Y = 0x1, + DMA_U = 0x2, + DMA_V = 0x4, +}; + +/* descriptor needed for the PXA DMA engine */ +struct pxa_cam_dma { + dma_addr_t sg_dma; + struct pxa_dma_desc *sg_cpu; + size_t sg_size; + int sglen; +}; /* buffer for one video frame */ struct pxa_buffer { @@ -78,16 +94,12 @@ struct pxa_buffer { const struct soc_camera_data_format *fmt; - /* our descriptor list needed for the PXA DMA engine */ - dma_addr_t sg_dma; - struct pxa_dma_desc *sg_cpu; - size_t sg_size; + /* our descriptor lists for Y, U and V channels */ + struct pxa_cam_dma dmas[3]; + int inwork; -}; -struct pxa_framebuffer_queue { - dma_addr_t sg_last_dma; - struct pxa_dma_desc *sg_last_cpu; + enum pxa_camera_active_dma active_dma; }; struct pxa_camera_dev { @@ -100,7 +112,9 @@ struct pxa_camera_dev { unsigned int irq; void __iomem *base; - unsigned int dma_chan_y; + + int channels; + unsigned int dma_chans[3]; struct pxacamera_platform_data *pdata; struct resource *res; @@ -112,6 +126,7 @@ struct pxa_camera_dev { spinlock_t lock; struct pxa_buffer *active; + struct pxa_dma_desc *sg_tail[3]; }; static const char *pxa_cam_driver_description = "PXA_Camera"; @@ -125,10 +140,21 @@ static int pxa_videobuf_setup(struct videobuf_queue *vq, unsigned int *count, unsigned int *size) { struct soc_camera_device *icd = vq->priv_data; + struct soc_camera_host *ici = + to_soc_camera_host(icd->dev.parent); + struct pxa_camera_dev *pcdev = ici->priv; dev_dbg(&icd->dev, "count=%d, size=%d\n", *count, *size); - *size = icd->width * icd->height * ((icd->current_fmt->depth + 7) >> 3); + /* planar capture requires Y, U and V buffers to be page aligned */ + if (pcdev->channels == 3) { + *size = PAGE_ALIGN(icd->width * icd->height); /* Y pages */ + *size += PAGE_ALIGN(icd->width * icd->height / 2); /* U pages */ + *size += PAGE_ALIGN(icd->width * icd->height / 2); /* V pages */ + } else { + *size = icd->width * icd->height * + ((icd->current_fmt->depth + 7) >> 3); + } if (0 == *count) *count = 32; @@ -145,6 +171,7 @@ static void free_buffer(struct videobuf_queue *vq, struct pxa_buffer *buf) to_soc_camera_host(icd->dev.parent); struct pxa_camera_dev *pcdev = ici->priv; struct videobuf_dmabuf *dma = videobuf_to_dma(&buf->vb); + int i; BUG_ON(in_interrupt()); @@ -157,14 +184,62 @@ static void free_buffer(struct videobuf_queue *vq, struct pxa_buffer *buf) videobuf_dma_unmap(vq, dma); videobuf_dma_free(dma); - if (buf->sg_cpu) - dma_free_coherent(pcdev->dev, buf->sg_size, buf->sg_cpu, - buf->sg_dma); - buf->sg_cpu = NULL; + for (i = 0; i < ARRAY_SIZE(buf->dmas); i++) { + if (buf->dmas[i].sg_cpu) + dma_free_coherent(pcdev->dev, buf->dmas[i].sg_size, + buf->dmas[i].sg_cpu, + buf->dmas[i].sg_dma); + buf->dmas[i].sg_cpu = NULL; + } buf->vb.state = VIDEOBUF_NEEDS_INIT; } +static int pxa_init_dma_channel(struct pxa_camera_dev *pcdev, + struct pxa_buffer *buf, + struct videobuf_dmabuf *dma, int channel, + int sglen, int sg_start, int cibr, + unsigned int size) +{ + struct pxa_cam_dma *pxa_dma = &buf->dmas[channel]; + int i; + + if (pxa_dma->sg_cpu) + dma_free_coherent(pcdev->dev, pxa_dma->sg_size, + pxa_dma->sg_cpu, pxa_dma->sg_dma); + + pxa_dma->sg_size = (sglen + 1) * sizeof(struct pxa_dma_desc); + pxa_dma->sg_cpu = dma_alloc_coherent(pcdev->dev, pxa_dma->sg_size, + &pxa_dma->sg_dma, GFP_KERNEL); + if (!pxa_dma->sg_cpu) + return -ENOMEM; + + pxa_dma->sglen = sglen; + + for (i = 0; i < sglen; i++) { + int sg_i = sg_start + i; + struct scatterlist *sg = dma->sglist; + unsigned int dma_len = sg_dma_len(&sg[sg_i]), xfer_len; + + pxa_dma->sg_cpu[i].dsadr = pcdev->res->start + cibr; + pxa_dma->sg_cpu[i].dtadr = sg_dma_address(&sg[sg_i]); + + /* PXA27x Developer's Manual 27.4.4.1: round up to 8 bytes */ + xfer_len = (min(dma_len, size) + 7) & ~7; + + pxa_dma->sg_cpu[i].dcmd = + DCMD_FLOWSRC | DCMD_BURST8 | DCMD_INCTRGADDR | xfer_len; + size -= dma_len; + pxa_dma->sg_cpu[i].ddadr = + pxa_dma->sg_dma + (i + 1) * sizeof(struct pxa_dma_desc); + } + + pxa_dma->sg_cpu[sglen - 1].ddadr = DDADR_STOP; + pxa_dma->sg_cpu[sglen - 1].dcmd |= DCMD_ENDIRQEN; + + return 0; +} + static int pxa_videobuf_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb, enum v4l2_field field) { @@ -173,7 +248,9 @@ static int pxa_videobuf_prepare(struct videobuf_queue *vq, to_soc_camera_host(icd->dev.parent); struct pxa_camera_dev *pcdev = ici->priv; struct pxa_buffer *buf = container_of(vb, struct pxa_buffer, vb); - int i, ret; + int ret; + int sglen_y, sglen_yu = 0, sglen_u = 0, sglen_v = 0; + int size_y, size_u = 0, size_v = 0; dev_dbg(&icd->dev, "%s (vb=0x%p) 0x%08lx %d\n", __func__, vb, vb->baddr, vb->bsize); @@ -218,49 +295,64 @@ static int pxa_videobuf_prepare(struct videobuf_queue *vq, if (ret) goto fail; - if (buf->sg_cpu) - dma_free_coherent(pcdev->dev, buf->sg_size, buf->sg_cpu, - buf->sg_dma); + if (pcdev->channels == 3) { + /* FIXME the calculations should be more precise */ + sglen_y = dma->sglen / 2; + sglen_u = sglen_v = dma->sglen / 4 + 1; + sglen_yu = sglen_y + sglen_u; + size_y = size / 2; + size_u = size_v = size / 4; + } else { + sglen_y = dma->sglen; + size_y = size; + } + + /* init DMA for Y channel */ + ret = pxa_init_dma_channel(pcdev, buf, dma, 0, sglen_y, + 0, 0x28, size_y); - buf->sg_size = (dma->sglen + 1) * sizeof(struct pxa_dma_desc); - buf->sg_cpu = dma_alloc_coherent(pcdev->dev, buf->sg_size, - &buf->sg_dma, GFP_KERNEL); - if (!buf->sg_cpu) { - ret = -ENOMEM; + if (ret) { + dev_err(pcdev->dev, + "DMA initialization for Y/RGB failed\n"); goto fail; } - dev_dbg(&icd->dev, "nents=%d size: %d sg=0x%p\n", - dma->sglen, size, dma->sglist); - for (i = 0; i < dma->sglen; i++) { - struct scatterlist *sg = dma->sglist; - unsigned int dma_len = sg_dma_len(&sg[i]), xfer_len; - - /* CIBR0 */ - buf->sg_cpu[i].dsadr = pcdev->res->start + 0x28; - buf->sg_cpu[i].dtadr = sg_dma_address(&sg[i]); - /* PXA270 Developer's Manual 27.4.4.1: - * round up to 8 bytes */ - xfer_len = (min(dma_len, size) + 7) & ~7; - if (xfer_len & 7) - dev_err(&icd->dev, "Unaligned buffer: " - "dma_len %u, size %u\n", dma_len, size); - buf->sg_cpu[i].dcmd = DCMD_FLOWSRC | DCMD_BURST8 | - DCMD_INCTRGADDR | xfer_len; - size -= dma_len; - buf->sg_cpu[i].ddadr = buf->sg_dma + (i + 1) * - sizeof(struct pxa_dma_desc); + if (pcdev->channels == 3) { + /* init DMA for U channel */ + ret = pxa_init_dma_channel(pcdev, buf, dma, 1, sglen_u, + sglen_y, 0x30, size_u); + if (ret) { + dev_err(pcdev->dev, + "DMA initialization for U failed\n"); + goto fail_u; + } + + /* init DMA for V channel */ + ret = pxa_init_dma_channel(pcdev, buf, dma, 2, sglen_v, + sglen_yu, 0x38, size_v); + if (ret) { + dev_err(pcdev->dev, + "DMA initialization for V failed\n"); + goto fail_v; + } } - buf->sg_cpu[dma->sglen - 1].ddadr = DDADR_STOP; - buf->sg_cpu[dma->sglen - 1].dcmd |= DCMD_ENDIRQEN; vb->state = VIDEOBUF_PREPARED; } buf->inwork = 0; + buf->active_dma = DMA_Y; + if (pcdev->channels == 3) + buf->active_dma |= DMA_U | DMA_V; return 0; +fail_v: + dma_free_coherent(pcdev->dev, buf->dmas[1].sg_size, + buf->dmas[1].sg_cpu, buf->dmas[1].sg_dma); +fail_u: + dma_free_coherent(pcdev->dev, buf->dmas[0].sg_size, + buf->dmas[0].sg_cpu, buf->dmas[0].sg_dma); fail: free_buffer(vq, buf); out: @@ -277,9 +369,8 @@ static void pxa_videobuf_queue(struct videobuf_queue *vq, struct pxa_camera_dev *pcdev = ici->priv; struct pxa_buffer *buf = container_of(vb, struct pxa_buffer, vb); struct pxa_buffer *active; - struct videobuf_dmabuf *dma = videobuf_to_dma(vb); - int nents = dma->sglen; unsigned long flags; + int i; dev_dbg(&icd->dev, "%s (vb=0x%p) 0x%08lx %d\n", __func__, vb, vb->baddr, vb->bsize); @@ -292,59 +383,63 @@ static void pxa_videobuf_queue(struct videobuf_queue *vq, if (!active) { CIFR |= CIFR_RESET_F; - DDADR(pcdev->dma_chan_y) = buf->sg_dma; - DCSR(pcdev->dma_chan_y) = DCSR_RUN; + + for (i = 0; i < pcdev->channels; i++) { + DDADR(pcdev->dma_chans[i]) = buf->dmas[i].sg_dma; + DCSR(pcdev->dma_chans[i]) = DCSR_RUN; + pcdev->sg_tail[i] = buf->dmas[i].sg_cpu + buf->dmas[i].sglen - 1; + } + pcdev->active = buf; CICR0 |= CICR0_ENB; } else { - struct videobuf_dmabuf *active_dma = - videobuf_to_dma(&active->vb); - /* Stop DMA engine */ - DCSR(pcdev->dma_chan_y) = 0; - - /* Add the descriptors we just initialized to the currently - * running chain - */ - active->sg_cpu[active_dma->sglen - 1].ddadr = buf->sg_dma; - - /* Setup a dummy descriptor with the DMA engines current - * state - */ - /* CIBR0 */ - buf->sg_cpu[nents].dsadr = pcdev->res->start + 0x28; - buf->sg_cpu[nents].dtadr = DTADR(pcdev->dma_chan_y); - buf->sg_cpu[nents].dcmd = DCMD(pcdev->dma_chan_y); - - if (DDADR(pcdev->dma_chan_y) == DDADR_STOP) { - /* The DMA engine is on the last descriptor, set the - * next descriptors address to the descriptors - * we just initialized - */ - buf->sg_cpu[nents].ddadr = buf->sg_dma; - } else { - buf->sg_cpu[nents].ddadr = DDADR(pcdev->dma_chan_y); - } + struct pxa_cam_dma *buf_dma; + struct pxa_cam_dma *act_dma; + int nents; - /* The next descriptor is the dummy descriptor */ - DDADR(pcdev->dma_chan_y) = buf->sg_dma + nents * - sizeof(struct pxa_dma_desc); + for (i = 0; i < pcdev->channels; i++) { + buf_dma = &buf->dmas[i]; + act_dma = &active->dmas[i]; + nents = buf_dma->sglen; -#ifdef DEBUG - if (CISR & CISR_IFO_0) { - dev_warn(pcdev->dev, "FIFO overrun\n"); - DDADR(pcdev->dma_chan_y) = pcdev->active->sg_dma; - - CICR0 &= ~CICR0_ENB; - CIFR |= CIFR_RESET_F; - DCSR(pcdev->dma_chan_y) = DCSR_RUN; - CICR0 |= CICR0_ENB; - } else -#endif - DCSR(pcdev->dma_chan_y) = DCSR_RUN; + /* Stop DMA engine */ + DCSR(pcdev->dma_chans[i]) = 0; + + /* Add the descriptors we just initialized to + the currently running chain */ + pcdev->sg_tail[i]->ddadr = buf_dma->sg_dma; + pcdev->sg_tail[i] = buf_dma->sg_cpu + buf_dma->sglen - 1; + + /* Setup a dummy descriptor with the DMA engines current + * state + */ + buf_dma->sg_cpu[nents].dsadr = + pcdev->res->start + 0x28 + i*8; /* CIBRx */ + buf_dma->sg_cpu[nents].dtadr = + DTADR(pcdev->dma_chans[i]); + buf_dma->sg_cpu[nents].dcmd = + DCMD(pcdev->dma_chans[i]); + + if (DDADR(pcdev->dma_chans[i]) == DDADR_STOP) { + /* The DMA engine is on the last + descriptor, set the next descriptors + address to the descriptors we just + initialized */ + buf_dma->sg_cpu[nents].ddadr = buf_dma->sg_dma; + } else { + buf_dma->sg_cpu[nents].ddadr = + DDADR(pcdev->dma_chans[i]); + } + + /* The next descriptor is the dummy descriptor */ + DDADR(pcdev->dma_chans[i]) = buf_dma->sg_dma + nents * + sizeof(struct pxa_dma_desc); + + DCSR(pcdev->dma_chans[i]) = DCSR_RUN; + } } spin_unlock_irqrestore(&pcdev->lock, flags); - } static void pxa_videobuf_release(struct videobuf_queue *vq, @@ -376,18 +471,42 @@ static void pxa_videobuf_release(struct videobuf_queue *vq, free_buffer(vq, buf); } -static void pxa_camera_dma_irq_y(int channel, void *data) +static void pxa_camera_wakeup(struct pxa_camera_dev *pcdev, + struct videobuf_buffer *vb, + struct pxa_buffer *buf) +{ + /* _init is used to debug races, see comment in pxa_camera_reqbufs() */ + list_del_init(&vb->queue); + vb->state = VIDEOBUF_DONE; + do_gettimeofday(&vb->ts); + vb->field_count++; + wake_up(&vb->done); + + if (list_empty(&pcdev->capture)) { + pcdev->active = NULL; + DCSR(pcdev->dma_chans[0]) = 0; + DCSR(pcdev->dma_chans[1]) = 0; + DCSR(pcdev->dma_chans[2]) = 0; + CICR0 &= ~CICR0_ENB; + return; + } + + pcdev->active = list_entry(pcdev->capture.next, + struct pxa_buffer, vb.queue); +} + +static void pxa_camera_dma_irq(int channel, struct pxa_camera_dev *pcdev, + enum pxa_camera_active_dma act_dma) { - struct pxa_camera_dev *pcdev = data; struct pxa_buffer *buf; unsigned long flags; - unsigned int status; + u32 status, camera_status, overrun; struct videobuf_buffer *vb; spin_lock_irqsave(&pcdev->lock, flags); - status = DCSR(pcdev->dma_chan_y); - DCSR(pcdev->dma_chan_y) = status; + status = DCSR(channel); + DCSR(channel) = status | DCSR_ENDINTR; if (status & DCSR_BUSERR) { dev_err(pcdev->dev, "DMA Bus Error IRQ!\n"); @@ -405,33 +524,57 @@ static void pxa_camera_dma_irq_y(int channel, void *data) goto out; } + camera_status = CISR; + overrun = CISR_IFO_0; + if (pcdev->channels == 3) + overrun |= CISR_IFO_1 | CISR_IFO_2; + if (camera_status & overrun) { + dev_dbg(pcdev->dev, "FIFO overrun! CISR: %x\n", camera_status); + /* Stop the Capture Interface */ + CICR0 &= ~CICR0_ENB; + /* Stop DMA */ + DCSR(channel) = 0; + /* Reset the FIFOs */ + CIFR |= CIFR_RESET_F; + /* Enable End-Of-Frame Interrupt */ + CICR0 &= ~CICR0_EOFM; + /* Restart the Capture Interface */ + CICR0 |= CICR0_ENB; + goto out; + } + vb = &pcdev->active->vb; buf = container_of(vb, struct pxa_buffer, vb); WARN_ON(buf->inwork || list_empty(&vb->queue)); dev_dbg(pcdev->dev, "%s (vb=0x%p) 0x%08lx %d\n", __func__, vb, vb->baddr, vb->bsize); - /* _init is used to debug races, see comment in pxa_camera_reqbufs() */ - list_del_init(&vb->queue); - vb->state = VIDEOBUF_DONE; - do_gettimeofday(&vb->ts); - vb->field_count++; - wake_up(&vb->done); - - if (list_empty(&pcdev->capture)) { - pcdev->active = NULL; - DCSR(pcdev->dma_chan_y) = 0; - CICR0 &= ~CICR0_ENB; - goto out; - } - - pcdev->active = list_entry(pcdev->capture.next, struct pxa_buffer, - vb.queue); + buf->active_dma &= ~act_dma; + if (!buf->active_dma) + pxa_camera_wakeup(pcdev, vb, buf); out: spin_unlock_irqrestore(&pcdev->lock, flags); } +static void pxa_camera_dma_irq_y(int channel, void *data) +{ + struct pxa_camera_dev *pcdev = data; + pxa_camera_dma_irq(channel, pcdev, DMA_Y); +} + +static void pxa_camera_dma_irq_u(int channel, void *data) +{ + struct pxa_camera_dev *pcdev = data; + pxa_camera_dma_irq(channel, pcdev, DMA_U); +} + +static void pxa_camera_dma_irq_v(int channel, void *data) +{ + struct pxa_camera_dev *pcdev = data; + pxa_camera_dma_irq(channel, pcdev, DMA_V); +} + static struct videobuf_queue_ops pxa_videobuf_ops = { .buf_setup = pxa_videobuf_setup, .buf_prepare = pxa_videobuf_prepare, @@ -524,8 +667,21 @@ static irqreturn_t pxa_camera_irq(int irq, void *data) dev_dbg(pcdev->dev, "Camera interrupt status 0x%x\n", status); + if (!status) + return IRQ_NONE; + CISR = status; + if (status & CISR_EOF) { + int i; + for (i = 0; i < pcdev->channels; i++) { + DDADR(pcdev->dma_chans[i]) = + pcdev->active->dmas[i].sg_dma; + DCSR(pcdev->dma_chans[i]) = DCSR_RUN; + } + CICR0 |= CICR0_EOFM; + } + return IRQ_HANDLED; } @@ -571,8 +727,11 @@ static void pxa_camera_remove_device(struct soc_camera_device *icd) /* disable capture, disable interrupts */ CICR0 = 0x3ff; + /* Stop DMA engine */ - DCSR(pcdev->dma_chan_y) = 0; + DCSR(pcdev->dma_chans[0]) = 0; + DCSR(pcdev->dma_chans[1]) = 0; + DCSR(pcdev->dma_chans[2]) = 0; icd->ops->release(icd); @@ -625,7 +784,7 @@ static int pxa_camera_set_bus_param(struct soc_camera_device *icd, __u32 pixfmt) to_soc_camera_host(icd->dev.parent); struct pxa_camera_dev *pcdev = ici->priv; unsigned long dw, bpp, bus_flags, camera_flags, common_flags; - u32 cicr0, cicr4 = 0; + u32 cicr0, cicr1, cicr4 = 0; int ret = test_platform_param(pcdev, icd->buswidth, &bus_flags); if (ret < 0) @@ -637,6 +796,8 @@ static int pxa_camera_set_bus_param(struct soc_camera_device *icd, __u32 pixfmt) if (!common_flags) return -EINVAL; + pcdev->channels = 1; + /* Make choises, based on platform preferences */ if ((common_flags & SOCAM_HSYNC_ACTIVE_HIGH) && (common_flags & SOCAM_HSYNC_ACTIVE_LOW)) { @@ -702,7 +863,26 @@ static int pxa_camera_set_bus_param(struct soc_camera_device *icd, __u32 pixfmt) cicr0 = CICR0; if (cicr0 & CICR0_ENB) CICR0 = cicr0 & ~CICR0_ENB; - CICR1 = CICR1_PPL_VAL(icd->width - 1) | bpp | dw; + + cicr1 = CICR1_PPL_VAL(icd->width - 1) | bpp | dw; + + switch (pixfmt) { + case V4L2_PIX_FMT_YUV422P: + pcdev->channels = 3; + cicr1 |= CICR1_YCBCR_F; + case V4L2_PIX_FMT_YUYV: + cicr1 |= CICR1_COLOR_SP_VAL(2); + break; + case V4L2_PIX_FMT_RGB555: + cicr1 |= CICR1_RGB_BPP_VAL(1) | CICR1_RGBT_CONV_VAL(2) | + CICR1_TBIT | CICR1_COLOR_SP_VAL(1); + break; + case V4L2_PIX_FMT_RGB565: + cicr1 |= CICR1_COLOR_SP_VAL(1) | CICR1_RGB_BPP_VAL(2); + break; + } + + CICR1 = cicr1; CICR2 = 0; CICR3 = CICR3_LPF_VAL(icd->height - 1) | CICR3_BFW_VAL(min((unsigned short)255, icd->y_skip_top)); @@ -905,16 +1085,36 @@ static int pxa_camera_probe(struct platform_device *pdev) pcdev->dev = &pdev->dev; /* request dma */ - pcdev->dma_chan_y = pxa_request_dma("CI_Y", DMA_PRIO_HIGH, - pxa_camera_dma_irq_y, pcdev); - if (pcdev->dma_chan_y < 0) { + pcdev->dma_chans[0] = pxa_request_dma("CI_Y", DMA_PRIO_HIGH, + pxa_camera_dma_irq_y, pcdev); + if (pcdev->dma_chans[0] < 0) { dev_err(pcdev->dev, "Can't request DMA for Y\n"); err = -ENOMEM; goto exit_iounmap; } - dev_dbg(pcdev->dev, "got DMA channel %d\n", pcdev->dma_chan_y); + dev_dbg(pcdev->dev, "got DMA channel %d\n", pcdev->dma_chans[0]); + + pcdev->dma_chans[1] = pxa_request_dma("CI_U", DMA_PRIO_HIGH, + pxa_camera_dma_irq_u, pcdev); + if (pcdev->dma_chans[1] < 0) { + dev_err(pcdev->dev, "Can't request DMA for U\n"); + err = -ENOMEM; + goto exit_free_dma_y; + } + dev_dbg(pcdev->dev, "got DMA channel (U) %d\n", pcdev->dma_chans[1]); + + pcdev->dma_chans[2] = pxa_request_dma("CI_V", DMA_PRIO_HIGH, + pxa_camera_dma_irq_v, pcdev); + if (pcdev->dma_chans[0] < 0) { + dev_err(pcdev->dev, "Can't request DMA for V\n"); + err = -ENOMEM; + goto exit_free_dma_u; + } + dev_dbg(pcdev->dev, "got DMA channel (V) %d\n", pcdev->dma_chans[2]); - DRCMR68 = pcdev->dma_chan_y | DRCMR_MAPVLD; + DRCMR68 = pcdev->dma_chans[0] | DRCMR_MAPVLD; + DRCMR69 = pcdev->dma_chans[1] | DRCMR_MAPVLD; + DRCMR70 = pcdev->dma_chans[2] | DRCMR_MAPVLD; /* request irq */ err = request_irq(pcdev->irq, pxa_camera_irq, 0, PXA_CAM_DRV_NAME, @@ -936,7 +1136,11 @@ static int pxa_camera_probe(struct platform_device *pdev) exit_free_irq: free_irq(pcdev->irq, pcdev); exit_free_dma: - pxa_free_dma(pcdev->dma_chan_y); + pxa_free_dma(pcdev->dma_chans[2]); +exit_free_dma_u: + pxa_free_dma(pcdev->dma_chans[1]); +exit_free_dma_y: + pxa_free_dma(pcdev->dma_chans[0]); exit_iounmap: iounmap(base); exit_release: @@ -956,7 +1160,9 @@ static int __devexit pxa_camera_remove(struct platform_device *pdev) clk_put(pcdev->clk); - pxa_free_dma(pcdev->dma_chan_y); + pxa_free_dma(pcdev->dma_chans[0]); + pxa_free_dma(pcdev->dma_chans[1]); + pxa_free_dma(pcdev->dma_chans[2]); free_irq(pcdev->irq, pcdev); soc_camera_host_unregister(&pxa_soc_camera_host); diff --git a/linux/drivers/media/video/saa7134/saa7134-cards.c b/linux/drivers/media/video/saa7134/saa7134-cards.c index 3bee2d71c..aa9e3aed1 100644 --- a/linux/drivers/media/video/saa7134/saa7134-cards.c +++ b/linux/drivers/media/video/saa7134/saa7134-cards.c @@ -30,6 +30,7 @@ #include "tuner-xc2028.h" #include <media/v4l2-common.h> #include <media/tveeprom.h> +#include "tea5767.h" /* commly used strings */ static char name_mute[] = "mute"; @@ -1049,7 +1050,7 @@ struct saa7134_board saa7134_boards[] = { }, [SAA7134_BOARD_MANLI_MTV002] = { /* Ognjen Nastic <ognjen@logosoft.ba> */ - .name = "Manli MuchTV M-TV002/Behold TV 403 FM", + .name = "Manli MuchTV M-TV002", .audio_clock = 0x00200000, .tuner_type = TUNER_PHILIPS_PAL, .radio_type = UNSET, @@ -1076,7 +1077,7 @@ struct saa7134_board saa7134_boards[] = { }, [SAA7134_BOARD_MANLI_MTV001] = { /* Ognjen Nastic <ognjen@logosoft.ba> UNTESTED */ - .name = "Manli MuchTV M-TV001/Behold TV 401", + .name = "Manli MuchTV M-TV001", .audio_clock = 0x00200000, .tuner_type = TUNER_PHILIPS_PAL, .radio_type = UNSET, @@ -2198,6 +2199,8 @@ struct saa7134_board saa7134_boards[] = { }, [SAA7134_BOARD_BEHOLD_409FM] = { /* <http://tuner.beholder.ru>, Sergey <skiv@orel.ru> */ + /* Beholder Intl. Ltd. 2008 */ + /*Dmitry Belimov <d.belimov@gmail.com> */ .name = "Beholder BeholdTV 409 FM", .audio_clock = 0x00187de7, .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, @@ -2205,6 +2208,7 @@ struct saa7134_board saa7134_boards[] = { .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, .tda9887_conf = TDA9887_PRESENT, + .gpiomask = 0x00008000, .inputs = {{ .name = name_tv, .vmux = 3, @@ -3615,12 +3619,15 @@ struct saa7134_board saa7134_boards[] = { }}, }, [SAA7134_BOARD_BEHOLD_401] = { + /* Beholder Intl. Ltd. 2008 */ + /*Dmitry Belimov <d.belimov@gmail.com> */ .name = "Beholder BeholdTV 401", .audio_clock = 0x00187de7, .tuner_type = TUNER_PHILIPS_FQ1216ME, .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, + .gpiomask = 0x00008000, .inputs = {{ .name = name_svideo, .vmux = 8, @@ -3641,12 +3648,15 @@ struct saa7134_board saa7134_boards[] = { }, }, [SAA7134_BOARD_BEHOLD_403] = { + /* Beholder Intl. Ltd. 2008 */ + /*Dmitry Belimov <d.belimov@gmail.com> */ .name = "Beholder BeholdTV 403", .audio_clock = 0x00187de7, .tuner_type = TUNER_PHILIPS_FQ1216ME, .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, + .gpiomask = 0x00008000, .inputs = {{ .name = name_svideo, .vmux = 8, @@ -3663,12 +3673,15 @@ struct saa7134_board saa7134_boards[] = { }}, }, [SAA7134_BOARD_BEHOLD_403FM] = { + /* Beholder Intl. Ltd. 2008 */ + /*Dmitry Belimov <d.belimov@gmail.com> */ .name = "Beholder BeholdTV 403 FM", .audio_clock = 0x00187de7, .tuner_type = TUNER_PHILIPS_FQ1216ME, .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, + .gpiomask = 0x00008000, .inputs = {{ .name = name_svideo, .vmux = 8, @@ -3689,6 +3702,8 @@ struct saa7134_board saa7134_boards[] = { }, }, [SAA7134_BOARD_BEHOLD_405] = { + /* Beholder Intl. Ltd. 2008 */ + /*Dmitry Belimov <d.belimov@gmail.com> */ .name = "Beholder BeholdTV 405", .audio_clock = 0x00187de7, .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, @@ -3696,6 +3711,7 @@ struct saa7134_board saa7134_boards[] = { .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, .tda9887_conf = TDA9887_PRESENT, + .gpiomask = 0x00008000, .inputs = {{ .name = name_svideo, .vmux = 8, @@ -3713,6 +3729,8 @@ struct saa7134_board saa7134_boards[] = { }, [SAA7134_BOARD_BEHOLD_405FM] = { /* Sergey <skiv@orel.ru> */ + /* Beholder Intl. Ltd. 2008 */ + /*Dmitry Belimov <d.belimov@gmail.com> */ .name = "Beholder BeholdTV 405 FM", .audio_clock = 0x00187de7, .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, @@ -3720,6 +3738,7 @@ struct saa7134_board saa7134_boards[] = { .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, .tda9887_conf = TDA9887_PRESENT, + .gpiomask = 0x00008000, .inputs = {{ .name = name_svideo, .vmux = 8, @@ -3740,6 +3759,8 @@ struct saa7134_board saa7134_boards[] = { }, }, [SAA7134_BOARD_BEHOLD_407] = { + /* Beholder Intl. Ltd. 2008 */ + /*Dmitry Belimov <d.belimov@gmail.com> */ .name = "Beholder BeholdTV 407", .audio_clock = 0x00187de7, .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, @@ -3747,7 +3768,7 @@ struct saa7134_board saa7134_boards[] = { .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, .tda9887_conf = TDA9887_PRESENT, - .gpiomask = 0xc0c000, + .gpiomask = 0x00008000, .inputs = {{ .name = name_svideo, .vmux = 8, @@ -3767,6 +3788,8 @@ struct saa7134_board saa7134_boards[] = { }}, }, [SAA7134_BOARD_BEHOLD_407FM] = { + /* Beholder Intl. Ltd. 2008 */ + /*Dmitry Belimov <d.belimov@gmail.com> */ .name = "Beholder BeholdTV 407 FM", .audio_clock = 0x00187de7, .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, @@ -3774,7 +3797,7 @@ struct saa7134_board saa7134_boards[] = { .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, .tda9887_conf = TDA9887_PRESENT, - .gpiomask = 0xc0c000, + .gpiomask = 0x00008000, .inputs = {{ .name = name_svideo, .vmux = 8, @@ -3799,6 +3822,8 @@ struct saa7134_board saa7134_boards[] = { }, }, [SAA7134_BOARD_BEHOLD_409] = { + /* Beholder Intl. Ltd. 2008 */ + /*Dmitry Belimov <d.belimov@gmail.com> */ .name = "Beholder BeholdTV 409", .audio_clock = 0x00187de7, .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, @@ -3806,6 +3831,7 @@ struct saa7134_board saa7134_boards[] = { .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, .tda9887_conf = TDA9887_PRESENT, + .gpiomask = 0x00008000, .inputs = {{ .name = name_tv, .vmux = 3, @@ -3822,6 +3848,8 @@ struct saa7134_board saa7134_boards[] = { }}, }, [SAA7134_BOARD_BEHOLD_505FM] = { + /* Beholder Intl. Ltd. 2008 */ + /*Dmitry Belimov <d.belimov@gmail.com> */ .name = "Beholder BeholdTV 505 FM/RDS", .audio_clock = 0x00200000, .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, @@ -3829,6 +3857,7 @@ struct saa7134_board saa7134_boards[] = { .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, .tda9887_conf = TDA9887_PRESENT, + .gpiomask = 0x00008000, .inputs = {{ .name = name_tv, .vmux = 3, @@ -3853,6 +3882,8 @@ struct saa7134_board saa7134_boards[] = { }, }, [SAA7134_BOARD_BEHOLD_507_9FM] = { + /* Beholder Intl. Ltd. 2008 */ + /*Dmitry Belimov <d.belimov@gmail.com> */ .name = "Beholder BeholdTV 507 FM/RDS / BeholdTV 509 FM", .audio_clock = 0x00187de7, .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, @@ -3860,6 +3891,7 @@ struct saa7134_board saa7134_boards[] = { .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, .tda9887_conf = TDA9887_PRESENT, + .gpiomask = 0x00008000, .inputs = {{ .name = name_tv, .vmux = 3, @@ -3880,6 +3912,8 @@ struct saa7134_board saa7134_boards[] = { }, }, [SAA7134_BOARD_BEHOLD_COLUMBUS_TVFM] = { + /* Beholder Intl. Ltd. 2008 */ + /*Dmitry Belimov <d.belimov@gmail.com> */ .name = "Beholder BeholdTV Columbus TVFM", .audio_clock = 0x00187de7, .tuner_type = TUNER_ALPS_TSBE5_PAL, @@ -3887,23 +3921,28 @@ struct saa7134_board saa7134_boards[] = { .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, .tda9887_conf = TDA9887_PRESENT, + .gpiomask = 0x000A8004, .inputs = {{ .name = name_tv, .vmux = 3, .amux = TV, .tv = 1, - },{ + .gpio = 0x000A8004, + }, { .name = name_comp1, .vmux = 1, .amux = LINE1, - },{ + .gpio = 0x000A8000, + }, { .name = name_svideo, .vmux = 8, .amux = LINE1, - }}, + .gpio = 0x000A8000, + } }, .radio = { .name = name_radio, .amux = LINE2, + .gpio = 0x000A8000, }, }, [SAA7134_BOARD_BEHOLD_607_9FM] = { @@ -5574,7 +5613,6 @@ int saa7134_board_init1(struct saa7134_dev *dev) case SAA7134_BOARD_AVERMEDIA_CARDBUS: case SAA7134_BOARD_AVERMEDIA_CARDBUS_506: case SAA7134_BOARD_AVERMEDIA_M115: - case SAA7134_BOARD_BEHOLD_COLUMBUS_TVFM: case SAA7134_BOARD_AVERMEDIA_A16D: #if 1 /* power-down tuner chip */ @@ -5587,6 +5625,20 @@ int saa7134_board_init1(struct saa7134_dev *dev) saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, 0xffffffff, 0xffffffff); msleep(10); break; + case SAA7134_BOARD_BEHOLD_COLUMBUS_TVFM: +#if 1 + /* power-down tuner chip */ + saa_andorl(SAA7134_GPIO_GPMODE0 >> 2, 0x000A8004, 0x000A8004); + saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, 0x000A8004, 0); +#endif + msleep(10); + /* power-up tuner chip */ + saa_andorl(SAA7134_GPIO_GPMODE0 >> 2, 0x000A8004, 0x000A8004); + saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, 0x000A8004, 0x000A8004); + msleep(10); + /* remote via GPIO */ + dev->has_remote = SAA7134_REMOTE_GPIO; + break; case SAA7134_BOARD_RTD_VFG7350: /* @@ -5875,6 +5927,20 @@ int saa7134_board_init2(struct saa7134_dev *dev) break; } break; + case SAA7134_BOARD_BEHOLD_COLUMBUS_TVFM: + { + struct v4l2_priv_tun_config tea5767_cfg; + struct tea5767_ctrl ctl; + + dev->i2c_client.addr = 0xC0; + /* set TEA5767(analog FM) defines */ + memset(&ctl, 0, sizeof(ctl)); + ctl.xtal_freq = TEA5767_HIGH_LO_13MHz; + tea5767_cfg.tuner = TUNER_TEA5767; + tea5767_cfg.priv = &ctl; + saa7134_i2c_call_clients(dev, TUNER_SET_CONFIG, &tea5767_cfg); + } + break; } if (dev->tuner_type == TUNER_XC2028) { diff --git a/linux/drivers/media/video/saa7134/saa7134-i2c.c b/linux/drivers/media/video/saa7134/saa7134-i2c.c index fda6d44cd..ecf0e3443 100644 --- a/linux/drivers/media/video/saa7134/saa7134-i2c.c +++ b/linux/drivers/media/video/saa7134/saa7134-i2c.c @@ -140,6 +140,8 @@ static inline int i2c_is_busy(enum i2c_status status) { switch (status) { case BUSY: + case TO_SCL: + case TO_ARB: return true; default: return false; diff --git a/linux/drivers/media/video/saa7134/saa7134-input.c b/linux/drivers/media/video/saa7134/saa7134-input.c index 225f075e5..c63435f22 100644 --- a/linux/drivers/media/video/saa7134/saa7134-input.c +++ b/linux/drivers/media/video/saa7134/saa7134-input.c @@ -331,6 +331,11 @@ int saa7134_input_init1(struct saa7134_dev *dev) break; case SAA7134_BOARD_MANLI_MTV001: case SAA7134_BOARD_MANLI_MTV002: + ir_codes = ir_codes_manli; + mask_keycode = 0x001f00; + mask_keyup = 0x004000; + polling = 50; /* ms */ + break; case SAA7134_BOARD_BEHOLD_409FM: case SAA7134_BOARD_BEHOLD_401: case SAA7134_BOARD_BEHOLD_403: @@ -343,7 +348,13 @@ int saa7134_input_init1(struct saa7134_dev *dev) case SAA7134_BOARD_BEHOLD_505FM: case SAA7134_BOARD_BEHOLD_507_9FM: ir_codes = ir_codes_manli; - mask_keycode = 0x001f00; + mask_keycode = 0x003f00; + mask_keyup = 0x004000; + polling = 50; /* ms */ + break; + case SAA7134_BOARD_BEHOLD_COLUMBUS_TVFM: + ir_codes = ir_codes_behold_columbus; + mask_keycode = 0x003f00; mask_keyup = 0x004000; polling = 50; // ms break; diff --git a/linux/drivers/media/video/soc_camera.c b/linux/drivers/media/video/soc_camera.c index 91a1251d9..a1b92446c 100644 --- a/linux/drivers/media/video/soc_camera.c +++ b/linux/drivers/media/video/soc_camera.c @@ -144,8 +144,6 @@ static int soc_camera_reqbufs(struct file *file, void *priv, return ret; return ici->ops->reqbufs(icf, p); - - return ret; } static int soc_camera_querybuf(struct file *file, void *priv, diff --git a/linux/drivers/media/video/tea5767.c b/linux/drivers/media/video/tea5767.c index c4f39a5d3..e5bfc72a4 100644 --- a/linux/drivers/media/video/tea5767.c +++ b/linux/drivers/media/video/tea5767.c @@ -400,11 +400,13 @@ int tea5767_autodetection(struct i2c_adapter* i2c_adap, u8 i2c_addr) return EINVAL; } +#if 0 /* Not working for TEA5767 in Beholder Columbus card */ /* It seems that tea5767 returns 0xff after the 5th byte */ if ((buffer[5] != 0xff) || (buffer[6] != 0xff)) { printk(KERN_WARNING "Returned more than 5 bytes. It is not a TEA5767\n"); return EINVAL; } +#endif #if 0 /*Sometimes, this code doesn't work */ /* Sets tuner at some freq (87.5 MHz) and see if it is ok */ |