diff options
Diffstat (limited to 'linux')
23 files changed, 3255 insertions, 2031 deletions
diff --git a/linux/drivers/media/common/tuners/mxl5007t.c b/linux/drivers/media/common/tuners/mxl5007t.c index c7aa69df1..09d93144d 100644 --- a/linux/drivers/media/common/tuners/mxl5007t.c +++ b/linux/drivers/media/common/tuners/mxl5007t.c @@ -1,7 +1,7 @@ /* * mxl5007t.c - driver for the MaxLinear MxL5007T silicon tuner * - * Copyright (C) 2008 Michael Krufky <mkrufky@linuxtv.org> + * Copyright (C) 2008, 2009 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 @@ -67,22 +67,17 @@ MODULE_PARM_DESC(debug, "set debug level"); #define MHz 1000000 enum mxl5007t_mode { - MxL_MODE_OTA_DVBT_ATSC = 0, - MxL_MODE_OTA_NTSC_PAL_GH = 1, - MxL_MODE_OTA_PAL_IB = 2, - MxL_MODE_OTA_PAL_D_SECAM_KL = 3, - MxL_MODE_OTA_ISDBT = 4, - MxL_MODE_CABLE_DIGITAL = 0x10, - MxL_MODE_CABLE_NTSC_PAL_GH = 0x11, - MxL_MODE_CABLE_PAL_IB = 0x12, - MxL_MODE_CABLE_PAL_D_SECAM_KL = 0x13, - MxL_MODE_CABLE_SCTE40 = 0x14, + MxL_MODE_ISDBT = 0, + MxL_MODE_DVBT = 1, + MxL_MODE_ATSC = 2, + MxL_MODE_CABLE = 0x10, }; enum mxl5007t_chip_version { MxL_UNKNOWN_ID = 0x00, MxL_5007_V1_F1 = 0x11, MxL_5007_V1_F2 = 0x12, + MxL_5007_V4 = 0x14, MxL_5007_V2_100_F1 = 0x21, MxL_5007_V2_100_F2 = 0x22, MxL_5007_V2_200_F1 = 0x23, @@ -97,67 +92,61 @@ struct reg_pair_t { /* ------------------------------------------------------------------------- */ static struct reg_pair_t init_tab[] = { - { 0x0b, 0x44 }, /* XTAL */ - { 0x0c, 0x60 }, /* IF */ - { 0x10, 0x00 }, /* MISC */ - { 0x12, 0xca }, /* IDAC */ - { 0x16, 0x90 }, /* MODE */ - { 0x32, 0x38 }, /* MODE Analog/Digital */ - { 0xd8, 0x18 }, /* CLK_OUT_ENABLE */ - { 0x2c, 0x34 }, /* OVERRIDE */ - { 0x4d, 0x40 }, /* OVERRIDE */ - { 0x7f, 0x02 }, /* OVERRIDE */ - { 0x9a, 0x52 }, /* OVERRIDE */ - { 0x48, 0x5a }, /* OVERRIDE */ - { 0x76, 0x1a }, /* OVERRIDE */ - { 0x6a, 0x48 }, /* OVERRIDE */ - { 0x64, 0x28 }, /* OVERRIDE */ - { 0x66, 0xe6 }, /* OVERRIDE */ - { 0x35, 0x0e }, /* OVERRIDE */ - { 0x7e, 0x01 }, /* OVERRIDE */ - { 0x83, 0x00 }, /* OVERRIDE */ - { 0x04, 0x0b }, /* OVERRIDE */ - { 0x05, 0x01 }, /* TOP_MASTER_ENABLE */ + { 0x02, 0x06 }, + { 0x03, 0x48 }, + { 0x05, 0x04 }, + { 0x06, 0x10 }, + { 0x2e, 0x15 }, /* OVERRIDE */ + { 0x30, 0x10 }, /* OVERRIDE */ + { 0x45, 0x58 }, /* OVERRIDE */ + { 0x48, 0x19 }, /* OVERRIDE */ + { 0x52, 0x03 }, /* OVERRIDE */ + { 0x53, 0x44 }, /* OVERRIDE */ + { 0x6a, 0x4b }, /* OVERRIDE */ + { 0x76, 0x00 }, /* OVERRIDE */ + { 0x78, 0x18 }, /* OVERRIDE */ + { 0x7a, 0x17 }, /* OVERRIDE */ + { 0x85, 0x06 }, /* OVERRIDE */ + { 0x01, 0x01 }, /* TOP_MASTER_ENABLE */ { 0, 0 } }; static struct reg_pair_t init_tab_cable[] = { - { 0x0b, 0x44 }, /* XTAL */ - { 0x0c, 0x60 }, /* IF */ - { 0x10, 0x00 }, /* MISC */ - { 0x12, 0xca }, /* IDAC */ - { 0x16, 0x90 }, /* MODE */ - { 0x32, 0x38 }, /* MODE A/D */ - { 0x71, 0x3f }, /* TOP1 */ - { 0x72, 0x3f }, /* TOP2 */ - { 0x74, 0x3f }, /* TOP3 */ - { 0xd8, 0x18 }, /* CLK_OUT_ENABLE */ - { 0x2c, 0x34 }, /* OVERRIDE */ - { 0x4d, 0x40 }, /* OVERRIDE */ - { 0x7f, 0x02 }, /* OVERRIDE */ - { 0x9a, 0x52 }, /* OVERRIDE */ - { 0x48, 0x5a }, /* OVERRIDE */ - { 0x76, 0x1a }, /* OVERRIDE */ - { 0x6a, 0x48 }, /* OVERRIDE */ - { 0x64, 0x28 }, /* OVERRIDE */ - { 0x66, 0xe6 }, /* OVERRIDE */ - { 0x35, 0x0e }, /* OVERRIDE */ - { 0x7e, 0x01 }, /* OVERRIDE */ - { 0x04, 0x0b }, /* OVERRIDE */ - { 0x68, 0xb4 }, /* OVERRIDE */ - { 0x36, 0x00 }, /* OVERRIDE */ - { 0x05, 0x01 }, /* TOP_MASTER_ENABLE */ + { 0x02, 0x06 }, + { 0x03, 0x48 }, + { 0x05, 0x04 }, + { 0x06, 0x10 }, + { 0x09, 0x3f }, + { 0x0a, 0x3f }, + { 0x0b, 0x3f }, + { 0x2e, 0x15 }, /* OVERRIDE */ + { 0x30, 0x10 }, /* OVERRIDE */ + { 0x45, 0x58 }, /* OVERRIDE */ + { 0x48, 0x19 }, /* OVERRIDE */ + { 0x52, 0x03 }, /* OVERRIDE */ + { 0x53, 0x44 }, /* OVERRIDE */ + { 0x6a, 0x4b }, /* OVERRIDE */ + { 0x76, 0x00 }, /* OVERRIDE */ + { 0x78, 0x18 }, /* OVERRIDE */ + { 0x7a, 0x17 }, /* OVERRIDE */ + { 0x85, 0x06 }, /* OVERRIDE */ + { 0x01, 0x01 }, /* TOP_MASTER_ENABLE */ { 0, 0 } }; /* ------------------------------------------------------------------------- */ static struct reg_pair_t reg_pair_rftune[] = { - { 0x11, 0x00 }, /* abort tune */ - { 0x13, 0x15 }, - { 0x14, 0x40 }, - { 0x15, 0x0e }, - { 0x11, 0x02 }, /* start tune */ + { 0x0f, 0x00 }, /* abort tune */ + { 0x0c, 0x15 }, + { 0x0d, 0x40 }, + { 0x0e, 0x0e }, + { 0x1f, 0x87 }, /* OVERRIDE */ + { 0x20, 0x1f }, /* OVERRIDE */ + { 0x21, 0x87 }, /* OVERRIDE */ + { 0x22, 0x1f }, /* OVERRIDE */ + { 0x80, 0x01 }, /* freq dependent */ + { 0x0f, 0x01 }, /* start tune */ { 0, 0 } }; @@ -232,63 +221,20 @@ static void mxl5007t_set_mode_bits(struct mxl5007t_state *state, s32 if_diff_out_level) { switch (mode) { - case MxL_MODE_OTA_DVBT_ATSC: - set_reg_bits(state->tab_init, 0x32, 0x0f, 0x06); - set_reg_bits(state->tab_init, 0x35, 0xff, 0x0e); + case MxL_MODE_ATSC: + set_reg_bits(state->tab_init, 0x06, 0x1f, 0x12); break; - case MxL_MODE_OTA_ISDBT: - set_reg_bits(state->tab_init, 0x32, 0x0f, 0x06); - set_reg_bits(state->tab_init, 0x35, 0xff, 0x12); + case MxL_MODE_DVBT: + set_reg_bits(state->tab_init, 0x06, 0x1f, 0x11); break; - case MxL_MODE_OTA_NTSC_PAL_GH: - set_reg_bits(state->tab_init, 0x16, 0x70, 0x00); - set_reg_bits(state->tab_init, 0x32, 0xff, 0x85); + case MxL_MODE_ISDBT: + set_reg_bits(state->tab_init, 0x06, 0x1f, 0x10); break; - case MxL_MODE_OTA_PAL_IB: - set_reg_bits(state->tab_init, 0x16, 0x70, 0x10); - set_reg_bits(state->tab_init, 0x32, 0xff, 0x85); - break; - case MxL_MODE_OTA_PAL_D_SECAM_KL: - set_reg_bits(state->tab_init, 0x16, 0x70, 0x20); - set_reg_bits(state->tab_init, 0x32, 0xff, 0x85); - break; - case MxL_MODE_CABLE_DIGITAL: - set_reg_bits(state->tab_init_cable, 0x71, 0xff, 0x01); - set_reg_bits(state->tab_init_cable, 0x72, 0xff, - 8 - if_diff_out_level); - set_reg_bits(state->tab_init_cable, 0x74, 0xff, 0x17); - break; - case MxL_MODE_CABLE_NTSC_PAL_GH: - set_reg_bits(state->tab_init, 0x16, 0x70, 0x00); - set_reg_bits(state->tab_init, 0x32, 0xff, 0x85); - set_reg_bits(state->tab_init_cable, 0x71, 0xff, 0x01); - set_reg_bits(state->tab_init_cable, 0x72, 0xff, - 8 - if_diff_out_level); - set_reg_bits(state->tab_init_cable, 0x74, 0xff, 0x17); - break; - case MxL_MODE_CABLE_PAL_IB: - set_reg_bits(state->tab_init, 0x16, 0x70, 0x10); - set_reg_bits(state->tab_init, 0x32, 0xff, 0x85); - set_reg_bits(state->tab_init_cable, 0x71, 0xff, 0x01); - set_reg_bits(state->tab_init_cable, 0x72, 0xff, + case MxL_MODE_CABLE: + set_reg_bits(state->tab_init_cable, 0x09, 0xff, 0xc1); + set_reg_bits(state->tab_init_cable, 0x0a, 0xff, 8 - if_diff_out_level); - set_reg_bits(state->tab_init_cable, 0x74, 0xff, 0x17); - break; - case MxL_MODE_CABLE_PAL_D_SECAM_KL: - set_reg_bits(state->tab_init, 0x16, 0x70, 0x20); - set_reg_bits(state->tab_init, 0x32, 0xff, 0x85); - set_reg_bits(state->tab_init_cable, 0x71, 0xff, 0x01); - set_reg_bits(state->tab_init_cable, 0x72, 0xff, - 8 - if_diff_out_level); - set_reg_bits(state->tab_init_cable, 0x74, 0xff, 0x17); - break; - case MxL_MODE_CABLE_SCTE40: - set_reg_bits(state->tab_init_cable, 0x36, 0xff, 0x08); - set_reg_bits(state->tab_init_cable, 0x68, 0xff, 0xbc); - set_reg_bits(state->tab_init_cable, 0x71, 0xff, 0x01); - set_reg_bits(state->tab_init_cable, 0x72, 0xff, - 8 - if_diff_out_level); - set_reg_bits(state->tab_init_cable, 0x74, 0xff, 0x17); + set_reg_bits(state->tab_init_cable, 0x0b, 0xff, 0x17); break; default: mxl_fail(-EINVAL); @@ -307,43 +253,43 @@ static void mxl5007t_set_if_freq_bits(struct mxl5007t_state *state, val = 0x00; break; case MxL_IF_4_5_MHZ: - val = 0x20; + val = 0x02; break; case MxL_IF_4_57_MHZ: - val = 0x30; + val = 0x03; break; case MxL_IF_5_MHZ: - val = 0x40; + val = 0x04; break; case MxL_IF_5_38_MHZ: - val = 0x50; + val = 0x05; break; case MxL_IF_6_MHZ: - val = 0x60; + val = 0x06; break; case MxL_IF_6_28_MHZ: - val = 0x70; + val = 0x07; break; case MxL_IF_9_1915_MHZ: - val = 0x80; + val = 0x08; break; case MxL_IF_35_25_MHZ: - val = 0x90; + val = 0x09; break; case MxL_IF_36_15_MHZ: - val = 0xa0; + val = 0x0a; break; case MxL_IF_44_MHZ: - val = 0xb0; + val = 0x0b; break; default: mxl_fail(-EINVAL); return; } - set_reg_bits(state->tab_init, 0x0c, 0xf0, val); + set_reg_bits(state->tab_init, 0x02, 0x0f, val); /* set inverted IF or normal IF */ - set_reg_bits(state->tab_init, 0x0c, 0x08, invert_if ? 0x08 : 0x00); + set_reg_bits(state->tab_init, 0x02, 0x10, invert_if ? 0x10 : 0x00); return; } @@ -351,56 +297,68 @@ static void mxl5007t_set_if_freq_bits(struct mxl5007t_state *state, static void mxl5007t_set_xtal_freq_bits(struct mxl5007t_state *state, enum mxl5007t_xtal_freq xtal_freq) { - u8 val; - switch (xtal_freq) { case MxL_XTAL_16_MHZ: - val = 0x00; /* select xtal freq & Ref Freq */ + /* select xtal freq & ref freq */ + set_reg_bits(state->tab_init, 0x03, 0xf0, 0x00); + set_reg_bits(state->tab_init, 0x05, 0x0f, 0x00); break; case MxL_XTAL_20_MHZ: - val = 0x11; + set_reg_bits(state->tab_init, 0x03, 0xf0, 0x10); + set_reg_bits(state->tab_init, 0x05, 0x0f, 0x01); break; case MxL_XTAL_20_25_MHZ: - val = 0x22; + set_reg_bits(state->tab_init, 0x03, 0xf0, 0x20); + set_reg_bits(state->tab_init, 0x05, 0x0f, 0x02); break; case MxL_XTAL_20_48_MHZ: - val = 0x33; + set_reg_bits(state->tab_init, 0x03, 0xf0, 0x30); + set_reg_bits(state->tab_init, 0x05, 0x0f, 0x03); break; case MxL_XTAL_24_MHZ: - val = 0x44; + set_reg_bits(state->tab_init, 0x03, 0xf0, 0x40); + set_reg_bits(state->tab_init, 0x05, 0x0f, 0x04); break; case MxL_XTAL_25_MHZ: - val = 0x55; + set_reg_bits(state->tab_init, 0x03, 0xf0, 0x50); + set_reg_bits(state->tab_init, 0x05, 0x0f, 0x05); break; case MxL_XTAL_25_14_MHZ: - val = 0x66; + set_reg_bits(state->tab_init, 0x03, 0xf0, 0x60); + set_reg_bits(state->tab_init, 0x05, 0x0f, 0x06); break; case MxL_XTAL_27_MHZ: - val = 0x77; + set_reg_bits(state->tab_init, 0x03, 0xf0, 0x70); + set_reg_bits(state->tab_init, 0x05, 0x0f, 0x07); break; case MxL_XTAL_28_8_MHZ: - val = 0x88; + set_reg_bits(state->tab_init, 0x03, 0xf0, 0x80); + set_reg_bits(state->tab_init, 0x05, 0x0f, 0x08); break; case MxL_XTAL_32_MHZ: - val = 0x99; + set_reg_bits(state->tab_init, 0x03, 0xf0, 0x90); + set_reg_bits(state->tab_init, 0x05, 0x0f, 0x09); break; case MxL_XTAL_40_MHZ: - val = 0xaa; + set_reg_bits(state->tab_init, 0x03, 0xf0, 0xa0); + set_reg_bits(state->tab_init, 0x05, 0x0f, 0x0a); break; case MxL_XTAL_44_MHZ: - val = 0xbb; + set_reg_bits(state->tab_init, 0x03, 0xf0, 0xb0); + set_reg_bits(state->tab_init, 0x05, 0x0f, 0x0b); break; case MxL_XTAL_48_MHZ: - val = 0xcc; + set_reg_bits(state->tab_init, 0x03, 0xf0, 0xc0); + set_reg_bits(state->tab_init, 0x05, 0x0f, 0x0c); break; case MxL_XTAL_49_3811_MHZ: - val = 0xdd; + set_reg_bits(state->tab_init, 0x03, 0xf0, 0xd0); + set_reg_bits(state->tab_init, 0x05, 0x0f, 0x0d); break; default: mxl_fail(-EINVAL); return; } - set_reg_bits(state->tab_init, 0x0b, 0xff, val); return; } @@ -417,16 +375,11 @@ static struct reg_pair_t *mxl5007t_calc_init_regs(struct mxl5007t_state *state, mxl5007t_set_if_freq_bits(state, cfg->if_freq_hz, cfg->invert_if); mxl5007t_set_xtal_freq_bits(state, cfg->xtal_freq_hz); - set_reg_bits(state->tab_init, 0x10, 0x40, cfg->loop_thru_enable << 6); - - set_reg_bits(state->tab_init, 0xd8, 0x08, cfg->clk_out_enable << 3); - - set_reg_bits(state->tab_init, 0x10, 0x07, cfg->clk_out_amp); + set_reg_bits(state->tab_init, 0x04, 0x01, cfg->loop_thru_enable); + set_reg_bits(state->tab_init, 0x03, 0x08, cfg->clk_out_enable << 3); + set_reg_bits(state->tab_init, 0x03, 0x07, cfg->clk_out_amp); - /* set IDAC to automatic mode control by AGC */ - set_reg_bits(state->tab_init, 0x12, 0x80, 0x00); - - if (mode >= MxL_MODE_CABLE_DIGITAL) { + if (mode >= MxL_MODE_CABLE) { copy_reg_bits(state->tab_init, state->tab_init_cable); return state->tab_init_cable; } else @@ -452,7 +405,7 @@ static void mxl5007t_set_bw_bits(struct mxl5007t_state *state, * and DIG_MODEINDEX_CSF */ break; case MxL_BW_7MHz: - val = 0x21; + val = 0x2a; break; case MxL_BW_8MHz: val = 0x3f; @@ -461,7 +414,7 @@ static void mxl5007t_set_bw_bits(struct mxl5007t_state *state, mxl_fail(-EINVAL); return; } - set_reg_bits(state->tab_rftune, 0x13, 0x3f, val); + set_reg_bits(state->tab_rftune, 0x0c, 0x3f, val); return; } @@ -498,8 +451,11 @@ reg_pair_t *mxl5007t_calc_rf_tune_regs(struct mxl5007t_state *state, if (temp > 7812) dig_rf_freq++; - set_reg_bits(state->tab_rftune, 0x14, 0xff, (u8)dig_rf_freq); - set_reg_bits(state->tab_rftune, 0x15, 0xff, (u8)(dig_rf_freq >> 8)); + set_reg_bits(state->tab_rftune, 0x0d, 0xff, (u8) dig_rf_freq); + set_reg_bits(state->tab_rftune, 0x0e, 0xff, (u8) (dig_rf_freq >> 8)); + + if (rf_freq >= 333000000) + set_reg_bits(state->tab_rftune, 0x80, 0x40, 0x40); return state->tab_rftune; } @@ -556,9 +512,10 @@ static int mxl5007t_read_reg(struct mxl5007t_state *state, u8 reg, u8 *val) static int mxl5007t_soft_reset(struct mxl5007t_state *state) { u8 d = 0xff; - struct i2c_msg msg = { .addr = state->i2c_props.addr, .flags = 0, - .buf = &d, .len = 1 }; - + struct i2c_msg msg = { + .addr = state->i2c_props.addr, .flags = 0, + .buf = &d, .len = 1 + }; int ret = i2c_transfer(state->i2c_props.adap, &msg, 1); if (ret != 1) { @@ -585,9 +542,6 @@ static int mxl5007t_tuner_init(struct mxl5007t_state *state, if (mxl_fail(ret)) goto fail; mdelay(1); - - ret = mxl5007t_write_reg(state, 0x2c, 0x35); - mxl_fail(ret); fail: return ret; } @@ -620,7 +574,7 @@ static int mxl5007t_synth_lock_status(struct mxl5007t_state *state, *rf_locked = 0; *ref_locked = 0; - ret = mxl5007t_read_reg(state, 0xcf, &d); + ret = mxl5007t_read_reg(state, 0xd8, &d); if (mxl_fail(ret)) goto fail; @@ -633,37 +587,14 @@ fail: return ret; } -static int mxl5007t_check_rf_input_power(struct mxl5007t_state *state, - s32 *rf_input_level) -{ - u8 d1, d2; - int ret; - - ret = mxl5007t_read_reg(state, 0xb7, &d1); - if (mxl_fail(ret)) - goto fail; - - ret = mxl5007t_read_reg(state, 0xbf, &d2); - if (mxl_fail(ret)) - goto fail; - - d2 = d2 >> 4; - if (d2 > 7) - d2 += 0xf0; - - *rf_input_level = (s32)(d1 + d2 - 113); -fail: - return ret; -} - /* ------------------------------------------------------------------------- */ static int mxl5007t_get_status(struct dvb_frontend *fe, u32 *status) { struct mxl5007t_state *state = fe->tuner_priv; - int rf_locked, ref_locked; - s32 rf_input_level = 0; - int ret; + int rf_locked, ref_locked, ret; + + *status = 0; if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 1); @@ -674,10 +605,8 @@ static int mxl5007t_get_status(struct dvb_frontend *fe, u32 *status) mxl_debug("%s%s", rf_locked ? "rf locked " : "", ref_locked ? "ref locked" : ""); - ret = mxl5007t_check_rf_input_power(state, &rf_input_level); - if (mxl_fail(ret)) - goto fail; - mxl_debug("rf input power: %d", rf_input_level); + if ((rf_locked) || (ref_locked)) + *status |= TUNER_STATUS_LOCKED; fail: if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0); @@ -700,11 +629,11 @@ static int mxl5007t_set_params(struct dvb_frontend *fe, switch (params->u.vsb.modulation) { case VSB_8: case VSB_16: - mode = MxL_MODE_OTA_DVBT_ATSC; + mode = MxL_MODE_ATSC; break; case QAM_64: case QAM_256: - mode = MxL_MODE_CABLE_DIGITAL; + mode = MxL_MODE_CABLE; break; default: mxl_err("modulation not set!"); @@ -726,7 +655,7 @@ static int mxl5007t_set_params(struct dvb_frontend *fe, mxl_err("bandwidth not set!"); return -EINVAL; } - mode = MxL_MODE_OTA_DVBT_ATSC; + mode = MxL_MODE_DVBT; } else { mxl_err("modulation type not supported!"); return -EINVAL; @@ -757,103 +686,20 @@ fail: return ret; } -static int mxl5007t_set_analog_params(struct dvb_frontend *fe, - struct analog_parameters *params) -{ - struct mxl5007t_state *state = fe->tuner_priv; - enum mxl5007t_bw_mhz bw = 0; /* FIXME */ - enum mxl5007t_mode cbl_mode; - enum mxl5007t_mode ota_mode; - char *mode_name; - int ret; - u32 freq = params->frequency * 62500; - -#if 0 - if (params->mode == V4L2_TUNER_RADIO) { - freq = freq / 1000; - mode_name = "fm"; - } else -#else -#define cable 1 -#endif - if (params->std & V4L2_STD_MN) { - cbl_mode = MxL_MODE_CABLE_NTSC_PAL_GH; - ota_mode = MxL_MODE_OTA_NTSC_PAL_GH; - mode_name = "MN"; - } else if (params->std & V4L2_STD_B) { - cbl_mode = MxL_MODE_CABLE_PAL_IB; - ota_mode = MxL_MODE_OTA_PAL_IB; - mode_name = "B"; - } else if (params->std & V4L2_STD_GH) { - cbl_mode = MxL_MODE_CABLE_NTSC_PAL_GH; - ota_mode = MxL_MODE_OTA_NTSC_PAL_GH; - mode_name = "GH"; - } else if (params->std & V4L2_STD_PAL_I) { - cbl_mode = MxL_MODE_CABLE_PAL_IB; - ota_mode = MxL_MODE_OTA_PAL_IB; - mode_name = "I"; - } else if (params->std & V4L2_STD_DK) { - cbl_mode = MxL_MODE_CABLE_PAL_D_SECAM_KL; - ota_mode = MxL_MODE_OTA_PAL_D_SECAM_KL; - mode_name = "DK"; - } else if (params->std & V4L2_STD_SECAM_L) { - cbl_mode = MxL_MODE_CABLE_PAL_D_SECAM_KL; - ota_mode = MxL_MODE_OTA_PAL_D_SECAM_KL; - mode_name = "L"; - } else if (params->std & V4L2_STD_SECAM_LC) { - cbl_mode = MxL_MODE_CABLE_PAL_D_SECAM_KL; - ota_mode = MxL_MODE_OTA_PAL_D_SECAM_KL; - mode_name = "L'"; - } else { - mode_name = "xx"; - /* FIXME */ - cbl_mode = MxL_MODE_CABLE_NTSC_PAL_GH; - ota_mode = MxL_MODE_OTA_NTSC_PAL_GH; - } - mxl_debug("setting mxl5007 to system %s", mode_name); - - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); - - mutex_lock(&state->lock); - - ret = mxl5007t_tuner_init(state, cable ? cbl_mode : ota_mode); - if (mxl_fail(ret)) - goto fail; - - ret = mxl5007t_tuner_rf_tune(state, freq, bw); - if (mxl_fail(ret)) - goto fail; - - state->frequency = freq; - state->bandwidth = 0; -fail: - mutex_unlock(&state->lock); - - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 0); - - return ret; -} - /* ------------------------------------------------------------------------- */ static int mxl5007t_init(struct dvb_frontend *fe) { struct mxl5007t_state *state = fe->tuner_priv; int ret; - u8 d; if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 1); - ret = mxl5007t_read_reg(state, 0x05, &d); - if (mxl_fail(ret)) - goto fail; - - ret = mxl5007t_write_reg(state, 0x05, d | 0x01); + /* wake from standby */ + ret = mxl5007t_write_reg(state, 0x01, 0x01); mxl_fail(ret); -fail: + if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0); @@ -864,18 +710,16 @@ static int mxl5007t_sleep(struct dvb_frontend *fe) { struct mxl5007t_state *state = fe->tuner_priv; int ret; - u8 d; if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 1); - ret = mxl5007t_read_reg(state, 0x05, &d); - if (mxl_fail(ret)) - goto fail; - - ret = mxl5007t_write_reg(state, 0x05, d & ~0x01); + /* enter standby mode */ + ret = mxl5007t_write_reg(state, 0x01, 0x00); mxl_fail(ret); -fail: + ret = mxl5007t_write_reg(state, 0x0f, 0x00); + mxl_fail(ret); + if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0); @@ -928,7 +772,6 @@ static struct dvb_tuner_ops mxl5007t_tuner_ops = { .init = mxl5007t_init, .sleep = mxl5007t_sleep, .set_params = mxl5007t_set_params, - .set_analog_params = mxl5007t_set_analog_params, .get_status = mxl5007t_get_status, .get_frequency = mxl5007t_get_frequency, .get_bandwidth = mxl5007t_get_bandwidth, @@ -941,7 +784,7 @@ static int mxl5007t_get_chip_id(struct mxl5007t_state *state) int ret; u8 id; - ret = mxl5007t_read_reg(state, 0xd3, &id); + ret = mxl5007t_read_reg(state, 0xd9, &id); if (mxl_fail(ret)) goto fail; @@ -964,12 +807,16 @@ static int mxl5007t_get_chip_id(struct mxl5007t_state *state) case MxL_5007_V2_200_F2: name = "MxL5007.v2.200.f2"; break; + case MxL_5007_V4: + name = "MxL5007T.v4"; + break; default: #if 0 ret = -EINVAL; goto fail; #else name = "MxL5007T"; + printk(KERN_WARNING "%s: unknown rev (%02x)\n", __func__, id); id = MxL_UNKNOWN_ID; #endif } @@ -997,7 +844,7 @@ struct dvb_frontend *mxl5007t_attach(struct dvb_frontend *fe, mutex_lock(&mxl5007t_list_mutex); instance = hybrid_tuner_request_state(struct mxl5007t_state, state, hybrid_tuner_instance_list, - i2c, addr, "mxl5007"); + i2c, addr, "mxl5007t"); switch (instance) { case 0: goto fail; @@ -1040,7 +887,7 @@ EXPORT_SYMBOL_GPL(mxl5007t_attach); MODULE_DESCRIPTION("MaxLinear MxL5007T Silicon IC tuner driver"); MODULE_AUTHOR("Michael Krufky <mkrufky@linuxtv.org>"); MODULE_LICENSE("GPL"); -MODULE_VERSION("0.1"); +MODULE_VERSION("0.2"); /* * Overrides for Emacs so that we follow Linus's tabbing style. diff --git a/linux/drivers/media/dvb/Kconfig b/linux/drivers/media/dvb/Kconfig index 40ebde53b..b01986918 100644 --- a/linux/drivers/media/dvb/Kconfig +++ b/linux/drivers/media/dvb/Kconfig @@ -51,6 +51,10 @@ comment "Supported SDMC DM1105 Adapters" depends on DVB_CORE && PCI && I2C source "drivers/media/dvb/dm1105/Kconfig" +comment "Supported FireWire (IEEE 1394) Adapters" + depends on DVB_CORE && IEEE1394 +source "drivers/media/dvb/firewire/Kconfig" + comment "Supported DVB Frontends" depends on DVB_CORE source "drivers/media/dvb/frontends/Kconfig" diff --git a/linux/drivers/media/dvb/Makefile b/linux/drivers/media/dvb/Makefile index f91e9eb15..6092a5bb5 100644 --- a/linux/drivers/media/dvb/Makefile +++ b/linux/drivers/media/dvb/Makefile @@ -3,3 +3,5 @@ # obj-y := dvb-core/ frontends/ ttpci/ ttusb-dec/ ttusb-budget/ b2c2/ bt8xx/ dvb-usb/ pluto2/ siano/ dm1105/ + +obj-$(CONFIG_DVB_FIREDTV) += firewire/ diff --git a/linux/drivers/media/dvb/firewire/Kconfig b/linux/drivers/media/dvb/firewire/Kconfig new file mode 100644 index 000000000..69028253e --- /dev/null +++ b/linux/drivers/media/dvb/firewire/Kconfig @@ -0,0 +1,22 @@ +config DVB_FIREDTV + tristate "FireDTV and FloppyDTV" + depends on DVB_CORE && IEEE1394 + help + Support for DVB receivers from Digital Everywhere + which are connected via IEEE 1394 (FireWire). + + These devices don't have an MPEG decoder built in, + so you need an external software decoder to watch TV. + + To compile this driver as a module, say M here: + the module will be called firedtv. + +if DVB_FIREDTV + +config DVB_FIREDTV_IEEE1394 + def_bool IEEE1394 + +config DVB_FIREDTV_INPUT + def_bool INPUT = y || (INPUT = m && DVB_FIREDTV = m) + +endif # DVB_FIREDTV diff --git a/linux/drivers/media/dvb/firewire/Makefile b/linux/drivers/media/dvb/firewire/Makefile new file mode 100644 index 000000000..2034695ba --- /dev/null +++ b/linux/drivers/media/dvb/firewire/Makefile @@ -0,0 +1,8 @@ +obj-$(CONFIG_DVB_FIREDTV) += firedtv.o + +firedtv-y := firedtv-avc.o firedtv-ci.o firedtv-dvb.o firedtv-fe.o +firedtv-$(CONFIG_DVB_FIREDTV_IEEE1394) += firedtv-1394.o +firedtv-$(CONFIG_DVB_FIREDTV_INPUT) += firedtv-rc.o + +ccflags-y += -Idrivers/media/dvb/dvb-core +ccflags-$(CONFIG_DVB_FIREDTV_IEEE1394) += -Idrivers/ieee1394 diff --git a/linux/drivers/media/dvb/firewire/firedtv-1394.c b/linux/drivers/media/dvb/firewire/firedtv-1394.c new file mode 100644 index 000000000..4e207658c --- /dev/null +++ b/linux/drivers/media/dvb/firewire/firedtv-1394.c @@ -0,0 +1,285 @@ +/* + * FireDTV driver (formerly known as FireSAT) + * + * Copyright (C) 2004 Andreas Monitzer <andy@monitzer.com> + * Copyright (C) 2007-2008 Ben Backx <ben@bbackx.com> + * Copyright (C) 2008 Henrik Kurelid <henrik@kurelid.se> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + */ + +#include <linux/device.h> +#include <linux/errno.h> +#include <linux/kernel.h> +#include <linux/list.h> +#include <linux/spinlock.h> +#include <linux/types.h> + +#include <dma.h> +#include <csr1212.h> +#include <highlevel.h> +#include <hosts.h> +#include <ieee1394.h> +#include <iso.h> +#include <nodemgr.h> + +#include "firedtv.h" + +static LIST_HEAD(node_list); +static DEFINE_SPINLOCK(node_list_lock); + +#define FIREWIRE_HEADER_SIZE 4 +#define CIP_HEADER_SIZE 8 + +static void rawiso_activity_cb(struct hpsb_iso *iso) +{ + struct firedtv *f, *fdtv = NULL; + unsigned int i, num, packet; + unsigned char *buf; + unsigned long flags; + int count; + + spin_lock_irqsave(&node_list_lock, flags); + list_for_each_entry(f, &node_list, list) + if (f->backend_data == iso) { + fdtv = f; + break; + } + spin_unlock_irqrestore(&node_list_lock, flags); + + packet = iso->first_packet; + num = hpsb_iso_n_ready(iso); + + if (!fdtv) { + dev_err(fdtv->device, "received at unknown iso channel\n"); + goto out; + } + + for (i = 0; i < num; i++, packet = (packet + 1) % iso->buf_packets) { + buf = dma_region_i(&iso->data_buf, unsigned char, + iso->infos[packet].offset + CIP_HEADER_SIZE); + count = (iso->infos[packet].len - CIP_HEADER_SIZE) / + (188 + FIREWIRE_HEADER_SIZE); + + /* ignore empty packet */ + if (iso->infos[packet].len <= CIP_HEADER_SIZE) + continue; + + while (count--) { + if (buf[FIREWIRE_HEADER_SIZE] == 0x47) + dvb_dmx_swfilter_packets(&fdtv->demux, + &buf[FIREWIRE_HEADER_SIZE], 1); + else + dev_err(fdtv->device, + "skipping invalid packet\n"); + buf += 188 + FIREWIRE_HEADER_SIZE; + } + } +out: + hpsb_iso_recv_release_packets(iso, num); +} + +static inline struct node_entry *node_of(struct firedtv *fdtv) +{ + return container_of(fdtv->device, struct unit_directory, device)->ne; +} + +static int node_lock(struct firedtv *fdtv, u64 addr, void *data, __be32 arg) +{ + return hpsb_node_lock(node_of(fdtv), addr, EXTCODE_COMPARE_SWAP, data, + (__force quadlet_t)arg); +} + +static int node_read(struct firedtv *fdtv, u64 addr, void *data, size_t len) +{ + return hpsb_node_read(node_of(fdtv), addr, data, len); +} + +static int node_write(struct firedtv *fdtv, u64 addr, void *data, size_t len) +{ + return hpsb_node_write(node_of(fdtv), addr, data, len); +} + +#define FDTV_ISO_BUFFER_PACKETS 256 +#define FDTV_ISO_BUFFER_SIZE (FDTV_ISO_BUFFER_PACKETS * 200) + +static int start_iso(struct firedtv *fdtv) +{ + struct hpsb_iso *iso_handle; + int ret; + + iso_handle = hpsb_iso_recv_init(node_of(fdtv)->host, + FDTV_ISO_BUFFER_SIZE, FDTV_ISO_BUFFER_PACKETS, + fdtv->isochannel, HPSB_ISO_DMA_DEFAULT, + -1, /* stat.config.irq_interval */ + rawiso_activity_cb); + if (iso_handle == NULL) { + dev_err(fdtv->device, "cannot initialize iso receive\n"); + return -ENOMEM; + } + fdtv->backend_data = iso_handle; + + ret = hpsb_iso_recv_start(iso_handle, -1, -1, 0); + if (ret != 0) { + dev_err(fdtv->device, "cannot start iso receive\n"); + hpsb_iso_shutdown(iso_handle); + fdtv->backend_data = NULL; + } + return ret; +} + +static void stop_iso(struct firedtv *fdtv) +{ + struct hpsb_iso *iso_handle = fdtv->backend_data; + + if (iso_handle != NULL) { + hpsb_iso_stop(iso_handle); + hpsb_iso_shutdown(iso_handle); + } + fdtv->backend_data = NULL; +} + +static const struct firedtv_backend fdtv_1394_backend = { + .lock = node_lock, + .read = node_read, + .write = node_write, + .start_iso = start_iso, + .stop_iso = stop_iso, +}; + +static void fcp_request(struct hpsb_host *host, int nodeid, int direction, + int cts, u8 *data, size_t length) +{ + struct firedtv *f, *fdtv = NULL; + unsigned long flags; + int su; + + if (length == 0 || (data[0] & 0xf0) != 0) + return; + + su = data[1] & 0x7; + + spin_lock_irqsave(&node_list_lock, flags); + list_for_each_entry(f, &node_list, list) + if (node_of(f)->host == host && + node_of(f)->nodeid == nodeid && + (f->subunit == su || (f->subunit == 0 && su == 0x7))) { + fdtv = f; + break; + } + spin_unlock_irqrestore(&node_list_lock, flags); + + if (fdtv) + avc_recv(fdtv, data, length); +} + +static int node_probe(struct device *dev) +{ + struct unit_directory *ud = + container_of(dev, struct unit_directory, device); + struct firedtv *fdtv; + int kv_len, err; + void *kv_str; + + kv_len = (ud->model_name_kv->value.leaf.len - 2) * sizeof(quadlet_t); + kv_str = CSR1212_TEXTUAL_DESCRIPTOR_LEAF_DATA(ud->model_name_kv); + + fdtv = fdtv_alloc(dev, &fdtv_1394_backend, kv_str, kv_len); + if (!fdtv) + return -ENOMEM; + + /* + * Work around a bug in udev's path_id script: Use the fw-host's dev + * instead of the unit directory's dev as parent of the input device. + */ + err = fdtv_register_rc(fdtv, dev->parent->parent); + if (err) + goto fail_free; + + spin_lock_irq(&node_list_lock); + list_add_tail(&fdtv->list, &node_list); + spin_unlock_irq(&node_list_lock); + + err = avc_identify_subunit(fdtv); + if (err) + goto fail; + + err = fdtv_dvb_register(fdtv); + if (err) + goto fail; + + avc_register_remote_control(fdtv); + return 0; +fail: + spin_lock_irq(&node_list_lock); + list_del(&fdtv->list); + spin_unlock_irq(&node_list_lock); + fdtv_unregister_rc(fdtv); +fail_free: + kfree(fdtv); + return err; +} + +static int node_remove(struct device *dev) +{ + struct firedtv *fdtv = dev->driver_data; + + fdtv_dvb_unregister(fdtv); + + spin_lock_irq(&node_list_lock); + list_del(&fdtv->list); + spin_unlock_irq(&node_list_lock); + + cancel_work_sync(&fdtv->remote_ctrl_work); + fdtv_unregister_rc(fdtv); + + kfree(fdtv); + return 0; +} + +static int node_update(struct unit_directory *ud) +{ + struct firedtv *fdtv = ud->device.driver_data; + + if (fdtv->isochannel >= 0) + cmp_establish_pp_connection(fdtv, fdtv->subunit, + fdtv->isochannel); + return 0; +} + +static struct hpsb_protocol_driver fdtv_driver = { + .name = "firedtv", + .update = node_update, + .driver = { + .probe = node_probe, + .remove = node_remove, + }, +}; + +static struct hpsb_highlevel fdtv_highlevel = { + .name = "firedtv", + .fcp_request = fcp_request, +}; + +int __init fdtv_1394_init(struct ieee1394_device_id id_table[]) +{ + int ret; + + hpsb_register_highlevel(&fdtv_highlevel); + fdtv_driver.id_table = id_table; + ret = hpsb_register_protocol(&fdtv_driver); + if (ret) { + printk(KERN_ERR "firedtv: failed to register protocol\n"); + hpsb_unregister_highlevel(&fdtv_highlevel); + } + return ret; +} + +void __exit fdtv_1394_exit(void) +{ + hpsb_unregister_protocol(&fdtv_driver); + hpsb_unregister_highlevel(&fdtv_highlevel); +} diff --git a/linux/drivers/media/dvb/firewire/firedtv-avc.c b/linux/drivers/media/dvb/firewire/firedtv-avc.c new file mode 100644 index 000000000..b55d9ccaf --- /dev/null +++ b/linux/drivers/media/dvb/firewire/firedtv-avc.c @@ -0,0 +1,1315 @@ +/* + * FireDTV driver (formerly known as FireSAT) + * + * Copyright (C) 2004 Andreas Monitzer <andy@monitzer.com> + * Copyright (C) 2008 Ben Backx <ben@bbackx.com> + * Copyright (C) 2008 Henrik Kurelid <henrik@kurelid.se> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + */ + +#include <linux/bug.h> +#include <linux/crc32.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/jiffies.h> +#include <linux/kernel.h> +#include <linux/moduleparam.h> +#include <linux/mutex.h> +#include <linux/string.h> +#include <linux/stringify.h> +#include <linux/wait.h> +#include <linux/workqueue.h> + +#include "firedtv.h" + +#define FCP_COMMAND_REGISTER 0xfffff0000b00ULL + +#define AVC_CTYPE_CONTROL 0x0 +#define AVC_CTYPE_STATUS 0x1 +#define AVC_CTYPE_NOTIFY 0x3 + +#define AVC_RESPONSE_ACCEPTED 0x9 +#define AVC_RESPONSE_STABLE 0xc +#define AVC_RESPONSE_CHANGED 0xd +#define AVC_RESPONSE_INTERIM 0xf + +#define AVC_SUBUNIT_TYPE_TUNER (0x05 << 3) +#define AVC_SUBUNIT_TYPE_UNIT (0x1f << 3) + +#define AVC_OPCODE_VENDOR 0x00 +#define AVC_OPCODE_READ_DESCRIPTOR 0x09 +#define AVC_OPCODE_DSIT 0xc8 +#define AVC_OPCODE_DSD 0xcb + +#define DESCRIPTOR_TUNER_STATUS 0x80 +#define DESCRIPTOR_SUBUNIT_IDENTIFIER 0x00 + +#define SFE_VENDOR_DE_COMPANYID_0 0x00 /* OUI of Digital Everywhere */ +#define SFE_VENDOR_DE_COMPANYID_1 0x12 +#define SFE_VENDOR_DE_COMPANYID_2 0x87 + +#define SFE_VENDOR_OPCODE_REGISTER_REMOTE_CONTROL 0x0a +#define SFE_VENDOR_OPCODE_LNB_CONTROL 0x52 +#define SFE_VENDOR_OPCODE_TUNE_QPSK 0x58 /* for DVB-S */ + +#define SFE_VENDOR_OPCODE_GET_FIRMWARE_VERSION 0x00 +#define SFE_VENDOR_OPCODE_HOST2CA 0x56 +#define SFE_VENDOR_OPCODE_CA2HOST 0x57 +#define SFE_VENDOR_OPCODE_CISTATUS 0x59 +#define SFE_VENDOR_OPCODE_TUNE_QPSK2 0x60 /* for DVB-S2 */ + +#define SFE_VENDOR_TAG_CA_RESET 0x00 +#define SFE_VENDOR_TAG_CA_APPLICATION_INFO 0x01 +#define SFE_VENDOR_TAG_CA_PMT 0x02 +#define SFE_VENDOR_TAG_CA_DATE_TIME 0x04 +#define SFE_VENDOR_TAG_CA_MMI 0x05 +#define SFE_VENDOR_TAG_CA_ENTER_MENU 0x07 + +#define EN50221_LIST_MANAGEMENT_ONLY 0x03 +#define EN50221_TAG_APP_INFO 0x9f8021 +#define EN50221_TAG_CA_INFO 0x9f8031 + +struct avc_command_frame { + int length; + u8 ctype; + u8 subunit; + u8 opcode; + u8 operand[509]; +}; + +struct avc_response_frame { + int length; + u8 response; + u8 subunit; + u8 opcode; + u8 operand[509]; +}; + +#define AVC_DEBUG_FCP_SUBACTIONS 1 +#define AVC_DEBUG_FCP_PAYLOADS 2 + +static int avc_debug; +module_param_named(debug, avc_debug, int, 0644); +MODULE_PARM_DESC(debug, "Verbose logging (default = 0" + ", FCP subactions = " __stringify(AVC_DEBUG_FCP_SUBACTIONS) + ", FCP payloads = " __stringify(AVC_DEBUG_FCP_PAYLOADS) + ", or all = -1)"); + +static const char *debug_fcp_ctype(unsigned int ctype) +{ + static const char *ctypes[] = { + [0x0] = "CONTROL", [0x1] = "STATUS", + [0x2] = "SPECIFIC INQUIRY", [0x3] = "NOTIFY", + [0x4] = "GENERAL INQUIRY", [0x8] = "NOT IMPLEMENTED", + [0x9] = "ACCEPTED", [0xa] = "REJECTED", + [0xb] = "IN TRANSITION", [0xc] = "IMPLEMENTED/STABLE", + [0xd] = "CHANGED", [0xf] = "INTERIM", + }; + const char *ret = ctype < ARRAY_SIZE(ctypes) ? ctypes[ctype] : NULL; + + return ret ? ret : "?"; +} + +static const char *debug_fcp_opcode(unsigned int opcode, + const u8 *data, size_t length) +{ + switch (opcode) { + case AVC_OPCODE_VENDOR: break; + case AVC_OPCODE_READ_DESCRIPTOR: return "ReadDescriptor"; + case AVC_OPCODE_DSIT: return "DirectSelectInfo.Type"; + case AVC_OPCODE_DSD: return "DirectSelectData"; + default: return "?"; + } + + if (length < 7 || + data[3] != SFE_VENDOR_DE_COMPANYID_0 || + data[4] != SFE_VENDOR_DE_COMPANYID_1 || + data[5] != SFE_VENDOR_DE_COMPANYID_2) + return "Vendor"; + + switch (data[6]) { + case SFE_VENDOR_OPCODE_REGISTER_REMOTE_CONTROL: return "RegisterRC"; + case SFE_VENDOR_OPCODE_LNB_CONTROL: return "LNBControl"; + case SFE_VENDOR_OPCODE_TUNE_QPSK: return "TuneQPSK"; + case SFE_VENDOR_OPCODE_HOST2CA: return "Host2CA"; + case SFE_VENDOR_OPCODE_CA2HOST: return "CA2Host"; + } + return "Vendor"; +} + +static void debug_fcp(const u8 *data, size_t length) +{ + unsigned int subunit_type, subunit_id, op; + const char *prefix = data[0] > 7 ? "FCP <- " : "FCP -> "; + + if (avc_debug & AVC_DEBUG_FCP_SUBACTIONS) { + subunit_type = data[1] >> 3; + subunit_id = data[1] & 7; + op = subunit_type == 0x1e || subunit_id == 5 ? ~0 : data[2]; + printk(KERN_INFO "%ssu=%x.%x l=%d: %-8s - %s\n", + prefix, subunit_type, subunit_id, length, + debug_fcp_ctype(data[0]), + debug_fcp_opcode(op, data, length)); + } + + if (avc_debug & AVC_DEBUG_FCP_PAYLOADS) + print_hex_dump(KERN_INFO, prefix, DUMP_PREFIX_NONE, 16, 1, + data, length, false); +} + +static int __avc_write(struct firedtv *fdtv, + const struct avc_command_frame *c, struct avc_response_frame *r) +{ + int err, retry; + + if (r) + fdtv->avc_reply_received = false; + + for (retry = 0; retry < 6; retry++) { + if (unlikely(avc_debug)) + debug_fcp(&c->ctype, c->length); + + err = fdtv->backend->write(fdtv, FCP_COMMAND_REGISTER, + (void *)&c->ctype, c->length); + if (err) { + fdtv->avc_reply_received = true; + dev_err(fdtv->device, "FCP command write failed\n"); + return err; + } + + if (!r) + return 0; + + /* + * AV/C specs say that answers should be sent within 150 ms. + * Time out after 200 ms. + */ + if (wait_event_timeout(fdtv->avc_wait, + fdtv->avc_reply_received, + msecs_to_jiffies(200)) != 0) { + r->length = fdtv->response_length; + memcpy(&r->response, fdtv->response, r->length); + + return 0; + } + } + dev_err(fdtv->device, "FCP response timed out\n"); + return -ETIMEDOUT; +} + +static int avc_write(struct firedtv *fdtv, + const struct avc_command_frame *c, struct avc_response_frame *r) +{ + int ret; + + if (mutex_lock_interruptible(&fdtv->avc_mutex)) + return -EINTR; + + ret = __avc_write(fdtv, c, r); + + mutex_unlock(&fdtv->avc_mutex); + return ret; +} + +int avc_recv(struct firedtv *fdtv, void *data, size_t length) +{ + struct avc_response_frame *r = + data - offsetof(struct avc_response_frame, response); + + if (unlikely(avc_debug)) + debug_fcp(data, length); + + if (length >= 8 && + r->operand[0] == SFE_VENDOR_DE_COMPANYID_0 && + r->operand[1] == SFE_VENDOR_DE_COMPANYID_1 && + r->operand[2] == SFE_VENDOR_DE_COMPANYID_2 && + r->operand[3] == SFE_VENDOR_OPCODE_REGISTER_REMOTE_CONTROL) { + if (r->response == AVC_RESPONSE_CHANGED) { + fdtv_handle_rc(fdtv, + r->operand[4] << 8 | r->operand[5]); + schedule_work(&fdtv->remote_ctrl_work); + } else if (r->response != AVC_RESPONSE_INTERIM) { + dev_info(fdtv->device, + "remote control result = %d\n", r->response); + } + return 0; + } + + if (fdtv->avc_reply_received) { + dev_err(fdtv->device, "out-of-order AVC response, ignored\n"); + return -EIO; + } + + memcpy(fdtv->response, data, length); + fdtv->response_length = length; + + fdtv->avc_reply_received = true; + wake_up(&fdtv->avc_wait); + + return 0; +} + +/* + * tuning command for setting the relative LNB frequency + * (not supported by the AVC standard) + */ +static void avc_tuner_tuneqpsk(struct firedtv *fdtv, + struct dvb_frontend_parameters *params, + struct avc_command_frame *c) +{ + c->opcode = AVC_OPCODE_VENDOR; + + c->operand[0] = SFE_VENDOR_DE_COMPANYID_0; + c->operand[1] = SFE_VENDOR_DE_COMPANYID_1; + c->operand[2] = SFE_VENDOR_DE_COMPANYID_2; + c->operand[3] = SFE_VENDOR_OPCODE_TUNE_QPSK; + + c->operand[4] = (params->frequency >> 24) & 0xff; + c->operand[5] = (params->frequency >> 16) & 0xff; + c->operand[6] = (params->frequency >> 8) & 0xff; + c->operand[7] = params->frequency & 0xff; + + c->operand[8] = ((params->u.qpsk.symbol_rate / 1000) >> 8) & 0xff; + c->operand[9] = (params->u.qpsk.symbol_rate / 1000) & 0xff; + + switch (params->u.qpsk.fec_inner) { + case FEC_1_2: c->operand[10] = 0x1; break; + case FEC_2_3: c->operand[10] = 0x2; break; + case FEC_3_4: c->operand[10] = 0x3; break; + case FEC_5_6: c->operand[10] = 0x4; break; + case FEC_7_8: c->operand[10] = 0x5; break; + case FEC_4_5: + case FEC_8_9: + case FEC_AUTO: + default: c->operand[10] = 0x0; + } + + if (fdtv->voltage == 0xff) + c->operand[11] = 0xff; + else if (fdtv->voltage == SEC_VOLTAGE_18) /* polarisation */ + c->operand[11] = 0; + else + c->operand[11] = 1; + + if (fdtv->tone == 0xff) + c->operand[12] = 0xff; + else if (fdtv->tone == SEC_TONE_ON) /* band */ + c->operand[12] = 1; + else + c->operand[12] = 0; + + if (fdtv->type == FIREDTV_DVB_S2) { + c->operand[13] = 0x1; + c->operand[14] = 0xff; + c->operand[15] = 0xff; + c->length = 20; + } else { + c->length = 16; + } +} + +static void avc_tuner_dsd_dvb_c(struct dvb_frontend_parameters *params, + struct avc_command_frame *c) +{ + c->opcode = AVC_OPCODE_DSD; + + c->operand[0] = 0; /* source plug */ + c->operand[1] = 0xd2; /* subfunction replace */ + c->operand[2] = 0x20; /* system id = DVB */ + c->operand[3] = 0x00; /* antenna number */ + c->operand[4] = 0x11; /* system_specific_multiplex selection_length */ + + /* multiplex_valid_flags, high byte */ + c->operand[5] = 0 << 7 /* reserved */ + | 0 << 6 /* Polarisation */ + | 0 << 5 /* Orbital_Pos */ + | 1 << 4 /* Frequency */ + | 1 << 3 /* Symbol_Rate */ + | 0 << 2 /* FEC_outer */ + | (params->u.qam.fec_inner != FEC_AUTO ? 1 << 1 : 0) + | (params->u.qam.modulation != QAM_AUTO ? 1 << 0 : 0); + + /* multiplex_valid_flags, low byte */ + c->operand[6] = 0 << 7 /* NetworkID */ + | 0 << 0 /* reserved */ ; + + c->operand[7] = 0x00; + c->operand[8] = 0x00; + c->operand[9] = 0x00; + c->operand[10] = 0x00; + + c->operand[11] = (((params->frequency / 4000) >> 16) & 0xff) | (2 << 6); + c->operand[12] = ((params->frequency / 4000) >> 8) & 0xff; + c->operand[13] = (params->frequency / 4000) & 0xff; + c->operand[14] = ((params->u.qpsk.symbol_rate / 1000) >> 12) & 0xff; + c->operand[15] = ((params->u.qpsk.symbol_rate / 1000) >> 4) & 0xff; + c->operand[16] = ((params->u.qpsk.symbol_rate / 1000) << 4) & 0xf0; + c->operand[17] = 0x00; + + switch (params->u.qpsk.fec_inner) { + case FEC_1_2: c->operand[18] = 0x1; break; + case FEC_2_3: c->operand[18] = 0x2; break; + case FEC_3_4: c->operand[18] = 0x3; break; + case FEC_5_6: c->operand[18] = 0x4; break; + case FEC_7_8: c->operand[18] = 0x5; break; + case FEC_8_9: c->operand[18] = 0x6; break; + case FEC_4_5: c->operand[18] = 0x8; break; + case FEC_AUTO: + default: c->operand[18] = 0x0; + } + + switch (params->u.qam.modulation) { + case QAM_16: c->operand[19] = 0x08; break; + case QAM_32: c->operand[19] = 0x10; break; + case QAM_64: c->operand[19] = 0x18; break; + case QAM_128: c->operand[19] = 0x20; break; + case QAM_256: c->operand[19] = 0x28; break; + case QAM_AUTO: + default: c->operand[19] = 0x00; + } + + c->operand[20] = 0x00; + c->operand[21] = 0x00; + /* Nr_of_dsd_sel_specs = 0 -> no PIDs are transmitted */ + c->operand[22] = 0x00; + + c->length = 28; +} + +static void avc_tuner_dsd_dvb_t(struct dvb_frontend_parameters *params, + struct avc_command_frame *c) +{ + struct dvb_ofdm_parameters *ofdm = ¶ms->u.ofdm; + + c->opcode = AVC_OPCODE_DSD; + + c->operand[0] = 0; /* source plug */ + c->operand[1] = 0xd2; /* subfunction replace */ + c->operand[2] = 0x20; /* system id = DVB */ + c->operand[3] = 0x00; /* antenna number */ + c->operand[4] = 0x0c; /* system_specific_multiplex selection_length */ + + /* multiplex_valid_flags, high byte */ + c->operand[5] = + 0 << 7 /* reserved */ + | 1 << 6 /* CenterFrequency */ + | (ofdm->bandwidth != BANDWIDTH_AUTO ? 1 << 5 : 0) + | (ofdm->constellation != QAM_AUTO ? 1 << 4 : 0) + | (ofdm->hierarchy_information != HIERARCHY_AUTO ? 1 << 3 : 0) + | (ofdm->code_rate_HP != FEC_AUTO ? 1 << 2 : 0) + | (ofdm->code_rate_LP != FEC_AUTO ? 1 << 1 : 0) + | (ofdm->guard_interval != GUARD_INTERVAL_AUTO ? 1 << 0 : 0); + + /* multiplex_valid_flags, low byte */ + c->operand[6] = + 0 << 7 /* NetworkID */ + | (ofdm->transmission_mode != TRANSMISSION_MODE_AUTO ? 1 << 6 : 0) + | 0 << 5 /* OtherFrequencyFlag */ + | 0 << 0 /* reserved */ ; + + c->operand[7] = 0x0; + c->operand[8] = (params->frequency / 10) >> 24; + c->operand[9] = ((params->frequency / 10) >> 16) & 0xff; + c->operand[10] = ((params->frequency / 10) >> 8) & 0xff; + c->operand[11] = (params->frequency / 10) & 0xff; + + switch (ofdm->bandwidth) { + case BANDWIDTH_7_MHZ: c->operand[12] = 0x20; break; + case BANDWIDTH_8_MHZ: + case BANDWIDTH_6_MHZ: /* not defined by AVC spec */ + case BANDWIDTH_AUTO: + default: c->operand[12] = 0x00; + } + + switch (ofdm->constellation) { + case QAM_16: c->operand[13] = 1 << 6; break; + case QAM_64: c->operand[13] = 2 << 6; break; + case QPSK: + default: c->operand[13] = 0x00; + } + + switch (ofdm->hierarchy_information) { + case HIERARCHY_1: c->operand[13] |= 1 << 3; break; + case HIERARCHY_2: c->operand[13] |= 2 << 3; break; + case HIERARCHY_4: c->operand[13] |= 3 << 3; break; + case HIERARCHY_AUTO: + case HIERARCHY_NONE: + default: break; + } + + switch (ofdm->code_rate_HP) { + case FEC_2_3: c->operand[13] |= 1; break; + case FEC_3_4: c->operand[13] |= 2; break; + case FEC_5_6: c->operand[13] |= 3; break; + case FEC_7_8: c->operand[13] |= 4; break; + case FEC_1_2: + default: break; + } + + switch (ofdm->code_rate_LP) { + case FEC_2_3: c->operand[14] = 1 << 5; break; + case FEC_3_4: c->operand[14] = 2 << 5; break; + case FEC_5_6: c->operand[14] = 3 << 5; break; + case FEC_7_8: c->operand[14] = 4 << 5; break; + case FEC_1_2: + default: c->operand[14] = 0x00; break; + } + + switch (ofdm->guard_interval) { + case GUARD_INTERVAL_1_16: c->operand[14] |= 1 << 3; break; + case GUARD_INTERVAL_1_8: c->operand[14] |= 2 << 3; break; + case GUARD_INTERVAL_1_4: c->operand[14] |= 3 << 3; break; + case GUARD_INTERVAL_1_32: + case GUARD_INTERVAL_AUTO: + default: break; + } + + switch (ofdm->transmission_mode) { + case TRANSMISSION_MODE_8K: c->operand[14] |= 1 << 1; break; + case TRANSMISSION_MODE_2K: + case TRANSMISSION_MODE_AUTO: + default: break; + } + + c->operand[15] = 0x00; /* network_ID[0] */ + c->operand[16] = 0x00; /* network_ID[1] */ + /* Nr_of_dsd_sel_specs = 0 -> no PIDs are transmitted */ + c->operand[17] = 0x00; + + c->length = 24; +} + +int avc_tuner_dsd(struct firedtv *fdtv, + struct dvb_frontend_parameters *params) +{ + char buffer[sizeof(struct avc_command_frame)]; + struct avc_command_frame *c = (void *)buffer; + struct avc_response_frame *r = (void *)buffer; /* FIXME: unused */ + + memset(c, 0, sizeof(*c)); + + c->ctype = AVC_CTYPE_CONTROL; + c->subunit = AVC_SUBUNIT_TYPE_TUNER | fdtv->subunit; + + switch (fdtv->type) { + case FIREDTV_DVB_S: + case FIREDTV_DVB_S2: avc_tuner_tuneqpsk(fdtv, params, c); break; + case FIREDTV_DVB_C: avc_tuner_dsd_dvb_c(params, c); break; + case FIREDTV_DVB_T: avc_tuner_dsd_dvb_t(params, c); break; + default: + BUG(); + } + + if (avc_write(fdtv, c, r) < 0) + return -EIO; + + msleep(500); +#if 0 + /* FIXME: */ + /* u8 *status was an out-parameter of avc_tuner_dsd, unused by caller */ + if (status) + *status = r->operand[2]; +#endif + return 0; +} + +int avc_tuner_set_pids(struct firedtv *fdtv, unsigned char pidc, u16 pid[]) +{ + char buffer[sizeof(struct avc_command_frame)]; + struct avc_command_frame *c = (void *)buffer; + struct avc_response_frame *r = (void *)buffer; /* FIXME: unused */ + int pos, k; + + if (pidc > 16 && pidc != 0xff) + return -EINVAL; + + memset(c, 0, sizeof(*c)); + + c->ctype = AVC_CTYPE_CONTROL; + c->subunit = AVC_SUBUNIT_TYPE_TUNER | fdtv->subunit; + c->opcode = AVC_OPCODE_DSD; + + c->operand[0] = 0; /* source plug */ + c->operand[1] = 0xd2; /* subfunction replace */ + c->operand[2] = 0x20; /* system id = DVB */ + c->operand[3] = 0x00; /* antenna number */ + c->operand[4] = 0x00; /* system_specific_multiplex selection_length */ + c->operand[5] = pidc; /* Nr_of_dsd_sel_specs */ + + pos = 6; + if (pidc != 0xff) + for (k = 0; k < pidc; k++) { + c->operand[pos++] = 0x13; /* flowfunction relay */ + c->operand[pos++] = 0x80; /* dsd_sel_spec_valid_flags -> PID */ + c->operand[pos++] = (pid[k] >> 8) & 0x1f; + c->operand[pos++] = pid[k] & 0xff; + c->operand[pos++] = 0x00; /* tableID */ + c->operand[pos++] = 0x00; /* filter_length */ + } + + c->length = ALIGN(3 + pos, 4); + + if (avc_write(fdtv, c, r) < 0) + return -EIO; + + msleep(50); + return 0; +} + +int avc_tuner_get_ts(struct firedtv *fdtv) +{ + char buffer[sizeof(struct avc_command_frame)]; + struct avc_command_frame *c = (void *)buffer; + struct avc_response_frame *r = (void *)buffer; /* FIXME: unused */ + int sl; + + memset(c, 0, sizeof(*c)); + + c->ctype = AVC_CTYPE_CONTROL; + c->subunit = AVC_SUBUNIT_TYPE_TUNER | fdtv->subunit; + c->opcode = AVC_OPCODE_DSIT; + + sl = fdtv->type == FIREDTV_DVB_T ? 0x0c : 0x11; + + c->operand[0] = 0; /* source plug */ + c->operand[1] = 0xd2; /* subfunction replace */ + c->operand[2] = 0xff; /* status */ + c->operand[3] = 0x20; /* system id = DVB */ + c->operand[4] = 0x00; /* antenna number */ + c->operand[5] = 0x0; /* system_specific_search_flags */ + c->operand[6] = sl; /* system_specific_multiplex selection_length */ + c->operand[7] = 0x00; /* valid_flags [0] */ + c->operand[8] = 0x00; /* valid_flags [1] */ + c->operand[7 + sl] = 0x00; /* nr_of_dsit_sel_specs (always 0) */ + + c->length = fdtv->type == FIREDTV_DVB_T ? 24 : 28; + + if (avc_write(fdtv, c, r) < 0) + return -EIO; + + msleep(250); + return 0; +} + +int avc_identify_subunit(struct firedtv *fdtv) +{ + char buffer[sizeof(struct avc_command_frame)]; + struct avc_command_frame *c = (void *)buffer; + struct avc_response_frame *r = (void *)buffer; + + memset(c, 0, sizeof(*c)); + + c->ctype = AVC_CTYPE_CONTROL; + c->subunit = AVC_SUBUNIT_TYPE_TUNER | fdtv->subunit; + c->opcode = AVC_OPCODE_READ_DESCRIPTOR; + + c->operand[0] = DESCRIPTOR_SUBUNIT_IDENTIFIER; + c->operand[1] = 0xff; + c->operand[2] = 0x00; + c->operand[3] = 0x00; /* length highbyte */ + c->operand[4] = 0x08; /* length lowbyte */ + c->operand[5] = 0x00; /* offset highbyte */ + c->operand[6] = 0x0d; /* offset lowbyte */ + + c->length = 12; + + if (avc_write(fdtv, c, r) < 0) + return -EIO; + + if ((r->response != AVC_RESPONSE_STABLE && + r->response != AVC_RESPONSE_ACCEPTED) || + (r->operand[3] << 8) + r->operand[4] != 8) { + dev_err(fdtv->device, "cannot read subunit identifier\n"); + return -EINVAL; + } + return 0; +} + +#define SIZEOF_ANTENNA_INPUT_INFO 22 + +int avc_tuner_status(struct firedtv *fdtv, struct firedtv_tuner_status *stat) +{ + char buffer[sizeof(struct avc_command_frame)]; + struct avc_command_frame *c = (void *)buffer; + struct avc_response_frame *r = (void *)buffer; + int length; + + memset(c, 0, sizeof(*c)); + + c->ctype = AVC_CTYPE_CONTROL; + c->subunit = AVC_SUBUNIT_TYPE_TUNER | fdtv->subunit; + c->opcode = AVC_OPCODE_READ_DESCRIPTOR; + + c->operand[0] = DESCRIPTOR_TUNER_STATUS; + c->operand[1] = 0xff; /* read_result_status */ + c->operand[2] = 0x00; /* reserved */ + c->operand[3] = 0; /* SIZEOF_ANTENNA_INPUT_INFO >> 8; */ + c->operand[4] = 0; /* SIZEOF_ANTENNA_INPUT_INFO & 0xff; */ + c->operand[5] = 0x00; + c->operand[6] = 0x00; + + c->length = 12; + + if (avc_write(fdtv, c, r) < 0) + return -EIO; + + if (r->response != AVC_RESPONSE_STABLE && + r->response != AVC_RESPONSE_ACCEPTED) { + dev_err(fdtv->device, "cannot read tuner status\n"); + return -EINVAL; + } + + length = r->operand[9]; + if (r->operand[1] != 0x10 || length != SIZEOF_ANTENNA_INPUT_INFO) { + dev_err(fdtv->device, "got invalid tuner status\n"); + return -EINVAL; + } + + stat->active_system = r->operand[10]; + stat->searching = r->operand[11] >> 7 & 1; + stat->moving = r->operand[11] >> 6 & 1; + stat->no_rf = r->operand[11] >> 5 & 1; + stat->input = r->operand[12] >> 7 & 1; + stat->selected_antenna = r->operand[12] & 0x7f; + stat->ber = r->operand[13] << 24 | + r->operand[14] << 16 | + r->operand[15] << 8 | + r->operand[16]; + stat->signal_strength = r->operand[17]; + stat->raster_frequency = r->operand[18] >> 6 & 2; + stat->rf_frequency = (r->operand[18] & 0x3f) << 16 | + r->operand[19] << 8 | + r->operand[20]; + stat->man_dep_info_length = r->operand[21]; + stat->front_end_error = r->operand[22] >> 4 & 1; + stat->antenna_error = r->operand[22] >> 3 & 1; + stat->front_end_power_status = r->operand[22] >> 1 & 1; + stat->power_supply = r->operand[22] & 1; + stat->carrier_noise_ratio = r->operand[23] << 8 | + r->operand[24]; + stat->power_supply_voltage = r->operand[27]; + stat->antenna_voltage = r->operand[28]; + stat->firewire_bus_voltage = r->operand[29]; + stat->ca_mmi = r->operand[30] & 1; + stat->ca_pmt_reply = r->operand[31] >> 7 & 1; + stat->ca_date_time_request = r->operand[31] >> 6 & 1; + stat->ca_application_info = r->operand[31] >> 5 & 1; + stat->ca_module_present_status = r->operand[31] >> 4 & 1; + stat->ca_dvb_flag = r->operand[31] >> 3 & 1; + stat->ca_error_flag = r->operand[31] >> 2 & 1; + stat->ca_initialization_status = r->operand[31] >> 1 & 1; + + return 0; +} + +int avc_lnb_control(struct firedtv *fdtv, char voltage, char burst, + char conttone, char nrdiseq, + struct dvb_diseqc_master_cmd *diseqcmd) +{ + char buffer[sizeof(struct avc_command_frame)]; + struct avc_command_frame *c = (void *)buffer; + struct avc_response_frame *r = (void *)buffer; + int i, j, k; + + memset(c, 0, sizeof(*c)); + + c->ctype = AVC_CTYPE_CONTROL; + c->subunit = AVC_SUBUNIT_TYPE_TUNER | fdtv->subunit; + c->opcode = AVC_OPCODE_VENDOR; + + c->operand[0] = SFE_VENDOR_DE_COMPANYID_0; + c->operand[1] = SFE_VENDOR_DE_COMPANYID_1; + c->operand[2] = SFE_VENDOR_DE_COMPANYID_2; + c->operand[3] = SFE_VENDOR_OPCODE_LNB_CONTROL; + + c->operand[4] = voltage; + c->operand[5] = nrdiseq; + + i = 6; + + for (j = 0; j < nrdiseq; j++) { + c->operand[i++] = diseqcmd[j].msg_len; + + for (k = 0; k < diseqcmd[j].msg_len; k++) + c->operand[i++] = diseqcmd[j].msg[k]; + } + + c->operand[i++] = burst; + c->operand[i++] = conttone; + + c->length = ALIGN(3 + i, 4); + + if (avc_write(fdtv, c, r) < 0) + return -EIO; + + if (r->response != AVC_RESPONSE_ACCEPTED) { + dev_err(fdtv->device, "LNB control failed\n"); + return -EINVAL; + } + + return 0; +} + +int avc_register_remote_control(struct firedtv *fdtv) +{ + char buffer[sizeof(struct avc_command_frame)]; + struct avc_command_frame *c = (void *)buffer; + + memset(c, 0, sizeof(*c)); + + c->ctype = AVC_CTYPE_NOTIFY; + c->subunit = AVC_SUBUNIT_TYPE_UNIT | 7; + c->opcode = AVC_OPCODE_VENDOR; + + c->operand[0] = SFE_VENDOR_DE_COMPANYID_0; + c->operand[1] = SFE_VENDOR_DE_COMPANYID_1; + c->operand[2] = SFE_VENDOR_DE_COMPANYID_2; + c->operand[3] = SFE_VENDOR_OPCODE_REGISTER_REMOTE_CONTROL; + + c->length = 8; + + return avc_write(fdtv, c, NULL); +} + +void avc_remote_ctrl_work(struct work_struct *work) +{ + struct firedtv *fdtv = + container_of(work, struct firedtv, remote_ctrl_work); + + /* Should it be rescheduled in failure cases? */ + avc_register_remote_control(fdtv); +} + +#if 0 /* FIXME: unused */ +int avc_tuner_host2ca(struct firedtv *fdtv) +{ + char buffer[sizeof(struct avc_command_frame)]; + struct avc_command_frame *c = (void *)buffer; + struct avc_response_frame *r = (void *)buffer; /* FIXME: unused */ + + memset(c, 0, sizeof(*c)); + + c->ctype = AVC_CTYPE_CONTROL; + c->subunit = AVC_SUBUNIT_TYPE_TUNER | fdtv->subunit; + c->opcode = AVC_OPCODE_VENDOR; + + c->operand[0] = SFE_VENDOR_DE_COMPANYID_0; + c->operand[1] = SFE_VENDOR_DE_COMPANYID_1; + c->operand[2] = SFE_VENDOR_DE_COMPANYID_2; + c->operand[3] = SFE_VENDOR_OPCODE_HOST2CA; + c->operand[4] = 0; /* slot */ + c->operand[5] = SFE_VENDOR_TAG_CA_APPLICATION_INFO; /* ca tag */ + c->operand[6] = 0; /* more/last */ + c->operand[7] = 0; /* length */ + + c->length = 12; + + if (avc_write(fdtv, c, r) < 0) + return -EIO; + + return 0; +} +#endif + +static int get_ca_object_pos(struct avc_response_frame *r) +{ + int length = 1; + + /* Check length of length field */ + if (r->operand[7] & 0x80) + length = (r->operand[7] & 0x7f) + 1; + return length + 7; +} + +static int get_ca_object_length(struct avc_response_frame *r) +{ +#if 0 /* FIXME: unused */ + int size = 0; + int i; + + if (r->operand[7] & 0x80) + for (i = 0; i < (r->operand[7] & 0x7f); i++) { + size <<= 8; + size += r->operand[8 + i]; + } +#endif + return r->operand[7]; +} + +int avc_ca_app_info(struct firedtv *fdtv, char *app_info, unsigned int *len) +{ + char buffer[sizeof(struct avc_command_frame)]; + struct avc_command_frame *c = (void *)buffer; + struct avc_response_frame *r = (void *)buffer; + int pos; + + memset(c, 0, sizeof(*c)); + + c->ctype = AVC_CTYPE_STATUS; + c->subunit = AVC_SUBUNIT_TYPE_TUNER | fdtv->subunit; + c->opcode = AVC_OPCODE_VENDOR; + + c->operand[0] = SFE_VENDOR_DE_COMPANYID_0; + c->operand[1] = SFE_VENDOR_DE_COMPANYID_1; + c->operand[2] = SFE_VENDOR_DE_COMPANYID_2; + c->operand[3] = SFE_VENDOR_OPCODE_CA2HOST; + c->operand[4] = 0; /* slot */ + c->operand[5] = SFE_VENDOR_TAG_CA_APPLICATION_INFO; /* ca tag */ + + c->length = 12; + + if (avc_write(fdtv, c, r) < 0) + return -EIO; + + /* FIXME: check response code and validate response data */ + + pos = get_ca_object_pos(r); + app_info[0] = (EN50221_TAG_APP_INFO >> 16) & 0xff; + app_info[1] = (EN50221_TAG_APP_INFO >> 8) & 0xff; + app_info[2] = (EN50221_TAG_APP_INFO >> 0) & 0xff; + app_info[3] = 6 + r->operand[pos + 4]; + app_info[4] = 0x01; + memcpy(&app_info[5], &r->operand[pos], 5 + r->operand[pos + 4]); + *len = app_info[3] + 4; + + return 0; +} + +int avc_ca_info(struct firedtv *fdtv, char *app_info, unsigned int *len) +{ + char buffer[sizeof(struct avc_command_frame)]; + struct avc_command_frame *c = (void *)buffer; + struct avc_response_frame *r = (void *)buffer; + int pos; + + memset(c, 0, sizeof(*c)); + + c->ctype = AVC_CTYPE_STATUS; + c->subunit = AVC_SUBUNIT_TYPE_TUNER | fdtv->subunit; + c->opcode = AVC_OPCODE_VENDOR; + + c->operand[0] = SFE_VENDOR_DE_COMPANYID_0; + c->operand[1] = SFE_VENDOR_DE_COMPANYID_1; + c->operand[2] = SFE_VENDOR_DE_COMPANYID_2; + c->operand[3] = SFE_VENDOR_OPCODE_CA2HOST; + c->operand[4] = 0; /* slot */ + c->operand[5] = SFE_VENDOR_TAG_CA_APPLICATION_INFO; /* ca tag */ + + c->length = 12; + + if (avc_write(fdtv, c, r) < 0) + return -EIO; + + pos = get_ca_object_pos(r); + app_info[0] = (EN50221_TAG_CA_INFO >> 16) & 0xff; + app_info[1] = (EN50221_TAG_CA_INFO >> 8) & 0xff; + app_info[2] = (EN50221_TAG_CA_INFO >> 0) & 0xff; + app_info[3] = 2; + app_info[4] = r->operand[pos + 0]; + app_info[5] = r->operand[pos + 1]; + *len = app_info[3] + 4; + + return 0; +} + +int avc_ca_reset(struct firedtv *fdtv) +{ + char buffer[sizeof(struct avc_command_frame)]; + struct avc_command_frame *c = (void *)buffer; + struct avc_response_frame *r = (void *)buffer; /* FIXME: unused */ + + memset(c, 0, sizeof(*c)); + + c->ctype = AVC_CTYPE_CONTROL; + c->subunit = AVC_SUBUNIT_TYPE_TUNER | fdtv->subunit; + c->opcode = AVC_OPCODE_VENDOR; + + c->operand[0] = SFE_VENDOR_DE_COMPANYID_0; + c->operand[1] = SFE_VENDOR_DE_COMPANYID_1; + c->operand[2] = SFE_VENDOR_DE_COMPANYID_2; + c->operand[3] = SFE_VENDOR_OPCODE_HOST2CA; + c->operand[4] = 0; /* slot */ + c->operand[5] = SFE_VENDOR_TAG_CA_RESET; /* ca tag */ + c->operand[6] = 0; /* more/last */ + c->operand[7] = 1; /* length */ + c->operand[8] = 0; /* force hardware reset */ + + c->length = 12; + + if (avc_write(fdtv, c, r) < 0) + return -EIO; + + return 0; +} + +int avc_ca_pmt(struct firedtv *fdtv, char *msg, int length) +{ + char buffer[sizeof(struct avc_command_frame)]; + struct avc_command_frame *c = (void *)buffer; + struct avc_response_frame *r = (void *)buffer; + int list_management; + int program_info_length; + int pmt_cmd_id; + int read_pos; + int write_pos; + int es_info_length; + int crc32_csum; + + memset(c, 0, sizeof(*c)); + + c->ctype = AVC_CTYPE_CONTROL; + c->subunit = AVC_SUBUNIT_TYPE_TUNER | fdtv->subunit; + c->opcode = AVC_OPCODE_VENDOR; + + if (msg[0] != EN50221_LIST_MANAGEMENT_ONLY) { + dev_info(fdtv->device, "forcing list_management to ONLY\n"); + msg[0] = EN50221_LIST_MANAGEMENT_ONLY; + } + /* We take the cmd_id from the programme level only! */ + list_management = msg[0]; + program_info_length = ((msg[4] & 0x0f) << 8) + msg[5]; + if (program_info_length > 0) + program_info_length--; /* Remove pmt_cmd_id */ + pmt_cmd_id = msg[6]; + + c->operand[0] = SFE_VENDOR_DE_COMPANYID_0; + c->operand[1] = SFE_VENDOR_DE_COMPANYID_1; + c->operand[2] = SFE_VENDOR_DE_COMPANYID_2; + c->operand[3] = SFE_VENDOR_OPCODE_HOST2CA; + c->operand[4] = 0; /* slot */ + c->operand[5] = SFE_VENDOR_TAG_CA_PMT; /* ca tag */ + c->operand[6] = 0; /* more/last */ + /* c->operand[7] = XXXprogram_info_length + 17; */ /* length */ + c->operand[8] = list_management; + c->operand[9] = 0x01; /* pmt_cmd=OK_descramble */ + + /* TS program map table */ + + c->operand[10] = 0x02; /* Table id=2 */ + c->operand[11] = 0x80; /* Section syntax + length */ + /* c->operand[12] = XXXprogram_info_length + 12; */ + c->operand[13] = msg[1]; /* Program number */ + c->operand[14] = msg[2]; + c->operand[15] = 0x01; /* Version number=0 + current/next=1 */ + c->operand[16] = 0x00; /* Section number=0 */ + c->operand[17] = 0x00; /* Last section number=0 */ + c->operand[18] = 0x1f; /* PCR_PID=1FFF */ + c->operand[19] = 0xff; + c->operand[20] = (program_info_length >> 8); /* Program info length */ + c->operand[21] = (program_info_length & 0xff); + + /* CA descriptors at programme level */ + read_pos = 6; + write_pos = 22; + if (program_info_length > 0) { + pmt_cmd_id = msg[read_pos++]; + if (pmt_cmd_id != 1 && pmt_cmd_id != 4) + dev_err(fdtv->device, + "invalid pmt_cmd_id %d\n", pmt_cmd_id); + + memcpy(&c->operand[write_pos], &msg[read_pos], + program_info_length); + read_pos += program_info_length; + write_pos += program_info_length; + } + while (read_pos < length) { + c->operand[write_pos++] = msg[read_pos++]; + c->operand[write_pos++] = msg[read_pos++]; + c->operand[write_pos++] = msg[read_pos++]; + es_info_length = + ((msg[read_pos] & 0x0f) << 8) + msg[read_pos + 1]; + read_pos += 2; + if (es_info_length > 0) + es_info_length--; /* Remove pmt_cmd_id */ + c->operand[write_pos++] = es_info_length >> 8; + c->operand[write_pos++] = es_info_length & 0xff; + if (es_info_length > 0) { + pmt_cmd_id = msg[read_pos++]; + if (pmt_cmd_id != 1 && pmt_cmd_id != 4) + dev_err(fdtv->device, "invalid pmt_cmd_id %d " + "at stream level\n", pmt_cmd_id); + + memcpy(&c->operand[write_pos], &msg[read_pos], + es_info_length); + read_pos += es_info_length; + write_pos += es_info_length; + } + } + + /* CRC */ + c->operand[write_pos++] = 0x00; + c->operand[write_pos++] = 0x00; + c->operand[write_pos++] = 0x00; + c->operand[write_pos++] = 0x00; + + c->operand[7] = write_pos - 8; + c->operand[12] = write_pos - 13; + + crc32_csum = crc32_be(0, &c->operand[10], c->operand[12] - 1); + c->operand[write_pos - 4] = (crc32_csum >> 24) & 0xff; + c->operand[write_pos - 3] = (crc32_csum >> 16) & 0xff; + c->operand[write_pos - 2] = (crc32_csum >> 8) & 0xff; + c->operand[write_pos - 1] = (crc32_csum >> 0) & 0xff; + + c->length = ALIGN(3 + write_pos, 4); + + if (avc_write(fdtv, c, r) < 0) + return -EIO; + + if (r->response != AVC_RESPONSE_ACCEPTED) { + dev_err(fdtv->device, + "CA PMT failed with response 0x%x\n", r->response); + return -EFAULT; + } + + return 0; +} + +int avc_ca_get_time_date(struct firedtv *fdtv, int *interval) +{ + char buffer[sizeof(struct avc_command_frame)]; + struct avc_command_frame *c = (void *)buffer; + struct avc_response_frame *r = (void *)buffer; + + memset(c, 0, sizeof(*c)); + + c->ctype = AVC_CTYPE_STATUS; + c->subunit = AVC_SUBUNIT_TYPE_TUNER | fdtv->subunit; + c->opcode = AVC_OPCODE_VENDOR; + + c->operand[0] = SFE_VENDOR_DE_COMPANYID_0; + c->operand[1] = SFE_VENDOR_DE_COMPANYID_1; + c->operand[2] = SFE_VENDOR_DE_COMPANYID_2; + c->operand[3] = SFE_VENDOR_OPCODE_CA2HOST; + c->operand[4] = 0; /* slot */ + c->operand[5] = SFE_VENDOR_TAG_CA_DATE_TIME; /* ca tag */ + c->operand[6] = 0; /* more/last */ + c->operand[7] = 0; /* length */ + + c->length = 12; + + if (avc_write(fdtv, c, r) < 0) + return -EIO; + + /* FIXME: check response code and validate response data */ + + *interval = r->operand[get_ca_object_pos(r)]; + + return 0; +} + +int avc_ca_enter_menu(struct firedtv *fdtv) +{ + char buffer[sizeof(struct avc_command_frame)]; + struct avc_command_frame *c = (void *)buffer; + struct avc_response_frame *r = (void *)buffer; /* FIXME: unused */ + + memset(c, 0, sizeof(*c)); + + c->ctype = AVC_CTYPE_STATUS; + c->subunit = AVC_SUBUNIT_TYPE_TUNER | fdtv->subunit; + c->opcode = AVC_OPCODE_VENDOR; + + c->operand[0] = SFE_VENDOR_DE_COMPANYID_0; + c->operand[1] = SFE_VENDOR_DE_COMPANYID_1; + c->operand[2] = SFE_VENDOR_DE_COMPANYID_2; + c->operand[3] = SFE_VENDOR_OPCODE_HOST2CA; + c->operand[4] = 0; /* slot */ + c->operand[5] = SFE_VENDOR_TAG_CA_ENTER_MENU; + c->operand[6] = 0; /* more/last */ + c->operand[7] = 0; /* length */ + + c->length = 12; + + if (avc_write(fdtv, c, r) < 0) + return -EIO; + + return 0; +} + +int avc_ca_get_mmi(struct firedtv *fdtv, char *mmi_object, unsigned int *len) +{ + char buffer[sizeof(struct avc_command_frame)]; + struct avc_command_frame *c = (void *)buffer; + struct avc_response_frame *r = (void *)buffer; + + memset(c, 0, sizeof(*c)); + + c->ctype = AVC_CTYPE_STATUS; + c->subunit = AVC_SUBUNIT_TYPE_TUNER | fdtv->subunit; + c->opcode = AVC_OPCODE_VENDOR; + + c->operand[0] = SFE_VENDOR_DE_COMPANYID_0; + c->operand[1] = SFE_VENDOR_DE_COMPANYID_1; + c->operand[2] = SFE_VENDOR_DE_COMPANYID_2; + c->operand[3] = SFE_VENDOR_OPCODE_CA2HOST; + c->operand[4] = 0; /* slot */ + c->operand[5] = SFE_VENDOR_TAG_CA_MMI; + c->operand[6] = 0; /* more/last */ + c->operand[7] = 0; /* length */ + + c->length = 12; + + if (avc_write(fdtv, c, r) < 0) + return -EIO; + + /* FIXME: check response code and validate response data */ + + *len = get_ca_object_length(r); + memcpy(mmi_object, &r->operand[get_ca_object_pos(r)], *len); + + return 0; +} + +#define CMP_OUTPUT_PLUG_CONTROL_REG_0 0xfffff0000904ULL + +static int cmp_read(struct firedtv *fdtv, void *buf, u64 addr, size_t len) +{ + int ret; + + if (mutex_lock_interruptible(&fdtv->avc_mutex)) + return -EINTR; + + ret = fdtv->backend->read(fdtv, addr, buf, len); + if (ret < 0) + dev_err(fdtv->device, "CMP: read I/O error\n"); + + mutex_unlock(&fdtv->avc_mutex); + return ret; +} + +static int cmp_lock(struct firedtv *fdtv, void *data, u64 addr, __be32 arg) +{ + int ret; + + if (mutex_lock_interruptible(&fdtv->avc_mutex)) + return -EINTR; + + ret = fdtv->backend->lock(fdtv, addr, data, arg); + if (ret < 0) + dev_err(fdtv->device, "CMP: lock I/O error\n"); + + mutex_unlock(&fdtv->avc_mutex); + return ret; +} + +static inline u32 get_opcr(__be32 opcr, u32 mask, u32 shift) +{ + return (be32_to_cpu(opcr) >> shift) & mask; +} + +static inline void set_opcr(__be32 *opcr, u32 value, u32 mask, u32 shift) +{ + *opcr &= ~cpu_to_be32(mask << shift); + *opcr |= cpu_to_be32((value & mask) << shift); +} + +#define get_opcr_online(v) get_opcr((v), 0x1, 31) +#define get_opcr_p2p_connections(v) get_opcr((v), 0x3f, 24) +#define get_opcr_channel(v) get_opcr((v), 0x3f, 16) + +#define set_opcr_p2p_connections(p, v) set_opcr((p), (v), 0x3f, 24) +#define set_opcr_channel(p, v) set_opcr((p), (v), 0x3f, 16) +#define set_opcr_data_rate(p, v) set_opcr((p), (v), 0x3, 14) +#define set_opcr_overhead_id(p, v) set_opcr((p), (v), 0xf, 10) + +int cmp_establish_pp_connection(struct firedtv *fdtv, int plug, int channel) +{ + __be32 old_opcr, opcr; + u64 opcr_address = CMP_OUTPUT_PLUG_CONTROL_REG_0 + (plug << 2); + int attempts = 0; + int ret; + + ret = cmp_read(fdtv, &opcr, opcr_address, 4); + if (ret < 0) + return ret; + +repeat: + if (!get_opcr_online(opcr)) { + dev_err(fdtv->device, "CMP: output offline\n"); + return -EBUSY; + } + + old_opcr = opcr; + + if (get_opcr_p2p_connections(opcr)) { + if (get_opcr_channel(opcr) != channel) { + dev_err(fdtv->device, "CMP: cannot change channel\n"); + return -EBUSY; + } + dev_info(fdtv->device, "CMP: overlaying connection\n"); + + /* We don't allocate isochronous resources. */ + } else { + set_opcr_channel(&opcr, channel); + set_opcr_data_rate(&opcr, 2); /* S400 */ + + /* FIXME: this is for the worst case - optimize */ + set_opcr_overhead_id(&opcr, 0); + + /* + * FIXME: allocate isochronous channel and bandwidth at IRM + * fdtv->backend->alloc_resources(fdtv, channels_mask, bw); + */ + } + + set_opcr_p2p_connections(&opcr, get_opcr_p2p_connections(opcr) + 1); + + ret = cmp_lock(fdtv, &opcr, opcr_address, old_opcr); + if (ret < 0) + return ret; + + if (old_opcr != opcr) { + /* + * FIXME: if old_opcr.P2P_Connections > 0, + * deallocate isochronous channel and bandwidth at IRM + * if (...) + * fdtv->backend->dealloc_resources(fdtv, channel, bw); + */ + + if (++attempts < 6) /* arbitrary limit */ + goto repeat; + return -EBUSY; + } + + return 0; +} + +void cmp_break_pp_connection(struct firedtv *fdtv, int plug, int channel) +{ + __be32 old_opcr, opcr; + u64 opcr_address = CMP_OUTPUT_PLUG_CONTROL_REG_0 + (plug << 2); + int attempts = 0; + + if (cmp_read(fdtv, &opcr, opcr_address, 4) < 0) + return; + +repeat: + if (!get_opcr_online(opcr) || !get_opcr_p2p_connections(opcr) || + get_opcr_channel(opcr) != channel) { + dev_err(fdtv->device, "CMP: no connection to break\n"); + return; + } + + old_opcr = opcr; + set_opcr_p2p_connections(&opcr, get_opcr_p2p_connections(opcr) - 1); + + if (cmp_lock(fdtv, &opcr, opcr_address, old_opcr) < 0) + return; + + if (old_opcr != opcr) { + /* + * FIXME: if old_opcr.P2P_Connections == 1, i.e. we were last + * owner, deallocate isochronous channel and bandwidth at IRM + * if (...) + * fdtv->backend->dealloc_resources(fdtv, channel, bw); + */ + + if (++attempts < 6) /* arbitrary limit */ + goto repeat; + } +} diff --git a/linux/drivers/media/dvb/firewire/firedtv-ci.c b/linux/drivers/media/dvb/firewire/firedtv-ci.c new file mode 100644 index 000000000..eeb80d0ea --- /dev/null +++ b/linux/drivers/media/dvb/firewire/firedtv-ci.c @@ -0,0 +1,260 @@ +/* + * FireDTV driver (formerly known as FireSAT) + * + * Copyright (C) 2004 Andreas Monitzer <andy@monitzer.com> + * Copyright (C) 2008 Henrik Kurelid <henrik@kurelid.se> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + */ + +#include <linux/device.h> +#include <linux/dvb/ca.h> +#include <linux/fs.h> +#include <linux/module.h> + +#include <dvbdev.h> + +#include "firedtv.h" + +#define EN50221_TAG_APP_INFO_ENQUIRY 0x9f8020 +#define EN50221_TAG_CA_INFO_ENQUIRY 0x9f8030 +#define EN50221_TAG_CA_PMT 0x9f8032 +#define EN50221_TAG_ENTER_MENU 0x9f8022 + +static int fdtv_ca_ready(struct firedtv_tuner_status *stat) +{ + return stat->ca_initialization_status == 1 && + stat->ca_error_flag == 0 && + stat->ca_dvb_flag == 1 && + stat->ca_module_present_status == 1; +} + +static int fdtv_get_ca_flags(struct firedtv_tuner_status *stat) +{ + int flags = 0; + + if (stat->ca_module_present_status == 1) + flags |= CA_CI_MODULE_PRESENT; + if (stat->ca_initialization_status == 1 && + stat->ca_error_flag == 0 && + stat->ca_dvb_flag == 1) + flags |= CA_CI_MODULE_READY; + return flags; +} + +static int fdtv_ca_reset(struct firedtv *fdtv) +{ + return avc_ca_reset(fdtv) ? -EFAULT : 0; +} + +static int fdtv_ca_get_caps(void *arg) +{ + struct ca_caps *cap = arg; + + cap->slot_num = 1; + cap->slot_type = CA_CI; + cap->descr_num = 1; + cap->descr_type = CA_ECD; + return 0; +} + +static int fdtv_ca_get_slot_info(struct firedtv *fdtv, void *arg) +{ + struct firedtv_tuner_status stat; + struct ca_slot_info *slot = arg; + + if (avc_tuner_status(fdtv, &stat)) + return -EFAULT; + + if (slot->num != 0) + return -EFAULT; + + slot->type = CA_CI; + slot->flags = fdtv_get_ca_flags(&stat); + return 0; +} + +static int fdtv_ca_app_info(struct firedtv *fdtv, void *arg) +{ + struct ca_msg *reply = arg; + + return avc_ca_app_info(fdtv, reply->msg, &reply->length) ? -EFAULT : 0; +} + +static int fdtv_ca_info(struct firedtv *fdtv, void *arg) +{ + struct ca_msg *reply = arg; + + return avc_ca_info(fdtv, reply->msg, &reply->length) ? -EFAULT : 0; +} + +static int fdtv_ca_get_mmi(struct firedtv *fdtv, void *arg) +{ + struct ca_msg *reply = arg; + + return avc_ca_get_mmi(fdtv, reply->msg, &reply->length) ? -EFAULT : 0; +} + +static int fdtv_ca_get_msg(struct firedtv *fdtv, void *arg) +{ + struct firedtv_tuner_status stat; + int err; + + switch (fdtv->ca_last_command) { + case EN50221_TAG_APP_INFO_ENQUIRY: + err = fdtv_ca_app_info(fdtv, arg); + break; + case EN50221_TAG_CA_INFO_ENQUIRY: + err = fdtv_ca_info(fdtv, arg); + break; + default: + if (avc_tuner_status(fdtv, &stat)) + err = -EFAULT; + else if (stat.ca_mmi == 1) + err = fdtv_ca_get_mmi(fdtv, arg); + else { + dev_info(fdtv->device, "unhandled CA message 0x%08x\n", + fdtv->ca_last_command); + err = -EFAULT; + } + } + fdtv->ca_last_command = 0; + return err; +} + +static int fdtv_ca_pmt(struct firedtv *fdtv, void *arg) +{ + struct ca_msg *msg = arg; + int data_pos; + int data_length; + int i; + + data_pos = 4; + if (msg->msg[3] & 0x80) { + data_length = 0; + for (i = 0; i < (msg->msg[3] & 0x7f); i++) + data_length = (data_length << 8) + msg->msg[data_pos++]; + } else { + data_length = msg->msg[3]; + } + + return avc_ca_pmt(fdtv, &msg->msg[data_pos], data_length) ? -EFAULT : 0; +} + +static int fdtv_ca_send_msg(struct firedtv *fdtv, void *arg) +{ + struct ca_msg *msg = arg; + int err; + + /* Do we need a semaphore for this? */ + fdtv->ca_last_command = + (msg->msg[0] << 16) + (msg->msg[1] << 8) + msg->msg[2]; + switch (fdtv->ca_last_command) { + case EN50221_TAG_CA_PMT: + err = fdtv_ca_pmt(fdtv, arg); + break; + case EN50221_TAG_APP_INFO_ENQUIRY: + /* handled in ca_get_msg */ + err = 0; + break; + case EN50221_TAG_CA_INFO_ENQUIRY: + /* handled in ca_get_msg */ + err = 0; + break; + case EN50221_TAG_ENTER_MENU: + err = avc_ca_enter_menu(fdtv); + break; + default: + dev_err(fdtv->device, "unhandled CA message 0x%08x\n", + fdtv->ca_last_command); + err = -EFAULT; + } + return err; +} + +static int fdtv_ca_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, void *arg) +{ + struct dvb_device *dvbdev = file->private_data; + struct firedtv *fdtv = dvbdev->priv; + struct firedtv_tuner_status stat; + int err; + + switch (cmd) { + case CA_RESET: + err = fdtv_ca_reset(fdtv); + break; + case CA_GET_CAP: + err = fdtv_ca_get_caps(arg); + break; + case CA_GET_SLOT_INFO: + err = fdtv_ca_get_slot_info(fdtv, arg); + break; + case CA_GET_MSG: + err = fdtv_ca_get_msg(fdtv, arg); + break; + case CA_SEND_MSG: + err = fdtv_ca_send_msg(fdtv, arg); + break; + default: + dev_info(fdtv->device, "unhandled CA ioctl %u\n", cmd); + err = -EOPNOTSUPP; + } + + /* FIXME Is this necessary? */ + avc_tuner_status(fdtv, &stat); + + return err; +} + +static unsigned int fdtv_ca_io_poll(struct file *file, poll_table *wait) +{ + return POLLIN; +} + +static struct file_operations fdtv_ca_fops = { + .owner = THIS_MODULE, + .ioctl = dvb_generic_ioctl, + .open = dvb_generic_open, + .release = dvb_generic_release, + .poll = fdtv_ca_io_poll, +}; + +static struct dvb_device fdtv_ca = { + .users = 1, + .readers = 1, + .writers = 1, + .fops = &fdtv_ca_fops, + .kernel_ioctl = fdtv_ca_ioctl, +}; + +int fdtv_ca_register(struct firedtv *fdtv) +{ + struct firedtv_tuner_status stat; + int err; + + if (avc_tuner_status(fdtv, &stat)) + return -EINVAL; + + if (!fdtv_ca_ready(&stat)) + return -EFAULT; + + err = dvb_register_device(&fdtv->adapter, &fdtv->cadev, + &fdtv_ca, fdtv, DVB_DEVICE_CA); + + if (stat.ca_application_info == 0) + dev_err(fdtv->device, "CaApplicationInfo is not set\n"); + if (stat.ca_date_time_request == 1) + avc_ca_get_time_date(fdtv, &fdtv->ca_time_interval); + + return err; +} + +void fdtv_ca_release(struct firedtv *fdtv) +{ + if (fdtv->cadev) + dvb_unregister_device(fdtv->cadev); +} diff --git a/linux/drivers/media/dvb/firewire/firedtv-dvb.c b/linux/drivers/media/dvb/firewire/firedtv-dvb.c new file mode 100644 index 000000000..9d308dd32 --- /dev/null +++ b/linux/drivers/media/dvb/firewire/firedtv-dvb.c @@ -0,0 +1,364 @@ +/* + * FireDTV driver (formerly known as FireSAT) + * + * Copyright (C) 2004 Andreas Monitzer <andy@monitzer.com> + * Copyright (C) 2008 Henrik Kurelid <henrik@kurelid.se> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + */ + +#include <linux/bitops.h> +#include <linux/device.h> +#include <linux/errno.h> +#include <linux/kernel.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/slab.h> +#include <linux/string.h> +#include <linux/types.h> +#include <linux/wait.h> +#include <linux/workqueue.h> + +#include <dmxdev.h> +#include <dvb_demux.h> +#include <dvbdev.h> +#include <dvb_frontend.h> + +#include "firedtv.h" + +static int alloc_channel(struct firedtv *fdtv) +{ + int i; + + for (i = 0; i < 16; i++) + if (!__test_and_set_bit(i, &fdtv->channel_active)) + break; + return i; +} + +static void collect_channels(struct firedtv *fdtv, int *pidc, u16 pid[]) +{ + int i, n; + + for (i = 0, n = 0; i < 16; i++) + if (test_bit(i, &fdtv->channel_active)) + pid[n++] = fdtv->channel_pid[i]; + *pidc = n; +} + +static inline void dealloc_channel(struct firedtv *fdtv, int i) +{ + __clear_bit(i, &fdtv->channel_active); +} + +int fdtv_start_feed(struct dvb_demux_feed *dvbdmxfeed) +{ + struct firedtv *fdtv = dvbdmxfeed->demux->priv; + int pidc, c, ret; + u16 pids[16]; + + switch (dvbdmxfeed->type) { + case DMX_TYPE_TS: + case DMX_TYPE_SEC: + break; + default: + dev_err(fdtv->device, "can't start dmx feed: invalid type %u\n", + dvbdmxfeed->type); + return -EINVAL; + } + + if (mutex_lock_interruptible(&fdtv->demux_mutex)) + return -EINTR; + + if (dvbdmxfeed->type == DMX_TYPE_TS) { + switch (dvbdmxfeed->pes_type) { + case DMX_TS_PES_VIDEO: + case DMX_TS_PES_AUDIO: + case DMX_TS_PES_TELETEXT: + case DMX_TS_PES_PCR: + case DMX_TS_PES_OTHER: + c = alloc_channel(fdtv); + break; + default: + dev_err(fdtv->device, + "can't start dmx feed: invalid pes type %u\n", + dvbdmxfeed->pes_type); + ret = -EINVAL; + goto out; + } + } else { + c = alloc_channel(fdtv); + } + + if (c > 15) { + dev_err(fdtv->device, "can't start dmx feed: busy\n"); + ret = -EBUSY; + goto out; + } + + dvbdmxfeed->priv = (typeof(dvbdmxfeed->priv))(unsigned long)c; + fdtv->channel_pid[c] = dvbdmxfeed->pid; + collect_channels(fdtv, &pidc, pids); + + if (dvbdmxfeed->pid == 8192) { + ret = avc_tuner_get_ts(fdtv); + if (ret) { + dealloc_channel(fdtv, c); + dev_err(fdtv->device, "can't get TS\n"); + goto out; + } + } else { + ret = avc_tuner_set_pids(fdtv, pidc, pids); + if (ret) { + dealloc_channel(fdtv, c); + dev_err(fdtv->device, "can't set PIDs\n"); + goto out; + } + } +out: + mutex_unlock(&fdtv->demux_mutex); + + return ret; +} + +int fdtv_stop_feed(struct dvb_demux_feed *dvbdmxfeed) +{ + struct dvb_demux *demux = dvbdmxfeed->demux; + struct firedtv *fdtv = demux->priv; + int pidc, c, ret; + u16 pids[16]; + + if (dvbdmxfeed->type == DMX_TYPE_TS && + !((dvbdmxfeed->ts_type & TS_PACKET) && + (demux->dmx.frontend->source != DMX_MEMORY_FE))) { + + if (dvbdmxfeed->ts_type & TS_DECODER) { + if (dvbdmxfeed->pes_type >= DMX_TS_PES_OTHER || + !demux->pesfilter[dvbdmxfeed->pes_type]) + return -EINVAL; + + demux->pids[dvbdmxfeed->pes_type] |= 0x8000; + demux->pesfilter[dvbdmxfeed->pes_type] = NULL; + } + + if (!(dvbdmxfeed->ts_type & TS_DECODER && + dvbdmxfeed->pes_type < DMX_TS_PES_OTHER)) + return 0; + } + + if (mutex_lock_interruptible(&fdtv->demux_mutex)) + return -EINTR; + + c = (unsigned long)dvbdmxfeed->priv; + dealloc_channel(fdtv, c); + collect_channels(fdtv, &pidc, pids); + + ret = avc_tuner_set_pids(fdtv, pidc, pids); + + mutex_unlock(&fdtv->demux_mutex); + + return ret; +} + +DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); + +int fdtv_dvb_register(struct firedtv *fdtv) +{ + int err; + + err = dvb_register_adapter(&fdtv->adapter, fdtv_model_names[fdtv->type], + THIS_MODULE, fdtv->device, adapter_nr); + if (err < 0) + goto fail_log; + + /*DMX_TS_FILTERING | DMX_SECTION_FILTERING*/ + fdtv->demux.dmx.capabilities = 0; + + fdtv->demux.priv = fdtv; + fdtv->demux.filternum = 16; + fdtv->demux.feednum = 16; + fdtv->demux.start_feed = fdtv_start_feed; + fdtv->demux.stop_feed = fdtv_stop_feed; + fdtv->demux.write_to_decoder = NULL; + + err = dvb_dmx_init(&fdtv->demux); + if (err) + goto fail_unreg_adapter; + + fdtv->dmxdev.filternum = 16; + fdtv->dmxdev.demux = &fdtv->demux.dmx; + fdtv->dmxdev.capabilities = 0; + + err = dvb_dmxdev_init(&fdtv->dmxdev, &fdtv->adapter); + if (err) + goto fail_dmx_release; + + fdtv->frontend.source = DMX_FRONTEND_0; + + err = fdtv->demux.dmx.add_frontend(&fdtv->demux.dmx, &fdtv->frontend); + if (err) + goto fail_dmxdev_release; + + err = fdtv->demux.dmx.connect_frontend(&fdtv->demux.dmx, + &fdtv->frontend); + if (err) + goto fail_rem_frontend; + + dvb_net_init(&fdtv->adapter, &fdtv->dvbnet, &fdtv->demux.dmx); + + fdtv_frontend_init(fdtv); + err = dvb_register_frontend(&fdtv->adapter, &fdtv->fe); + if (err) + goto fail_net_release; + + err = fdtv_ca_register(fdtv); + if (err) + dev_info(fdtv->device, + "Conditional Access Module not enabled\n"); + return 0; + +fail_net_release: + dvb_net_release(&fdtv->dvbnet); + fdtv->demux.dmx.close(&fdtv->demux.dmx); +fail_rem_frontend: + fdtv->demux.dmx.remove_frontend(&fdtv->demux.dmx, &fdtv->frontend); +fail_dmxdev_release: + dvb_dmxdev_release(&fdtv->dmxdev); +fail_dmx_release: + dvb_dmx_release(&fdtv->demux); +fail_unreg_adapter: + dvb_unregister_adapter(&fdtv->adapter); +fail_log: + dev_err(fdtv->device, "DVB initialization failed\n"); + return err; +} + +void fdtv_dvb_unregister(struct firedtv *fdtv) +{ + fdtv_ca_release(fdtv); + dvb_unregister_frontend(&fdtv->fe); + dvb_net_release(&fdtv->dvbnet); + fdtv->demux.dmx.close(&fdtv->demux.dmx); + fdtv->demux.dmx.remove_frontend(&fdtv->demux.dmx, &fdtv->frontend); + dvb_dmxdev_release(&fdtv->dmxdev); + dvb_dmx_release(&fdtv->demux); + dvb_unregister_adapter(&fdtv->adapter); +} + +const char *fdtv_model_names[] = { + [FIREDTV_UNKNOWN] = "unknown type", + [FIREDTV_DVB_S] = "FireDTV S/CI", + [FIREDTV_DVB_C] = "FireDTV C/CI", + [FIREDTV_DVB_T] = "FireDTV T/CI", + [FIREDTV_DVB_S2] = "FireDTV S2 ", +}; + +struct firedtv *fdtv_alloc(struct device *dev, + const struct firedtv_backend *backend, + const char *name, size_t name_len) +{ + struct firedtv *fdtv; + int i; + + fdtv = kzalloc(sizeof(*fdtv), GFP_KERNEL); + if (!fdtv) + return NULL; + + dev->driver_data = fdtv; + fdtv->device = dev; + fdtv->isochannel = -1; + fdtv->voltage = 0xff; + fdtv->tone = 0xff; + fdtv->backend = backend; + + mutex_init(&fdtv->avc_mutex); + init_waitqueue_head(&fdtv->avc_wait); + fdtv->avc_reply_received = true; + mutex_init(&fdtv->demux_mutex); + INIT_WORK(&fdtv->remote_ctrl_work, avc_remote_ctrl_work); + + for (i = ARRAY_SIZE(fdtv_model_names); --i; ) + if (strlen(fdtv_model_names[i]) <= name_len && + strncmp(name, fdtv_model_names[i], name_len) == 0) + break; + fdtv->type = i; + + return fdtv; +} + +#define MATCH_FLAGS (IEEE1394_MATCH_VENDOR_ID | IEEE1394_MATCH_MODEL_ID | \ + IEEE1394_MATCH_SPECIFIER_ID | IEEE1394_MATCH_VERSION) + +#define DIGITAL_EVERYWHERE_OUI 0x001287 +#define AVC_UNIT_SPEC_ID_ENTRY 0x00a02d +#define AVC_SW_VERSION_ENTRY 0x010001 + +static struct ieee1394_device_id fdtv_id_table[] = { + { + /* FloppyDTV S/CI and FloppyDTV S2 */ + .match_flags = MATCH_FLAGS, + .vendor_id = DIGITAL_EVERYWHERE_OUI, + .model_id = 0x000024, + .specifier_id = AVC_UNIT_SPEC_ID_ENTRY, + .version = AVC_SW_VERSION_ENTRY, + }, { + /* FloppyDTV T/CI */ + .match_flags = MATCH_FLAGS, + .vendor_id = DIGITAL_EVERYWHERE_OUI, + .model_id = 0x000025, + .specifier_id = AVC_UNIT_SPEC_ID_ENTRY, + .version = AVC_SW_VERSION_ENTRY, + }, { + /* FloppyDTV C/CI */ + .match_flags = MATCH_FLAGS, + .vendor_id = DIGITAL_EVERYWHERE_OUI, + .model_id = 0x000026, + .specifier_id = AVC_UNIT_SPEC_ID_ENTRY, + .version = AVC_SW_VERSION_ENTRY, + }, { + /* FireDTV S/CI and FloppyDTV S2 */ + .match_flags = MATCH_FLAGS, + .vendor_id = DIGITAL_EVERYWHERE_OUI, + .model_id = 0x000034, + .specifier_id = AVC_UNIT_SPEC_ID_ENTRY, + .version = AVC_SW_VERSION_ENTRY, + }, { + /* FireDTV T/CI */ + .match_flags = MATCH_FLAGS, + .vendor_id = DIGITAL_EVERYWHERE_OUI, + .model_id = 0x000035, + .specifier_id = AVC_UNIT_SPEC_ID_ENTRY, + .version = AVC_SW_VERSION_ENTRY, + }, { + /* FireDTV C/CI */ + .match_flags = MATCH_FLAGS, + .vendor_id = DIGITAL_EVERYWHERE_OUI, + .model_id = 0x000036, + .specifier_id = AVC_UNIT_SPEC_ID_ENTRY, + .version = AVC_SW_VERSION_ENTRY, + }, {} +}; +MODULE_DEVICE_TABLE(ieee1394, fdtv_id_table); + +static int __init fdtv_init(void) +{ + return fdtv_1394_init(fdtv_id_table); +} + +static void __exit fdtv_exit(void) +{ + fdtv_1394_exit(); +} + +module_init(fdtv_init); +module_exit(fdtv_exit); + +MODULE_AUTHOR("Andreas Monitzer <andy@monitzer.com>"); +MODULE_AUTHOR("Ben Backx <ben@bbackx.com>"); +MODULE_DESCRIPTION("FireDTV DVB Driver"); +MODULE_LICENSE("GPL"); +MODULE_SUPPORTED_DEVICE("FireDTV DVB"); diff --git a/linux/drivers/media/dvb/firewire/firedtv-fe.c b/linux/drivers/media/dvb/firewire/firedtv-fe.c new file mode 100644 index 000000000..7ba43630a --- /dev/null +++ b/linux/drivers/media/dvb/firewire/firedtv-fe.c @@ -0,0 +1,247 @@ +/* + * FireDTV driver (formerly known as FireSAT) + * + * Copyright (C) 2004 Andreas Monitzer <andy@monitzer.com> + * Copyright (C) 2008 Henrik Kurelid <henrik@kurelid.se> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + */ + +#include <linux/device.h> +#include <linux/errno.h> +#include <linux/kernel.h> +#include <linux/string.h> +#include <linux/types.h> + +#include <dvb_frontend.h> + +#include "firedtv.h" + +static int fdtv_dvb_init(struct dvb_frontend *fe) +{ + struct firedtv *fdtv = fe->sec_priv; + int err; + + /* FIXME - allocate free channel at IRM */ + fdtv->isochannel = fdtv->adapter.num; + + err = cmp_establish_pp_connection(fdtv, fdtv->subunit, + fdtv->isochannel); + if (err) { + dev_err(fdtv->device, + "could not establish point to point connection\n"); + return err; + } + + return fdtv->backend->start_iso(fdtv); +} + +static int fdtv_sleep(struct dvb_frontend *fe) +{ + struct firedtv *fdtv = fe->sec_priv; + + fdtv->backend->stop_iso(fdtv); + cmp_break_pp_connection(fdtv, fdtv->subunit, fdtv->isochannel); + fdtv->isochannel = -1; + return 0; +} + +#define LNBCONTROL_DONTCARE 0xff + +static int fdtv_diseqc_send_master_cmd(struct dvb_frontend *fe, + struct dvb_diseqc_master_cmd *cmd) +{ + struct firedtv *fdtv = fe->sec_priv; + + return avc_lnb_control(fdtv, LNBCONTROL_DONTCARE, LNBCONTROL_DONTCARE, + LNBCONTROL_DONTCARE, 1, cmd); +} + +static int fdtv_diseqc_send_burst(struct dvb_frontend *fe, + fe_sec_mini_cmd_t minicmd) +{ + return 0; +} + +static int fdtv_set_tone(struct dvb_frontend *fe, fe_sec_tone_mode_t tone) +{ + struct firedtv *fdtv = fe->sec_priv; + + fdtv->tone = tone; + return 0; +} + +static int fdtv_set_voltage(struct dvb_frontend *fe, + fe_sec_voltage_t voltage) +{ + struct firedtv *fdtv = fe->sec_priv; + + fdtv->voltage = voltage; + return 0; +} + +static int fdtv_read_status(struct dvb_frontend *fe, fe_status_t *status) +{ + struct firedtv *fdtv = fe->sec_priv; + struct firedtv_tuner_status stat; + + if (avc_tuner_status(fdtv, &stat)) + return -EINVAL; + + if (stat.no_rf) + *status = 0; + else + *status = FE_HAS_SIGNAL | FE_HAS_VITERBI | FE_HAS_SYNC | + FE_HAS_CARRIER | FE_HAS_LOCK; + return 0; +} + +static int fdtv_read_ber(struct dvb_frontend *fe, u32 *ber) +{ + struct firedtv *fdtv = fe->sec_priv; + struct firedtv_tuner_status stat; + + if (avc_tuner_status(fdtv, &stat)) + return -EINVAL; + + *ber = stat.ber; + return 0; +} + +static int fdtv_read_signal_strength(struct dvb_frontend *fe, u16 *strength) +{ + struct firedtv *fdtv = fe->sec_priv; + struct firedtv_tuner_status stat; + + if (avc_tuner_status(fdtv, &stat)) + return -EINVAL; + + *strength = stat.signal_strength << 8; + return 0; +} + +static int fdtv_read_snr(struct dvb_frontend *fe, u16 *snr) +{ + struct firedtv *fdtv = fe->sec_priv; + struct firedtv_tuner_status stat; + + if (avc_tuner_status(fdtv, &stat)) + return -EINVAL; + + /* C/N[dB] = -10 * log10(snr / 65535) */ + *snr = stat.carrier_noise_ratio * 257; + return 0; +} + +static int fdtv_read_uncorrected_blocks(struct dvb_frontend *fe, u32 *ucblocks) +{ + return -EOPNOTSUPP; +} + +#define ACCEPTED 0x9 + +static int fdtv_set_frontend(struct dvb_frontend *fe, + struct dvb_frontend_parameters *params) +{ + struct firedtv *fdtv = fe->sec_priv; + + /* FIXME: avc_tuner_dsd never returns ACCEPTED. Check status? */ + if (avc_tuner_dsd(fdtv, params) != ACCEPTED) + return -EINVAL; + else + return 0; /* not sure of this... */ +} + +static int fdtv_get_frontend(struct dvb_frontend *fe, + struct dvb_frontend_parameters *params) +{ + return -EOPNOTSUPP; +} + +void fdtv_frontend_init(struct firedtv *fdtv) +{ + struct dvb_frontend_ops *ops = &fdtv->fe.ops; + struct dvb_frontend_info *fi = &ops->info; + + ops->init = fdtv_dvb_init; + ops->sleep = fdtv_sleep; + + ops->set_frontend = fdtv_set_frontend; + ops->get_frontend = fdtv_get_frontend; + + ops->read_status = fdtv_read_status; + ops->read_ber = fdtv_read_ber; + ops->read_signal_strength = fdtv_read_signal_strength; + ops->read_snr = fdtv_read_snr; + ops->read_ucblocks = fdtv_read_uncorrected_blocks; + + ops->diseqc_send_master_cmd = fdtv_diseqc_send_master_cmd; + ops->diseqc_send_burst = fdtv_diseqc_send_burst; + ops->set_tone = fdtv_set_tone; + ops->set_voltage = fdtv_set_voltage; + + switch (fdtv->type) { + case FIREDTV_DVB_S: + case FIREDTV_DVB_S2: + fi->type = FE_QPSK; + + fi->frequency_min = 950000; + fi->frequency_max = 2150000; + fi->frequency_stepsize = 125; + fi->symbol_rate_min = 1000000; + fi->symbol_rate_max = 40000000; + + fi->caps = FE_CAN_INVERSION_AUTO | + FE_CAN_FEC_1_2 | + FE_CAN_FEC_2_3 | + FE_CAN_FEC_3_4 | + FE_CAN_FEC_5_6 | + FE_CAN_FEC_7_8 | + FE_CAN_FEC_AUTO | + FE_CAN_QPSK; + break; + + case FIREDTV_DVB_C: + fi->type = FE_QAM; + + fi->frequency_min = 47000000; + fi->frequency_max = 866000000; + fi->frequency_stepsize = 62500; + fi->symbol_rate_min = 870000; + fi->symbol_rate_max = 6900000; + + fi->caps = FE_CAN_INVERSION_AUTO | + FE_CAN_QAM_16 | + FE_CAN_QAM_32 | + FE_CAN_QAM_64 | + FE_CAN_QAM_128 | + FE_CAN_QAM_256 | + FE_CAN_QAM_AUTO; + break; + + case FIREDTV_DVB_T: + fi->type = FE_OFDM; + + fi->frequency_min = 49000000; + fi->frequency_max = 861000000; + fi->frequency_stepsize = 62500; + + fi->caps = FE_CAN_INVERSION_AUTO | + FE_CAN_FEC_2_3 | + FE_CAN_TRANSMISSION_MODE_AUTO | + FE_CAN_GUARD_INTERVAL_AUTO | + FE_CAN_HIERARCHY_AUTO; + break; + + default: + dev_err(fdtv->device, "no frontend for model type %d\n", + fdtv->type); + } + strcpy(fi->name, fdtv_model_names[fdtv->type]); + + fdtv->fe.dvb = &fdtv->adapter; + fdtv->fe.sec_priv = fdtv; +} diff --git a/linux/drivers/media/dvb/firewire/firedtv-rc.c b/linux/drivers/media/dvb/firewire/firedtv-rc.c new file mode 100644 index 000000000..46a6324d7 --- /dev/null +++ b/linux/drivers/media/dvb/firewire/firedtv-rc.c @@ -0,0 +1,190 @@ +/* + * FireDTV driver (formerly known as FireSAT) + * + * Copyright (C) 2004 Andreas Monitzer <andy@monitzer.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + */ + +#include <linux/bitops.h> +#include <linux/input.h> +#include <linux/kernel.h> +#include <linux/string.h> +#include <linux/types.h> + +#include "firedtv.h" + +/* fixed table with older keycodes, geared towards MythTV */ +const static u16 oldtable[] = { + + /* code from device: 0x4501...0x451f */ + + KEY_ESC, + KEY_F9, + KEY_1, + KEY_2, + KEY_3, + KEY_4, + KEY_5, + KEY_6, + KEY_7, + KEY_8, + KEY_9, + KEY_I, + KEY_0, + KEY_ENTER, + KEY_RED, + KEY_UP, + KEY_GREEN, + KEY_F10, + KEY_SPACE, + KEY_F11, + KEY_YELLOW, + KEY_DOWN, + KEY_BLUE, + KEY_Z, + KEY_P, + KEY_PAGEDOWN, + KEY_LEFT, + KEY_W, + KEY_RIGHT, + KEY_P, + KEY_M, + + /* code from device: 0x4540...0x4542 */ + + KEY_R, + KEY_V, + KEY_C, +}; + +/* user-modifiable table for a remote as sold in 2008 */ +const static u16 keytable[] = { + + /* code from device: 0x0300...0x031f */ + + [0x00] = KEY_POWER, + [0x01] = KEY_SLEEP, + [0x02] = KEY_STOP, + [0x03] = KEY_OK, + [0x04] = KEY_RIGHT, + [0x05] = KEY_1, + [0x06] = KEY_2, + [0x07] = KEY_3, + [0x08] = KEY_LEFT, + [0x09] = KEY_4, + [0x0a] = KEY_5, + [0x0b] = KEY_6, + [0x0c] = KEY_UP, + [0x0d] = KEY_7, + [0x0e] = KEY_8, + [0x0f] = KEY_9, + [0x10] = KEY_DOWN, + [0x11] = KEY_TITLE, /* "OSD" - fixme */ + [0x12] = KEY_0, + [0x13] = KEY_F20, /* "16:9" - fixme */ + [0x14] = KEY_SCREEN, /* "FULL" - fixme */ + [0x15] = KEY_MUTE, + [0x16] = KEY_SUBTITLE, + [0x17] = KEY_RECORD, + [0x18] = KEY_TEXT, + [0x19] = KEY_AUDIO, + [0x1a] = KEY_RED, + [0x1b] = KEY_PREVIOUS, + [0x1c] = KEY_REWIND, + [0x1d] = KEY_PLAYPAUSE, + [0x1e] = KEY_NEXT, + [0x1f] = KEY_VOLUMEUP, + + /* code from device: 0x0340...0x0354 */ + + [0x20] = KEY_CHANNELUP, + [0x21] = KEY_F21, /* "4:3" - fixme */ + [0x22] = KEY_TV, + [0x23] = KEY_DVD, + [0x24] = KEY_VCR, + [0x25] = KEY_AUX, + [0x26] = KEY_GREEN, + [0x27] = KEY_YELLOW, + [0x28] = KEY_BLUE, + [0x29] = KEY_CHANNEL, /* "CH.LIST" */ + [0x2a] = KEY_VENDOR, /* "CI" - fixme */ + [0x2b] = KEY_VOLUMEDOWN, + [0x2c] = KEY_CHANNELDOWN, + [0x2d] = KEY_LAST, + [0x2e] = KEY_INFO, + [0x2f] = KEY_FORWARD, + [0x30] = KEY_LIST, + [0x31] = KEY_FAVORITES, + [0x32] = KEY_MENU, + [0x33] = KEY_EPG, + [0x34] = KEY_EXIT, +}; + +int fdtv_register_rc(struct firedtv *fdtv, struct device *dev) +{ + struct input_dev *idev; + int i, err; + + idev = input_allocate_device(); + if (!idev) + return -ENOMEM; + + fdtv->remote_ctrl_dev = idev; + idev->name = "FireDTV remote control"; + idev->dev.parent = dev; + idev->evbit[0] = BIT_MASK(EV_KEY); + idev->keycode = kmemdup(keytable, sizeof(keytable), GFP_KERNEL); + if (!idev->keycode) { + err = -ENOMEM; + goto fail; + } + idev->keycodesize = sizeof(keytable[0]); + idev->keycodemax = ARRAY_SIZE(keytable); + + for (i = 0; i < ARRAY_SIZE(keytable); i++) + set_bit(keytable[i], idev->keybit); + + err = input_register_device(idev); + if (err) + goto fail_free_keymap; + + return 0; + +fail_free_keymap: + kfree(idev->keycode); +fail: + input_free_device(idev); + return err; +} + +void fdtv_unregister_rc(struct firedtv *fdtv) +{ + kfree(fdtv->remote_ctrl_dev->keycode); + input_unregister_device(fdtv->remote_ctrl_dev); +} + +void fdtv_handle_rc(struct firedtv *fdtv, unsigned int code) +{ + u16 *keycode = fdtv->remote_ctrl_dev->keycode; + + if (code >= 0x0300 && code <= 0x031f) + code = keycode[code - 0x0300]; + else if (code >= 0x0340 && code <= 0x0354) + code = keycode[code - 0x0320]; + else if (code >= 0x4501 && code <= 0x451f) + code = oldtable[code - 0x4501]; + else if (code >= 0x4540 && code <= 0x4542) + code = oldtable[code - 0x4521]; + else { + printk(KERN_DEBUG "firedtv: invalid key code 0x%04x " + "from remote control\n", code); + return; + } + + input_report_key(fdtv->remote_ctrl_dev, code, 1); + input_report_key(fdtv->remote_ctrl_dev, code, 0); +} diff --git a/linux/drivers/media/dvb/firewire/firedtv.h b/linux/drivers/media/dvb/firewire/firedtv.h new file mode 100644 index 000000000..d48530b81 --- /dev/null +++ b/linux/drivers/media/dvb/firewire/firedtv.h @@ -0,0 +1,182 @@ +/* + * FireDTV driver (formerly known as FireSAT) + * + * Copyright (C) 2004 Andreas Monitzer <andy@monitzer.com> + * Copyright (C) 2008 Henrik Kurelid <henrik@kurelid.se> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + */ + +#ifndef _FIREDTV_H +#define _FIREDTV_H + +#include <linux/dvb/dmx.h> +#include <linux/dvb/frontend.h> +#include <linux/list.h> +#include <linux/mutex.h> +#include <linux/spinlock_types.h> +#include <linux/types.h> +#include <linux/wait.h> +#include <linux/workqueue.h> + +#include <demux.h> +#include <dmxdev.h> +#include <dvb_demux.h> +#include <dvb_frontend.h> +#include <dvb_net.h> +#include <dvbdev.h> + +struct firedtv_tuner_status { + unsigned active_system:8; + unsigned searching:1; + unsigned moving:1; + unsigned no_rf:1; + unsigned input:1; + unsigned selected_antenna:7; + unsigned ber:32; + unsigned signal_strength:8; + unsigned raster_frequency:2; + unsigned rf_frequency:22; + unsigned man_dep_info_length:8; + unsigned front_end_error:1; + unsigned antenna_error:1; + unsigned front_end_power_status:1; + unsigned power_supply:1; + unsigned carrier_noise_ratio:16; + unsigned power_supply_voltage:8; + unsigned antenna_voltage:8; + unsigned firewire_bus_voltage:8; + unsigned ca_mmi:1; + unsigned ca_pmt_reply:1; + unsigned ca_date_time_request:1; + unsigned ca_application_info:1; + unsigned ca_module_present_status:1; + unsigned ca_dvb_flag:1; + unsigned ca_error_flag:1; + unsigned ca_initialization_status:1; +}; + +enum model_type { + FIREDTV_UNKNOWN = 0, + FIREDTV_DVB_S = 1, + FIREDTV_DVB_C = 2, + FIREDTV_DVB_T = 3, + FIREDTV_DVB_S2 = 4, +}; + +struct device; +struct input_dev; +struct firedtv; + +struct firedtv_backend { + int (*lock)(struct firedtv *fdtv, u64 addr, void *data, __be32 arg); + int (*read)(struct firedtv *fdtv, u64 addr, void *data, size_t len); + int (*write)(struct firedtv *fdtv, u64 addr, void *data, size_t len); + int (*start_iso)(struct firedtv *fdtv); + void (*stop_iso)(struct firedtv *fdtv); +}; + +struct firedtv { + struct device *device; + struct list_head list; + + struct dvb_adapter adapter; + struct dmxdev dmxdev; + struct dvb_demux demux; + struct dmx_frontend frontend; + struct dvb_net dvbnet; + struct dvb_frontend fe; + + struct dvb_device *cadev; + int ca_last_command; + int ca_time_interval; + + struct mutex avc_mutex; + wait_queue_head_t avc_wait; + bool avc_reply_received; + struct work_struct remote_ctrl_work; + struct input_dev *remote_ctrl_dev; + + enum model_type type; + char subunit; + char isochannel; + fe_sec_voltage_t voltage; + fe_sec_tone_mode_t tone; + + const struct firedtv_backend *backend; + void *backend_data; + + struct mutex demux_mutex; + unsigned long channel_active; + u16 channel_pid[16]; + + size_t response_length; + u8 response[512]; +}; + +/* firedtv-1394.c */ +#ifdef CONFIG_DVB_FIREDTV_IEEE1394 +int fdtv_1394_init(struct ieee1394_device_id id_table[]); +void fdtv_1394_exit(void); +#else +static inline int fdtv_1394_init(struct ieee1394_device_id it[]) { return 0; } +static inline void fdtv_1394_exit(void) {} +#endif + +/* firedtv-avc.c */ +int avc_recv(struct firedtv *fdtv, void *data, size_t length); +int avc_tuner_status(struct firedtv *fdtv, struct firedtv_tuner_status *stat); +struct dvb_frontend_parameters; +int avc_tuner_dsd(struct firedtv *fdtv, struct dvb_frontend_parameters *params); +int avc_tuner_set_pids(struct firedtv *fdtv, unsigned char pidc, u16 pid[]); +int avc_tuner_get_ts(struct firedtv *fdtv); +int avc_identify_subunit(struct firedtv *fdtv); +struct dvb_diseqc_master_cmd; +int avc_lnb_control(struct firedtv *fdtv, char voltage, char burst, + char conttone, char nrdiseq, + struct dvb_diseqc_master_cmd *diseqcmd); +void avc_remote_ctrl_work(struct work_struct *work); +int avc_register_remote_control(struct firedtv *fdtv); +int avc_ca_app_info(struct firedtv *fdtv, char *app_info, unsigned int *len); +int avc_ca_info(struct firedtv *fdtv, char *app_info, unsigned int *len); +int avc_ca_reset(struct firedtv *fdtv); +int avc_ca_pmt(struct firedtv *fdtv, char *app_info, int length); +int avc_ca_get_time_date(struct firedtv *fdtv, int *interval); +int avc_ca_enter_menu(struct firedtv *fdtv); +int avc_ca_get_mmi(struct firedtv *fdtv, char *mmi_object, unsigned int *len); +int cmp_establish_pp_connection(struct firedtv *fdtv, int plug, int channel); +void cmp_break_pp_connection(struct firedtv *fdtv, int plug, int channel); + +/* firedtv-ci.c */ +int fdtv_ca_register(struct firedtv *fdtv); +void fdtv_ca_release(struct firedtv *fdtv); + +/* firedtv-dvb.c */ +int fdtv_start_feed(struct dvb_demux_feed *dvbdmxfeed); +int fdtv_stop_feed(struct dvb_demux_feed *dvbdmxfeed); +int fdtv_dvb_register(struct firedtv *fdtv); +void fdtv_dvb_unregister(struct firedtv *fdtv); +struct firedtv *fdtv_alloc(struct device *dev, + const struct firedtv_backend *backend, + const char *name, size_t name_len); +extern const char *fdtv_model_names[]; + +/* firedtv-fe.c */ +void fdtv_frontend_init(struct firedtv *fdtv); + +/* firedtv-rc.c */ +#ifdef CONFIG_DVB_FIREDTV_INPUT +int fdtv_register_rc(struct firedtv *fdtv, struct device *dev); +void fdtv_unregister_rc(struct firedtv *fdtv); +void fdtv_handle_rc(struct firedtv *fdtv, unsigned int code); +#else +static inline int fdtv_register_rc(struct firedtv *fdtv, + struct device *dev) { return 0; } +static inline void fdtv_unregister_rc(struct firedtv *fdtv) {} +static inline void fdtv_handle_rc(struct firedtv *fdtv, unsigned int code) {} +#endif + +#endif /* _FIREDTV_H */ diff --git a/linux/drivers/media/dvb/siano/sms-cards.c b/linux/drivers/media/dvb/siano/sms-cards.c index 66f591937..63e4d0ec6 100644 --- a/linux/drivers/media/dvb/siano/sms-cards.c +++ b/linux/drivers/media/dvb/siano/sms-cards.c @@ -19,6 +19,10 @@ #include "sms-cards.h" +static int sms_dbg; +module_param_named(cards_dbg, sms_dbg, int, 0644); +MODULE_PARM_DESC(cards_dbg, "set debug level (info=1, adv=2 (or-able))"); + static struct sms_board sms_boards[] = { [SMS_BOARD_UNKNOWN] = { .name = "Unknown board", diff --git a/linux/drivers/media/dvb/siano/smscoreapi.c b/linux/drivers/media/dvb/siano/smscoreapi.c index 812b57ab7..8c37a060b 100644 --- a/linux/drivers/media/dvb/siano/smscoreapi.c +++ b/linux/drivers/media/dvb/siano/smscoreapi.c @@ -34,7 +34,7 @@ #include "smscoreapi.h" #include "sms-cards.h" -int sms_dbg; +static int sms_dbg; module_param_named(debug, sms_dbg, int, 0644); MODULE_PARM_DESC(debug, "set debug level (info=1, adv=2 (or-able))"); diff --git a/linux/drivers/media/dvb/siano/smscoreapi.h b/linux/drivers/media/dvb/siano/smscoreapi.h index 14dcbdd80..85da09f86 100644 --- a/linux/drivers/media/dvb/siano/smscoreapi.h +++ b/linux/drivers/media/dvb/siano/smscoreapi.h @@ -578,8 +578,6 @@ int smscore_led_state(struct smscore_device_t *core, int led); /* ------------------------------------------------------------------------ */ -extern int sms_dbg; - #define DBG_INFO 1 #define DBG_ADV 2 diff --git a/linux/drivers/media/dvb/siano/smsdvb.c b/linux/drivers/media/dvb/siano/smsdvb.c index c91f6f242..035bd52c9 100644 --- a/linux/drivers/media/dvb/siano/smsdvb.c +++ b/linux/drivers/media/dvb/siano/smsdvb.c @@ -50,6 +50,10 @@ struct smsdvb_client_t { static struct list_head g_smsdvb_clients; static struct mutex g_smsdvb_clientslock; +static int sms_dbg; +module_param_named(debug, sms_dbg, int, 0644); +MODULE_PARM_DESC(debug, "set debug level (info=1, adv=2 (or-able))"); + static int smsdvb_onresponse(void *context, struct smscore_buffer_t *cb) { struct smsdvb_client_t *client = (struct smsdvb_client_t *) context; diff --git a/linux/drivers/media/dvb/siano/smsusb.c b/linux/drivers/media/dvb/siano/smsusb.c index b25ab60a4..67be79549 100644 --- a/linux/drivers/media/dvb/siano/smsusb.c +++ b/linux/drivers/media/dvb/siano/smsusb.c @@ -27,6 +27,10 @@ #include "smscoreapi.h" #include "sms-cards.h" +static int sms_dbg; +module_param_named(debug, sms_dbg, int, 0644); +MODULE_PARM_DESC(debug, "set debug level (info=1, adv=2 (or-able))"); + #define USB1_BUFFER_SIZE 0x1000 #define USB2_BUFFER_SIZE 0x4000 diff --git a/linux/drivers/media/video/Kconfig b/linux/drivers/media/video/Kconfig index bb7df8c18..6c26618b8 100644 --- a/linux/drivers/media/video/Kconfig +++ b/linux/drivers/media/video/Kconfig @@ -253,7 +253,7 @@ comment "Video decoders" config VIDEO_BT819 tristate "BT819A VideoStream decoder" - depends on VIDEO_V4L1 && I2C + depends on VIDEO_V4L2 && I2C ---help--- Support for BT819A video decoder. @@ -262,7 +262,7 @@ config VIDEO_BT819 config VIDEO_BT856 tristate "BT856 VideoStream decoder" - depends on VIDEO_V4L1 && I2C + depends on VIDEO_V4L2 && I2C ---help--- Support for BT856 video decoder. @@ -271,7 +271,7 @@ config VIDEO_BT856 config VIDEO_BT866 tristate "BT866 VideoStream decoder" - depends on VIDEO_V4L1 && I2C + depends on VIDEO_V4L2 && I2C ---help--- Support for BT866 video decoder. @@ -280,7 +280,7 @@ config VIDEO_BT866 config VIDEO_KS0127 tristate "KS0127 video decoder" - depends on VIDEO_V4L1 && I2C + depends on VIDEO_V4L2 && I2C ---help--- Support for KS0127 video decoder. @@ -363,7 +363,7 @@ config VIDEO_TVP5150 config VIDEO_VPX3220 tristate "vpx3220a, vpx3216b & vpx3214c video decoders" - depends on VIDEO_V4L1 && I2C + depends on VIDEO_V4L2 && I2C ---help--- Support for VPX322x video decoders. @@ -401,7 +401,7 @@ config VIDEO_SAA7127 config VIDEO_SAA7185 tristate "Philips SAA7185 video encoder" - depends on VIDEO_V4L1 && I2C + depends on VIDEO_V4L2 && I2C ---help--- Support for the Philips SAA7185 video encoder. @@ -410,7 +410,7 @@ config VIDEO_SAA7185 config VIDEO_ADV7170 tristate "Analog Devices ADV7170 video encoder" - depends on VIDEO_V4L1 && I2C + depends on VIDEO_V4L2 && I2C ---help--- Support for the Analog Devices ADV7170 video encoder driver @@ -419,7 +419,7 @@ config VIDEO_ADV7170 config VIDEO_ADV7175 tristate "Analog Devices ADV7175 video encoder" - depends on VIDEO_V4L1 && I2C + depends on VIDEO_V4L2 && I2C ---help--- Support for the Analog Devices ADV7175 video encoder driver diff --git a/linux/drivers/media/video/pxa_camera.h b/linux/drivers/media/video/pxa_camera.h deleted file mode 100644 index 89cbfc9a3..000000000 --- a/linux/drivers/media/video/pxa_camera.h +++ /dev/null @@ -1,95 +0,0 @@ -/* Camera Interface */ -#define CICR0 __REG(0x50000000) -#define CICR1 __REG(0x50000004) -#define CICR2 __REG(0x50000008) -#define CICR3 __REG(0x5000000C) -#define CICR4 __REG(0x50000010) -#define CISR __REG(0x50000014) -#define CIFR __REG(0x50000018) -#define CITOR __REG(0x5000001C) -#define CIBR0 __REG(0x50000028) -#define CIBR1 __REG(0x50000030) -#define CIBR2 __REG(0x50000038) - -#define CICR0_DMAEN (1 << 31) /* DMA request enable */ -#define CICR0_PAR_EN (1 << 30) /* Parity enable */ -#define CICR0_SL_CAP_EN (1 << 29) /* Capture enable for slave mode */ -#define CICR0_ENB (1 << 28) /* Camera interface enable */ -#define CICR0_DIS (1 << 27) /* Camera interface disable */ -#define CICR0_SIM (0x7 << 24) /* Sensor interface mode mask */ -#define CICR0_TOM (1 << 9) /* Time-out mask */ -#define CICR0_RDAVM (1 << 8) /* Receive-data-available mask */ -#define CICR0_FEM (1 << 7) /* FIFO-empty mask */ -#define CICR0_EOLM (1 << 6) /* End-of-line mask */ -#define CICR0_PERRM (1 << 5) /* Parity-error mask */ -#define CICR0_QDM (1 << 4) /* Quick-disable mask */ -#define CICR0_CDM (1 << 3) /* Disable-done mask */ -#define CICR0_SOFM (1 << 2) /* Start-of-frame mask */ -#define CICR0_EOFM (1 << 1) /* End-of-frame mask */ -#define CICR0_FOM (1 << 0) /* FIFO-overrun mask */ - -#define CICR1_TBIT (1 << 31) /* Transparency bit */ -#define CICR1_RGBT_CONV (0x3 << 29) /* RGBT conversion mask */ -#define CICR1_PPL (0x7ff << 15) /* Pixels per line mask */ -#define CICR1_RGB_CONV (0x7 << 12) /* RGB conversion mask */ -#define CICR1_RGB_F (1 << 11) /* RGB format */ -#define CICR1_YCBCR_F (1 << 10) /* YCbCr format */ -#define CICR1_RGB_BPP (0x7 << 7) /* RGB bis per pixel mask */ -#define CICR1_RAW_BPP (0x3 << 5) /* Raw bis per pixel mask */ -#define CICR1_COLOR_SP (0x3 << 3) /* Color space mask */ -#define CICR1_DW (0x7 << 0) /* Data width mask */ - -#define CICR2_BLW (0xff << 24) /* Beginning-of-line pixel clock - wait count mask */ -#define CICR2_ELW (0xff << 16) /* End-of-line pixel clock - wait count mask */ -#define CICR2_HSW (0x3f << 10) /* Horizontal sync pulse width mask */ -#define CICR2_BFPW (0x3f << 3) /* Beginning-of-frame pixel clock - wait count mask */ -#define CICR2_FSW (0x7 << 0) /* Frame stabilization - wait count mask */ - -#define CICR3_BFW (0xff << 24) /* Beginning-of-frame line clock - wait count mask */ -#define CICR3_EFW (0xff << 16) /* End-of-frame line clock - wait count mask */ -#define CICR3_VSW (0x3f << 10) /* Vertical sync pulse width mask */ -#define CICR3_BFPW (0x3f << 3) /* Beginning-of-frame pixel clock - wait count mask */ -#define CICR3_LPF (0x7ff << 0) /* Lines per frame mask */ - -#define CICR4_MCLK_DLY (0x3 << 24) /* MCLK Data Capture Delay mask */ -#define CICR4_PCLK_EN (1 << 23) /* Pixel clock enable */ -#define CICR4_PCP (1 << 22) /* Pixel clock polarity */ -#define CICR4_HSP (1 << 21) /* Horizontal sync polarity */ -#define CICR4_VSP (1 << 20) /* Vertical sync polarity */ -#define CICR4_MCLK_EN (1 << 19) /* MCLK enable */ -#define CICR4_FR_RATE (0x7 << 8) /* Frame rate mask */ -#define CICR4_DIV (0xff << 0) /* Clock divisor mask */ - -#define CISR_FTO (1 << 15) /* FIFO time-out */ -#define CISR_RDAV_2 (1 << 14) /* Channel 2 receive data available */ -#define CISR_RDAV_1 (1 << 13) /* Channel 1 receive data available */ -#define CISR_RDAV_0 (1 << 12) /* Channel 0 receive data available */ -#define CISR_FEMPTY_2 (1 << 11) /* Channel 2 FIFO empty */ -#define CISR_FEMPTY_1 (1 << 10) /* Channel 1 FIFO empty */ -#define CISR_FEMPTY_0 (1 << 9) /* Channel 0 FIFO empty */ -#define CISR_EOL (1 << 8) /* End of line */ -#define CISR_PAR_ERR (1 << 7) /* Parity error */ -#define CISR_CQD (1 << 6) /* Camera interface quick disable */ -#define CISR_CDD (1 << 5) /* Camera interface disable done */ -#define CISR_SOF (1 << 4) /* Start of frame */ -#define CISR_EOF (1 << 3) /* End of frame */ -#define CISR_IFO_2 (1 << 2) /* FIFO overrun for Channel 2 */ -#define CISR_IFO_1 (1 << 1) /* FIFO overrun for Channel 1 */ -#define CISR_IFO_0 (1 << 0) /* FIFO overrun for Channel 0 */ - -#define CIFR_FLVL2 (0x7f << 23) /* FIFO 2 level mask */ -#define CIFR_FLVL1 (0x7f << 16) /* FIFO 1 level mask */ -#define CIFR_FLVL0 (0xff << 8) /* FIFO 0 level mask */ -#define CIFR_THL_0 (0x3 << 4) /* Threshold Level for Channel 0 FIFO */ -#define CIFR_RESET_F (1 << 3) /* Reset input FIFOs */ -#define CIFR_FEN2 (1 << 2) /* FIFO enable for channel 2 */ -#define CIFR_FEN1 (1 << 1) /* FIFO enable for channel 1 */ -#define CIFR_FEN0 (1 << 0) /* FIFO enable for channel 0 */ - diff --git a/linux/drivers/media/video/tvmixer.c b/linux/drivers/media/video/tvmixer.c deleted file mode 100644 index bc6702a0a..000000000 --- a/linux/drivers/media/video/tvmixer.c +++ /dev/null @@ -1,345 +0,0 @@ -/* - */ - -#include <linux/module.h> -#include <linux/kernel.h> -#include <linux/string.h> -#include <linux/timer.h> -#include <linux/delay.h> -#include <linux/errno.h> -#include <linux/slab.h> -#include <linux/i2c.h> -#include <linux/smp_lock.h> -#include "compat.h" -#include <linux/videodev2.h> -#include <linux/init.h> -#include <linux/kdev_t.h> -#include <linux/sound.h> -#include <linux/soundcard.h> - -#include <asm/uaccess.h> - -#define DEV_MAX 4 - -static int devnr = -1; -module_param(devnr, int, 0644); - -MODULE_AUTHOR("Gerd Knorr"); -MODULE_LICENSE("GPL"); - -/* ----------------------------------------------------------------------- */ - -struct TVMIXER { - struct i2c_client *dev; - int minor; - int count; -}; - -static struct TVMIXER devices[DEV_MAX]; - -static int tvmixer_adapters(struct i2c_adapter *adap); -static int tvmixer_clients(struct i2c_client *client); - -/* ----------------------------------------------------------------------- */ - -static int mix_to_v4l(int i) -{ - int r; - - r = ((i & 0xff) * 65536 + 50) / 100; - if (r > 65535) r = 65535; - if (r < 0) r = 0; - return r; -} - -static int v4l_to_mix(int i) -{ - int r; - - r = (i * 100 + 32768) / 65536; - if (r > 100) r = 100; - if (r < 0) r = 0; - return r | (r << 8); -} - -static int v4l_to_mix2(int l, int r) -{ - r = (r * 100 + 32768) / 65536; - if (r > 100) r = 100; - if (r < 0) r = 0; - l = (l * 100 + 32768) / 65536; - if (l > 100) l = 100; - if (l < 0) l = 0; - return (r << 8) | l; -} - -static int tvmixer_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) -{ - struct video_audio va; - int left,right,ret,val = 0; - struct TVMIXER *mix = file->private_data; - struct i2c_client *client = mix->dev; - void __user *argp = (void __user *)arg; - int __user *p = argp; - - if (NULL == client) - return -ENODEV; - - if (cmd == SOUND_MIXER_INFO) { - mixer_info info; - strlcpy(info.id, "tv card", sizeof(info.id)); - strlcpy(info.name, client->name, sizeof(info.name)); - info.modify_counter = 42 /* FIXME */; - if (copy_to_user(argp, &info, sizeof(info))) - return -EFAULT; - return 0; - } - if (cmd == SOUND_OLD_MIXER_INFO) { - _old_mixer_info info; - strlcpy(info.id, "tv card", sizeof(info.id)); - strlcpy(info.name, client->name, sizeof(info.name)); - if (copy_to_user(argp, &info, sizeof(info))) - return -EFAULT; - return 0; - } - if (cmd == OSS_GETVERSION) - return put_user(SOUND_VERSION, p); - - if (_SIOC_DIR(cmd) & _SIOC_WRITE) - if (get_user(val, p)) - return -EFAULT; - - /* read state */ - memset(&va,0,sizeof(va)); - client->driver->command(client,VIDIOCGAUDIO,&va); - - switch (cmd) { - case MIXER_READ(SOUND_MIXER_RECMASK): - case MIXER_READ(SOUND_MIXER_CAPS): - case MIXER_READ(SOUND_MIXER_RECSRC): - case MIXER_WRITE(SOUND_MIXER_RECSRC): - ret = 0; - break; - - case MIXER_READ(SOUND_MIXER_STEREODEVS): - ret = SOUND_MASK_VOLUME; - break; - case MIXER_READ(SOUND_MIXER_DEVMASK): - ret = SOUND_MASK_VOLUME; - if (va.flags & VIDEO_AUDIO_BASS) - ret |= SOUND_MASK_BASS; - if (va.flags & VIDEO_AUDIO_TREBLE) - ret |= SOUND_MASK_TREBLE; - break; - - case MIXER_WRITE(SOUND_MIXER_VOLUME): - left = mix_to_v4l(val); - right = mix_to_v4l(val >> 8); - va.volume = max(left,right); - va.balance = (32768*min(left,right)) / (va.volume ? va.volume : 1); - va.balance = (left<right) ? (65535-va.balance) : va.balance; - if (va.volume) - va.flags &= ~VIDEO_AUDIO_MUTE; - client->driver->command(client,VIDIOCSAUDIO,&va); - client->driver->command(client,VIDIOCGAUDIO,&va); - /* fall throuth */ - case MIXER_READ(SOUND_MIXER_VOLUME): - left = (min(65536 - va.balance,32768) * - va.volume) / 32768; - right = (min(va.balance,(u16)32768) * - va.volume) / 32768; - ret = v4l_to_mix2(left,right); - break; - - case MIXER_WRITE(SOUND_MIXER_BASS): - va.bass = mix_to_v4l(val); - client->driver->command(client,VIDIOCSAUDIO,&va); - client->driver->command(client,VIDIOCGAUDIO,&va); - /* fall throuth */ - case MIXER_READ(SOUND_MIXER_BASS): - ret = v4l_to_mix(va.bass); - break; - - case MIXER_WRITE(SOUND_MIXER_TREBLE): - va.treble = mix_to_v4l(val); - client->driver->command(client,VIDIOCSAUDIO,&va); - client->driver->command(client,VIDIOCGAUDIO,&va); - /* fall throuth */ - case MIXER_READ(SOUND_MIXER_TREBLE): - ret = v4l_to_mix(va.treble); - break; - - default: - return -EINVAL; - } - if (put_user(ret, p)) - return -EFAULT; - return 0; -} - -static int tvmixer_open(struct inode *inode, struct file *file) -{ - int i, minor = iminor(inode); - struct TVMIXER *mix = NULL; - struct i2c_client *client = NULL; - - lock_kernel(); - for (i = 0; i < DEV_MAX; i++) { - if (devices[i].minor == minor) { - mix = devices+i; - client = mix->dev; - break; - } - } - - if (NULL == client) { - unlock_kernel(); - return -ENODEV; - } - - /* lock bttv in memory while the mixer is in use */ - file->private_data = mix; - if (client->adapter->owner) - try_module_get(client->adapter->owner); - unlock_kernel(); - return 0; -} - -static int tvmixer_release(struct inode *inode, struct file *file) -{ - struct TVMIXER *mix = file->private_data; - struct i2c_client *client; - - client = mix->dev; - if (NULL == client) { - return -ENODEV; - } - - module_put(client->adapter->owner); - return 0; -} - -static struct i2c_driver driver = { - .driver = { - .name = "tvmixer", - }, -#ifndef I2C_DF_DUMMY - .detach_adapter = tvmixer_adapters, -#endif - .attach_adapter = tvmixer_adapters, - .detach_client = tvmixer_clients, -}; - -static const struct file_operations tvmixer_fops = { - .owner = THIS_MODULE, - .llseek = no_llseek, - .ioctl = tvmixer_ioctl, - .open = tvmixer_open, - .release = tvmixer_release, -}; - -/* ----------------------------------------------------------------------- */ - -static int tvmixer_adapters(struct i2c_adapter *adap) -{ - struct i2c_client *client; - - list_for_each_entry(client, &adap->clients, list) - tvmixer_clients(client); - return 0; -} - -static int tvmixer_clients(struct i2c_client *client) -{ - struct video_audio va; - int i,minor; - - if (!(client->adapter->class & I2C_CLASS_TV_ANALOG)) - return -1; - - /* unregister ?? */ - for (i = 0; i < DEV_MAX; i++) { - if (devices[i].dev == client) { - /* unregister */ - unregister_sound_mixer(devices[i].minor); - devices[i].dev = NULL; - devices[i].minor = -1; - printk("tvmixer: %s unregistered (#1)\n", - client->name); - return 0; - } - } - - /* look for a free slot */ - for (i = 0; i < DEV_MAX; i++) - if (NULL == devices[i].dev) - break; - if (i == DEV_MAX) { - printk(KERN_WARNING "tvmixer: DEV_MAX too small\n"); - return -1; - } - - /* audio chip with mixer ??? */ - if (NULL == client->driver->command) - return -1; - memset(&va,0,sizeof(va)); - if (0 != client->driver->command(client,VIDIOCGAUDIO,&va)) - return -1; - if (0 == (va.flags & VIDEO_AUDIO_VOLUME)) - return -1; - - /* everything is fine, register */ -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 17) - if ((minor = register_sound_mixer(&tvmixer_fops, devnr)) < 0) { -#else - if ((minor = register_sound_mixer((struct file_operations *)&tvmixer_fops, devnr)) < 0) { -#endif - printk(KERN_ERR "tvmixer: cannot allocate mixer device\n"); - return -1; - } - - devices[i].minor = minor; - devices[i].count = 0; - devices[i].dev = client; - printk("tvmixer: %s (%s) registered with minor %d\n", - client->name,client->adapter->name,minor); - - return 0; -} - -/* ----------------------------------------------------------------------- */ - -static int __init tvmixer_init_module(void) -{ - int i; - - for (i = 0; i < DEV_MAX; i++) - devices[i].minor = -1; - - return i2c_add_driver(&driver); -} - -static void __exit tvmixer_cleanup_module(void) -{ - int i; - - i2c_del_driver(&driver); - for (i = 0; i < DEV_MAX; i++) { - if (devices[i].minor != -1) { - unregister_sound_mixer(devices[i].minor); - printk("tvmixer: %s unregistered (#2)\n", - devices[i].dev->name); - } - } -} - -module_init(tvmixer_init_module); -module_exit(tvmixer_cleanup_module); - -/* - * Overrides for Emacs so that we follow Linus's tabbing style. - * --------------------------------------------------------------------------- - * Local variables: - * c-basic-offset: 8 - * End: - */ diff --git a/linux/include/sound/tea575x-tuner.h b/linux/include/sound/tea575x-tuner.h index b09cf5882..4c6443a7a 100644 --- a/linux/include/sound/tea575x-tuner.h +++ b/linux/include/sound/tea575x-tuner.h @@ -23,24 +23,16 @@ */ #include "compat.h" -#include <linux/videodev.h> +#include <linux/videodev2.h> #include <media/v4l2-dev.h> +#include <media/v4l2-ioctl.h> -#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,17) -typedef struct snd_tea575x tea575x_t; -#else struct snd_tea575x; -#endif struct snd_tea575x_ops { -#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,17) - void (*write)(tea575x_t *tea, unsigned int val); - unsigned int (*read)(tea575x_t *tea); -#else void (*write)(struct snd_tea575x *tea, unsigned int val); unsigned int (*read)(struct snd_tea575x *tea); void (*mute)(struct snd_tea575x *tea, unsigned int mute); -#endif }; struct snd_tea575x { @@ -49,11 +41,10 @@ struct snd_tea575x { #else struct snd_card *card; #endif - struct video_device vd; /* video device */ - struct v4l2_file_operations fops; + struct video_device *vd; /* video device */ int dev_nr; /* requested device number + 1 */ - int vd_registered; /* video device is registered */ int tea5759; /* 5759 chip is present */ + int mute; /* Device is muted? */ unsigned int freq_fixup; /* crystal onboard */ unsigned int val; /* hw value */ unsigned long freq; /* frequency */ @@ -62,12 +53,7 @@ struct snd_tea575x { void *private_data; }; -#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,17) -void snd_tea575x_init(tea575x_t *tea); -void snd_tea575x_exit(tea575x_t *tea); -#else void snd_tea575x_init(struct snd_tea575x *tea); void snd_tea575x_exit(struct snd_tea575x *tea); -#endif #endif /* __SOUND_TEA575X_TUNER_H */ diff --git a/linux/sound/i2c/other/tea575x-tuner.c b/linux/sound/i2c/other/tea575x-tuner.c index db47945e9..8937198db 100644 --- a/linux/sound/i2c/other/tea575x-tuner.c +++ b/linux/sound/i2c/other/tea575x-tuner.c @@ -24,6 +24,7 @@ #include <linux/delay.h> #include <linux/interrupt.h> #include <linux/init.h> +#include <linux/version.h> #include "compat.h" #include <sound/core.h> #include <sound/tea575x-tuner.h> @@ -32,6 +33,13 @@ MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>"); MODULE_DESCRIPTION("Routines for control of TEA5757/5759 Philips AM/FM radio tuner chips"); MODULE_LICENSE("GPL"); +static int radio_nr = -1; +module_param(radio_nr, int, 0); + +#define RADIO_VERSION KERNEL_VERSION(0, 0, 2) +#define FREQ_LO (87 * 16000) +#define FREQ_HI (108 * 16000) + /* * definitions */ @@ -54,6 +62,17 @@ MODULE_LICENSE("GPL"); #define TEA575X_BIT_DUMMY (1<<15) /* buffer */ #define TEA575X_BIT_FREQ_MASK 0x7fff +static struct v4l2_queryctrl radio_qctrl[] = { + { + .id = V4L2_CID_AUDIO_MUTE, + .name = "Mute", + .minimum = 0, + .maximum = 1, + .default_value = 1, + .type = V4L2_CTRL_TYPE_BOOLEAN, + } +}; + /* * lowlevel part */ @@ -89,125 +108,196 @@ static void snd_tea575x_set_freq(struct snd_tea575x *tea) * Linux Video interface */ -static long snd_tea575x_ioctl(struct file *file, - unsigned int cmd, unsigned long data) +static int vidioc_querycap(struct file *file, void *priv, + struct v4l2_capability *v) { -#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,17) - tea575x_t *tea = video_drvdata(file); -#else struct snd_tea575x *tea = video_drvdata(file); -#endif - void __user *arg = (void __user *)data; - - switch(cmd) { - case VIDIOCGCAP: - { - struct video_capability v; - v.type = VID_TYPE_TUNER; - v.channels = 1; - v.audios = 1; - /* No we don't do pictures */ - v.maxwidth = 0; - v.maxheight = 0; - v.minwidth = 0; - v.minheight = 0; - strcpy(v.name, tea->tea5759 ? "TEA5759" : "TEA5757"); - if (copy_to_user(arg,&v,sizeof(v))) - return -EFAULT; - return 0; - } - case VIDIOCGTUNER: - { - struct video_tuner v; - if (copy_from_user(&v, arg,sizeof(v))!=0) - return -EFAULT; - if (v.tuner) /* Only 1 tuner */ - return -EINVAL; - v.rangelow = (87*16000); - v.rangehigh = (108*16000); - v.flags = VIDEO_TUNER_LOW; - v.mode = VIDEO_MODE_AUTO; - strcpy(v.name, "FM"); - v.signal = 0xFFFF; - if (copy_to_user(arg, &v, sizeof(v))) - return -EFAULT; - return 0; - } - case VIDIOCSTUNER: - { - struct video_tuner v; - if(copy_from_user(&v, arg, sizeof(v))) - return -EFAULT; - if(v.tuner!=0) - return -EINVAL; - /* Only 1 tuner so no setting needed ! */ + + strcpy(v->card, tea->tea5759 ? "TEA5759" : "TEA5757"); + strlcpy(v->driver, "tea575x-tuner", sizeof(v->driver)); + strlcpy(v->card, "Maestro Radio", sizeof(v->card)); + sprintf(v->bus_info, "PCI"); + v->version = RADIO_VERSION; + v->capabilities = V4L2_CAP_TUNER; + return 0; +} + +static int vidioc_g_tuner(struct file *file, void *priv, + struct v4l2_tuner *v) +{ + if (v->index > 0) + return -EINVAL; + + strcpy(v->name, "FM"); + v->type = V4L2_TUNER_RADIO; + v->rangelow = FREQ_LO; + v->rangehigh = FREQ_HI; + v->rxsubchans = V4L2_TUNER_SUB_MONO|V4L2_TUNER_SUB_STEREO; + v->capability = V4L2_TUNER_CAP_LOW; + v->audmode = V4L2_TUNER_MODE_MONO; + v->signal = 0xffff; + return 0; +} + +static int vidioc_s_tuner(struct file *file, void *priv, + struct v4l2_tuner *v) +{ + if (v->index > 0) + return -EINVAL; + return 0; +} + +static int vidioc_g_frequency(struct file *file, void *priv, + struct v4l2_frequency *f) +{ + struct snd_tea575x *tea = video_drvdata(file); + + f->type = V4L2_TUNER_RADIO; + f->frequency = tea->freq; + return 0; +} + +static int vidioc_s_frequency(struct file *file, void *priv, + struct v4l2_frequency *f) +{ + struct snd_tea575x *tea = video_drvdata(file); + + if (f->frequency < FREQ_LO || f->frequency > FREQ_HI) + return -EINVAL; + + tea->freq = f->frequency; + + snd_tea575x_set_freq(tea); + + return 0; +} + +static int vidioc_g_audio(struct file *file, void *priv, + struct v4l2_audio *a) +{ + if (a->index > 1) + return -EINVAL; + + strcpy(a->name, "Radio"); + a->capability = V4L2_AUDCAP_STEREO; + return 0; +} + +static int vidioc_s_audio(struct file *file, void *priv, + struct v4l2_audio *a) +{ + if (a->index != 0) + return -EINVAL; + return 0; +} + +static int vidioc_queryctrl(struct file *file, void *priv, + struct v4l2_queryctrl *qc) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) { + if (qc->id && qc->id == radio_qctrl[i].id) { + memcpy(qc, &(radio_qctrl[i]), + sizeof(*qc)); return 0; } - case VIDIOCGFREQ: - if(copy_to_user(arg, &tea->freq, sizeof(tea->freq))) - return -EFAULT; - return 0; - case VIDIOCSFREQ: - if(copy_from_user(&tea->freq, arg, sizeof(tea->freq))) - return -EFAULT; - snd_tea575x_set_freq(tea); - return 0; - case VIDIOCGAUDIO: - { - struct video_audio v; - memset(&v, 0, sizeof(v)); - strcpy(v.name, "Radio"); - if(copy_to_user(arg,&v, sizeof(v))) - return -EFAULT; + } + return -EINVAL; +} + +static int vidioc_g_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + struct snd_tea575x *tea = video_drvdata(file); + +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 17) + switch (ctrl->id) { + case V4L2_CID_AUDIO_MUTE: + if (tea->ops->mute) { + ctrl->value = tea->mute; return 0; } - case VIDIOCSAUDIO: - { - struct video_audio v; - if(copy_from_user(&v, arg, sizeof(v))) - return -EFAULT; -#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 17) - if (tea->ops->mute) - tea->ops->mute(tea, - (v.flags & - VIDEO_AUDIO_MUTE) ? 1 : 0); + } #endif - if(v.audio) - return -EINVAL; + return -EINVAL; +} + +static int vidioc_s_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + struct snd_tea575x *tea = video_drvdata(file); + +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 17) + switch (ctrl->id) { + case V4L2_CID_AUDIO_MUTE: + if (tea->ops->mute) { + tea->ops->mute(tea, ctrl->value); + tea->mute = 1; return 0; } - default: - return -ENOIOCTLCMD; } +#endif + return -EINVAL; +} + +static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i) +{ + *i = 0; + return 0; } -static void snd_tea575x_release(struct video_device *vfd) +static int vidioc_s_input(struct file *filp, void *priv, unsigned int i) { + if (i != 0) + return -EINVAL; + return 0; } static int snd_tea575x_exclusive_open(struct file *file) { -#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,17) - tea575x_t *tea = video_drvdata(file); -#else struct snd_tea575x *tea = video_drvdata(file); -#endif return test_and_set_bit(0, &tea->in_use) ? -EBUSY : 0; } static int snd_tea575x_exclusive_release(struct file *file) { -#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,17) - tea575x_t *tea = video_drvdata(file); -#else struct snd_tea575x *tea = video_drvdata(file); -#endif clear_bit(0, &tea->in_use); return 0; } +static const struct v4l2_file_operations tea575x_fops = { + .owner = THIS_MODULE, + .open = snd_tea575x_exclusive_open, + .release = snd_tea575x_exclusive_release, + .ioctl = video_ioctl2, +}; + +static const struct v4l2_ioctl_ops tea575x_ioctl_ops = { + .vidioc_querycap = vidioc_querycap, + .vidioc_g_tuner = vidioc_g_tuner, + .vidioc_s_tuner = vidioc_s_tuner, + .vidioc_g_audio = vidioc_g_audio, + .vidioc_s_audio = vidioc_s_audio, + .vidioc_g_input = vidioc_g_input, + .vidioc_s_input = vidioc_s_input, + .vidioc_g_frequency = vidioc_g_frequency, + .vidioc_s_frequency = vidioc_s_frequency, + .vidioc_queryctrl = vidioc_queryctrl, + .vidioc_g_ctrl = vidioc_g_ctrl, + .vidioc_s_ctrl = vidioc_s_ctrl, +}; + +static struct video_device tea575x_radio = { + .name = "tea575x-tuner", + .fops = &tea575x_fops, + .ioctl_ops = &tea575x_ioctl_ops, + .release = video_device_release, +}; + /* * initialize all the tea575x chips */ @@ -217,51 +307,59 @@ void snd_tea575x_init(tea575x_t *tea) void snd_tea575x_init(struct snd_tea575x *tea) #endif { + int retval; unsigned int val; + struct video_device *tea575x_radio_inst; val = tea->ops->read(tea); if (val == 0x1ffffff || val == 0) { - snd_printk(KERN_ERR "Cannot find TEA575x chip\n"); + snd_printk(KERN_ERR + "tea575x-tuner: Cannot find TEA575x chip\n"); return; } - memset(&tea->vd, 0, sizeof(tea->vd)); - strcpy(tea->vd.name, tea->tea5759 ? "TEA5759 radio" : "TEA5757 radio"); - tea->vd.release = snd_tea575x_release; - video_set_drvdata(&tea->vd, tea); - tea->vd.fops = &tea->fops; tea->in_use = 0; - tea->fops.owner = tea->card->module; - tea->fops.open = snd_tea575x_exclusive_open; - tea->fops.release = snd_tea575x_exclusive_release; - tea->fops.ioctl = snd_tea575x_ioctl; - if (video_register_device(&tea->vd, VFL_TYPE_RADIO, tea->dev_nr - 1) < 0) { - snd_printk(KERN_ERR "unable to register tea575x tuner\n"); + tea->val = TEA575X_BIT_BAND_FM | TEA575X_BIT_SEARCH_10_40; + tea->freq = 90500 * 16; /* 90.5Mhz default */ + + tea575x_radio_inst = video_device_alloc(); + if (tea575x_radio_inst == NULL) { + printk(KERN_ERR "tea575x-tuner: not enough memory\n"); return; } - tea->vd_registered = 1; - tea->val = TEA575X_BIT_BAND_FM | TEA575X_BIT_SEARCH_10_40; - tea->freq = 90500 * 16; /* 90.5Mhz default */ + memcpy(tea575x_radio_inst, &tea575x_radio, sizeof(tea575x_radio)); + + strcpy(tea575x_radio.name, tea->tea5759 ? + "TEA5759 radio" : "TEA5757 radio"); + + video_set_drvdata(tea575x_radio_inst, tea); + + retval = video_register_device(tea575x_radio_inst, + VFL_TYPE_RADIO, radio_nr); + if (retval) { + printk(KERN_ERR "tea575x-tuner: can't register video device!\n"); + kfree(tea575x_radio_inst); + return; + } snd_tea575x_set_freq(tea); #if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 17) /* mute on init */ - if (tea->ops->mute) + if (tea->ops->mute) { tea->ops->mute(tea, 1); + tea->mute = 1; + } #endif + tea->vd = tea575x_radio_inst; } -#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,17) -void snd_tea575x_exit(tea575x_t *tea) -#else void snd_tea575x_exit(struct snd_tea575x *tea) -#endif { - if (tea->vd_registered) { - video_unregister_device(&tea->vd); - tea->vd_registered = 0; + if (tea->vd) { + video_unregister_device(tea->vd); + tea->vd = NULL; } } diff --git a/linux/sound/oss/btaudio.c b/linux/sound/oss/btaudio.c deleted file mode 100644 index cf10bf82a..000000000 --- a/linux/sound/oss/btaudio.c +++ /dev/null @@ -1,1156 +0,0 @@ -/* - btaudio - bt878 audio dma driver for linux 2.4.x - - (c) 2000-2002 Gerd Knorr <kraxel@bytesex.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, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - -*/ - -#include <linux/module.h> -#include <linux/errno.h> -#include <linux/pci.h> -#include <linux/sched.h> -#include <linux/signal.h> -#include <linux/types.h> -#include <linux/interrupt.h> -#include <linux/init.h> -#include <linux/poll.h> -#include <linux/sound.h> -#include <linux/soundcard.h> -#include <linux/slab.h> -#include <linux/kdev_t.h> -#include <linux/mutex.h> - -#include <asm/uaccess.h> -#include <asm/io.h> -#include "compat.h" - - -/* mmio access */ -#define btwrite(dat,adr) writel((dat), (bta->mmio+(adr))) -#define btread(adr) readl(bta->mmio+(adr)) - -#define btand(dat,adr) btwrite((dat) & btread(adr), adr) -#define btor(dat,adr) btwrite((dat) | btread(adr), adr) -#define btaor(dat,mask,adr) btwrite((dat) | ((mask) & btread(adr)), adr) - -/* registers (shifted because bta->mmio is long) */ -#define REG_INT_STAT (0x100 >> 2) -#define REG_INT_MASK (0x104 >> 2) -#define REG_GPIO_DMA_CTL (0x10c >> 2) -#define REG_PACKET_LEN (0x110 >> 2) -#define REG_RISC_STRT_ADD (0x114 >> 2) -#define REG_RISC_COUNT (0x120 >> 2) - -/* IRQ bits - REG_INT_(STAT|MASK) */ -#define IRQ_SCERR (1 << 19) -#define IRQ_OCERR (1 << 18) -#define IRQ_PABORT (1 << 17) -#define IRQ_RIPERR (1 << 16) -#define IRQ_PPERR (1 << 15) -#define IRQ_FDSR (1 << 14) -#define IRQ_FTRGT (1 << 13) -#define IRQ_FBUS (1 << 12) -#define IRQ_RISCI (1 << 11) -#define IRQ_OFLOW (1 << 3) - -#define IRQ_BTAUDIO (IRQ_SCERR | IRQ_OCERR | IRQ_PABORT | IRQ_RIPERR |\ - IRQ_PPERR | IRQ_FDSR | IRQ_FTRGT | IRQ_FBUS |\ - IRQ_RISCI) - -/* REG_GPIO_DMA_CTL bits */ -#define DMA_CTL_A_PWRDN (1 << 26) -#define DMA_CTL_DA_SBR (1 << 14) -#define DMA_CTL_DA_ES2 (1 << 13) -#define DMA_CTL_ACAP_EN (1 << 4) -#define DMA_CTL_RISC_EN (1 << 1) -#define DMA_CTL_FIFO_EN (1 << 0) - -/* RISC instructions */ -#define RISC_WRITE (0x01 << 28) -#define RISC_JUMP (0x07 << 28) -#define RISC_SYNC (0x08 << 28) - -/* RISC bits */ -#define RISC_WR_SOL (1 << 27) -#define RISC_WR_EOL (1 << 26) -#define RISC_IRQ (1 << 24) -#define RISC_SYNC_RESYNC (1 << 15) -#define RISC_SYNC_FM1 0x06 -#define RISC_SYNC_VRO 0x0c - -#define HWBASE_AD (448000) - -/* -------------------------------------------------------------- */ - -struct btaudio { - /* linked list */ - struct btaudio *next; - - /* device info */ - int dsp_digital; - int dsp_analog; - int mixer_dev; - struct pci_dev *pci; - unsigned int irq; - unsigned long mem; - unsigned long __iomem *mmio; - - /* locking */ - int users; - struct mutex lock; - - /* risc instructions */ - unsigned int risc_size; - unsigned long *risc_cpu; - dma_addr_t risc_dma; - - /* audio data */ - unsigned int buf_size; - unsigned char *buf_cpu; - dma_addr_t buf_dma; - - /* buffer setup */ - int line_bytes; - int line_count; - int block_bytes; - int block_count; - - /* read fifo management */ - int recording; - int dma_block; - int read_offset; - int read_count; - wait_queue_head_t readq; - - /* settings */ - int gain[3]; - int source; - int bits; - int decimation; - int mixcount; - int sampleshift; - int channels; - int analog; - int rate; -}; - -struct cardinfo { - char *name; - int rate; -}; - -static struct btaudio *btaudios; -static unsigned int debug; -static unsigned int irq_debug; - -/* -------------------------------------------------------------- */ - -#define BUF_DEFAULT 128*1024 -#define BUF_MIN 8192 - -static int alloc_buffer(struct btaudio *bta) -{ - if (NULL == bta->buf_cpu) { - for (bta->buf_size = BUF_DEFAULT; bta->buf_size >= BUF_MIN; - bta->buf_size = bta->buf_size >> 1) { - bta->buf_cpu = pci_alloc_consistent - (bta->pci, bta->buf_size, &bta->buf_dma); - if (NULL != bta->buf_cpu) - break; - } - if (NULL == bta->buf_cpu) - return -ENOMEM; - memset(bta->buf_cpu,0,bta->buf_size); - } - if (NULL == bta->risc_cpu) { - bta->risc_size = PAGE_SIZE; - bta->risc_cpu = pci_alloc_consistent - (bta->pci, bta->risc_size, &bta->risc_dma); - if (NULL == bta->risc_cpu) { - pci_free_consistent(bta->pci, bta->buf_size, bta->buf_cpu, bta->buf_dma); - bta->buf_cpu = NULL; - return -ENOMEM; - } - } - return 0; -} - -static void free_buffer(struct btaudio *bta) -{ - if (NULL != bta->buf_cpu) { - pci_free_consistent(bta->pci, bta->buf_size, - bta->buf_cpu, bta->buf_dma); - bta->buf_cpu = NULL; - } - if (NULL != bta->risc_cpu) { - pci_free_consistent(bta->pci, bta->risc_size, - bta->risc_cpu, bta->risc_dma); - bta->risc_cpu = NULL; - } -} - -static int make_risc(struct btaudio *bta) -{ - int rp, bp, line, block; - unsigned long risc; - - bta->block_bytes = bta->buf_size >> 4; - bta->block_count = 1 << 4; - bta->line_bytes = bta->block_bytes; - bta->line_count = bta->block_count; - while (bta->line_bytes > 4095) { - bta->line_bytes >>= 1; - bta->line_count <<= 1; - } - if (bta->line_count > 255) - return -EINVAL; - if (debug) - printk(KERN_DEBUG - "btaudio: bufsize=%d - bs=%d bc=%d - ls=%d, lc=%d\n", - bta->buf_size,bta->block_bytes,bta->block_count, - bta->line_bytes,bta->line_count); - rp = 0; bp = 0; - block = 0; - bta->risc_cpu[rp++] = cpu_to_le32(RISC_SYNC|RISC_SYNC_FM1); - bta->risc_cpu[rp++] = cpu_to_le32(0); - for (line = 0; line < bta->line_count; line++) { - risc = RISC_WRITE | RISC_WR_SOL | RISC_WR_EOL; - risc |= bta->line_bytes; - if (0 == (bp & (bta->block_bytes-1))) { - risc |= RISC_IRQ; - risc |= (block & 0x0f) << 16; - risc |= (~block & 0x0f) << 20; - block++; - } - bta->risc_cpu[rp++] = cpu_to_le32(risc); - bta->risc_cpu[rp++] = cpu_to_le32(bta->buf_dma + bp); - bp += bta->line_bytes; - } - bta->risc_cpu[rp++] = cpu_to_le32(RISC_SYNC|RISC_SYNC_VRO); - bta->risc_cpu[rp++] = cpu_to_le32(0); - bta->risc_cpu[rp++] = cpu_to_le32(RISC_JUMP); - bta->risc_cpu[rp++] = cpu_to_le32(bta->risc_dma); - return 0; -} - -static int start_recording(struct btaudio *bta) -{ - int ret; - - if (0 != (ret = alloc_buffer(bta))) - return ret; - if (0 != (ret = make_risc(bta))) - return ret; - - btwrite(bta->risc_dma, REG_RISC_STRT_ADD); - btwrite((bta->line_count << 16) | bta->line_bytes, - REG_PACKET_LEN); - btwrite(IRQ_BTAUDIO, REG_INT_MASK); - if (bta->analog) { - btwrite(DMA_CTL_ACAP_EN | - DMA_CTL_RISC_EN | - DMA_CTL_FIFO_EN | - DMA_CTL_DA_ES2 | - ((bta->bits == 8) ? DMA_CTL_DA_SBR : 0) | - (bta->gain[bta->source] << 28) | - (bta->source << 24) | - (bta->decimation << 8), - REG_GPIO_DMA_CTL); - } else { - btwrite(DMA_CTL_ACAP_EN | - DMA_CTL_RISC_EN | - DMA_CTL_FIFO_EN | - DMA_CTL_DA_ES2 | - DMA_CTL_A_PWRDN | - (1 << 6) | - ((bta->bits == 8) ? DMA_CTL_DA_SBR : 0) | - (bta->gain[bta->source] << 28) | - (bta->source << 24) | - (bta->decimation << 8), - REG_GPIO_DMA_CTL); - } - bta->dma_block = 0; - bta->read_offset = 0; - bta->read_count = 0; - bta->recording = 1; - if (debug) - printk(KERN_DEBUG "btaudio: recording started\n"); - return 0; -} - -static void stop_recording(struct btaudio *bta) -{ - btand(~15, REG_GPIO_DMA_CTL); - bta->recording = 0; - if (debug) - printk(KERN_DEBUG "btaudio: recording stopped\n"); -} - - -/* -------------------------------------------------------------- */ - -static int btaudio_mixer_open(struct inode *inode, struct file *file) -{ - int minor = iminor(inode); - struct btaudio *bta; - - for (bta = btaudios; bta != NULL; bta = bta->next) - if (bta->mixer_dev == minor) - break; - if (NULL == bta) - return -ENODEV; - - if (debug) - printk("btaudio: open mixer [%d]\n",minor); - file->private_data = bta; - return 0; -} - -static int btaudio_mixer_release(struct inode *inode, struct file *file) -{ - return 0; -} - -static int btaudio_mixer_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) -{ - struct btaudio *bta = file->private_data; - int ret,val=0,i=0; - void __user *argp = (void __user *)arg; - - if (cmd == SOUND_MIXER_INFO) { - mixer_info info; - memset(&info,0,sizeof(info)); - strlcpy(info.id,"bt878",sizeof(info.id)); - strlcpy(info.name,"Brooktree Bt878 audio",sizeof(info.name)); - info.modify_counter = bta->mixcount; - if (copy_to_user(argp, &info, sizeof(info))) - return -EFAULT; - return 0; - } - if (cmd == SOUND_OLD_MIXER_INFO) { - _old_mixer_info info; - memset(&info,0,sizeof(info)); - strlcpy(info.id, "bt878", sizeof(info.id)); - strlcpy(info.name,"Brooktree Bt878 audio",sizeof(info.name)); - if (copy_to_user(argp, &info, sizeof(info))) - return -EFAULT; - return 0; - } - if (cmd == OSS_GETVERSION) - return put_user(SOUND_VERSION, (int __user *)argp); - - /* read */ - if (_SIOC_DIR(cmd) & _SIOC_WRITE) - if (get_user(val, (int __user *)argp)) - return -EFAULT; - - switch (cmd) { - case MIXER_READ(SOUND_MIXER_CAPS): - ret = SOUND_CAP_EXCL_INPUT; - break; - case MIXER_READ(SOUND_MIXER_STEREODEVS): - ret = 0; - break; - case MIXER_READ(SOUND_MIXER_RECMASK): - case MIXER_READ(SOUND_MIXER_DEVMASK): - ret = SOUND_MASK_LINE1|SOUND_MASK_LINE2|SOUND_MASK_LINE3; - break; - - case MIXER_WRITE(SOUND_MIXER_RECSRC): - if (val & SOUND_MASK_LINE1 && bta->source != 0) - bta->source = 0; - else if (val & SOUND_MASK_LINE2 && bta->source != 1) - bta->source = 1; - else if (val & SOUND_MASK_LINE3 && bta->source != 2) - bta->source = 2; - btaor((bta->gain[bta->source] << 28) | - (bta->source << 24), - 0x0cffffff, REG_GPIO_DMA_CTL); - case MIXER_READ(SOUND_MIXER_RECSRC): - switch (bta->source) { - case 0: ret = SOUND_MASK_LINE1; break; - case 1: ret = SOUND_MASK_LINE2; break; - case 2: ret = SOUND_MASK_LINE3; break; - default: ret = 0; - } - break; - - case MIXER_WRITE(SOUND_MIXER_LINE1): - case MIXER_WRITE(SOUND_MIXER_LINE2): - case MIXER_WRITE(SOUND_MIXER_LINE3): - if (MIXER_WRITE(SOUND_MIXER_LINE1) == cmd) - i = 0; - if (MIXER_WRITE(SOUND_MIXER_LINE2) == cmd) - i = 1; - if (MIXER_WRITE(SOUND_MIXER_LINE3) == cmd) - i = 2; - bta->gain[i] = (val & 0xff) * 15 / 100; - if (bta->gain[i] > 15) bta->gain[i] = 15; - if (bta->gain[i] < 0) bta->gain[i] = 0; - if (i == bta->source) - btaor((bta->gain[bta->source]<<28), - 0x0fffffff, REG_GPIO_DMA_CTL); - ret = bta->gain[i] * 100 / 15; - ret |= ret << 8; - break; - - case MIXER_READ(SOUND_MIXER_LINE1): - case MIXER_READ(SOUND_MIXER_LINE2): - case MIXER_READ(SOUND_MIXER_LINE3): - if (MIXER_READ(SOUND_MIXER_LINE1) == cmd) - i = 0; - if (MIXER_READ(SOUND_MIXER_LINE2) == cmd) - i = 1; - if (MIXER_READ(SOUND_MIXER_LINE3) == cmd) - i = 2; - ret = bta->gain[i] * 100 / 15; - ret |= ret << 8; - break; - - default: - return -EINVAL; - } - if (put_user(ret, (int __user *)argp)) - return -EFAULT; - return 0; -} - -static const struct file_operations btaudio_mixer_fops = { - .owner = THIS_MODULE, - .llseek = no_llseek, - .open = btaudio_mixer_open, - .release = btaudio_mixer_release, - .ioctl = btaudio_mixer_ioctl, -}; - -/* -------------------------------------------------------------- */ - -static int btaudio_dsp_open(struct inode *inode, struct file *file, - struct btaudio *bta, int analog) -{ - mutex_lock(&bta->lock); - if (bta->users) - goto busy; - bta->users++; - file->private_data = bta; - - bta->analog = analog; - bta->dma_block = 0; - bta->read_offset = 0; - bta->read_count = 0; - bta->sampleshift = 0; - - mutex_unlock(&bta->lock); - return 0; - - busy: - mutex_unlock(&bta->lock); - return -EBUSY; -} - -static int btaudio_dsp_open_digital(struct inode *inode, struct file *file) -{ - int minor = iminor(inode); - struct btaudio *bta; - - for (bta = btaudios; bta != NULL; bta = bta->next) - if (bta->dsp_digital == minor) - break; - if (NULL == bta) - return -ENODEV; - - if (debug) - printk("btaudio: open digital dsp [%d]\n",minor); - return btaudio_dsp_open(inode,file,bta,0); -} - -static int btaudio_dsp_open_analog(struct inode *inode, struct file *file) -{ - int minor = iminor(inode); - struct btaudio *bta; - - for (bta = btaudios; bta != NULL; bta = bta->next) - if (bta->dsp_analog == minor) - break; - if (NULL == bta) - return -ENODEV; - - if (debug) - printk("btaudio: open analog dsp [%d]\n",minor); - return btaudio_dsp_open(inode,file,bta,1); -} - -static int btaudio_dsp_release(struct inode *inode, struct file *file) -{ - struct btaudio *bta = file->private_data; - - mutex_lock(&bta->lock); - if (bta->recording) - stop_recording(bta); - bta->users--; - mutex_unlock(&bta->lock); - return 0; -} - -static ssize_t btaudio_dsp_read(struct file *file, char __user *buffer, - size_t swcount, loff_t *ppos) -{ - struct btaudio *bta = file->private_data; - int hwcount = swcount << bta->sampleshift; - int nsrc, ndst, err, ret = 0; - DECLARE_WAITQUEUE(wait, current); - - add_wait_queue(&bta->readq, &wait); - mutex_lock(&bta->lock); - while (swcount > 0) { - if (0 == bta->read_count) { - if (!bta->recording) { - if (0 != (err = start_recording(bta))) { - if (0 == ret) - ret = err; - break; - } - } - if (file->f_flags & O_NONBLOCK) { - if (0 == ret) - ret = -EAGAIN; - break; - } - mutex_unlock(&bta->lock); - current->state = TASK_INTERRUPTIBLE; - schedule(); - mutex_lock(&bta->lock); - if(signal_pending(current)) { - if (0 == ret) - ret = -EINTR; - break; - } - } - nsrc = (bta->read_count < hwcount) ? bta->read_count : hwcount; - if (nsrc > bta->buf_size - bta->read_offset) - nsrc = bta->buf_size - bta->read_offset; - ndst = nsrc >> bta->sampleshift; - - if ((bta->analog && 0 == bta->sampleshift) || - (!bta->analog && 2 == bta->channels)) { - /* just copy */ - if (copy_to_user(buffer + ret, bta->buf_cpu + bta->read_offset, nsrc)) { - if (0 == ret) - ret = -EFAULT; - break; - } - - } else if (!bta->analog) { - /* stereo => mono (digital audio) */ - __s16 *src = (__s16*)(bta->buf_cpu + bta->read_offset); - __s16 __user *dst = (__s16 __user *)(buffer + ret); - __s16 avg; - int n = ndst>>1; - if (!access_ok(VERIFY_WRITE, dst, ndst)) { - if (0 == ret) - ret = -EFAULT; - break; - } - for (; n; n--, dst++) { - avg = (__s16)le16_to_cpu(*src) / 2; src++; - avg += (__s16)le16_to_cpu(*src) / 2; src++; - __put_user(cpu_to_le16(avg),dst); - } - - } else if (8 == bta->bits) { - /* copy + byte downsampling (audio A/D) */ - __u8 *src = bta->buf_cpu + bta->read_offset; - __u8 __user *dst = buffer + ret; - int n = ndst; - if (!access_ok(VERIFY_WRITE, dst, ndst)) { - if (0 == ret) - ret = -EFAULT; - break; - } - for (; n; n--, src += (1 << bta->sampleshift), dst++) - __put_user(*src, dst); - - } else { - /* copy + word downsampling (audio A/D) */ - __u16 *src = (__u16*)(bta->buf_cpu + bta->read_offset); - __u16 __user *dst = (__u16 __user *)(buffer + ret); - int n = ndst>>1; - if (!access_ok(VERIFY_WRITE,dst,ndst)) { - if (0 == ret) - ret = -EFAULT; - break; - } - for (; n; n--, src += (1 << bta->sampleshift), dst++) - __put_user(*src, dst); - } - - ret += ndst; - swcount -= ndst; - hwcount -= nsrc; - bta->read_count -= nsrc; - bta->read_offset += nsrc; - if (bta->read_offset == bta->buf_size) - bta->read_offset = 0; - } - mutex_unlock(&bta->lock); - remove_wait_queue(&bta->readq, &wait); - current->state = TASK_RUNNING; - return ret; -} - -static ssize_t btaudio_dsp_write(struct file *file, const char __user *buffer, - size_t count, loff_t *ppos) -{ - return -EINVAL; -} - -static int btaudio_dsp_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) -{ - struct btaudio *bta = file->private_data; - int s, i, ret, val = 0; - void __user *argp = (void __user *)arg; - int __user *p = argp; - - switch (cmd) { - case OSS_GETVERSION: - return put_user(SOUND_VERSION, p); - case SNDCTL_DSP_GETCAPS: - return 0; - - case SNDCTL_DSP_SPEED: - if (get_user(val, p)) - return -EFAULT; - if (bta->analog) { - for (s = 0; s < 16; s++) - if (val << s >= HWBASE_AD*4/15) - break; - for (i = 15; i >= 5; i--) - if (val << s <= HWBASE_AD*4/i) - break; - bta->sampleshift = s; - bta->decimation = i; - if (debug) - printk(KERN_DEBUG "btaudio: rate: req=%d " - "dec=%d shift=%d hwrate=%d swrate=%d\n", - val,i,s,(HWBASE_AD*4/i),(HWBASE_AD*4/i)>>s); - } else { - bta->sampleshift = (bta->channels == 2) ? 0 : 1; - bta->decimation = 0; - } - if (bta->recording) { - mutex_lock(&bta->lock); - stop_recording(bta); - start_recording(bta); - mutex_unlock(&bta->lock); - } - /* fall through */ - case SOUND_PCM_READ_RATE: - if (bta->analog) { - return put_user(HWBASE_AD*4/bta->decimation>>bta->sampleshift, p); - } else { - return put_user(bta->rate, p); - } - - case SNDCTL_DSP_STEREO: - if (!bta->analog) { - if (get_user(val, p)) - return -EFAULT; - bta->channels = (val > 0) ? 2 : 1; - bta->sampleshift = (bta->channels == 2) ? 0 : 1; - if (debug) - printk(KERN_INFO - "btaudio: stereo=%d channels=%d\n", - val,bta->channels); - } else { - if (val == 1) - return -EFAULT; - else { - bta->channels = 1; - if (debug) - printk(KERN_INFO - "btaudio: stereo=0 channels=1\n"); - } - } - return put_user((bta->channels)-1, p); - - case SNDCTL_DSP_CHANNELS: - if (!bta->analog) { - if (get_user(val, p)) - return -EFAULT; - bta->channels = (val > 1) ? 2 : 1; - bta->sampleshift = (bta->channels == 2) ? 0 : 1; - if (debug) - printk(KERN_DEBUG - "btaudio: val=%d channels=%d\n", - val,bta->channels); - } - /* fall through */ - case SOUND_PCM_READ_CHANNELS: - return put_user(bta->channels, p); - - case SNDCTL_DSP_GETFMTS: /* Returns a mask */ - if (bta->analog) - return put_user(AFMT_S16_LE|AFMT_S8, p); - else - return put_user(AFMT_S16_LE, p); - - case SNDCTL_DSP_SETFMT: /* Selects ONE fmt*/ - if (get_user(val, p)) - return -EFAULT; - if (val != AFMT_QUERY) { - if (bta->analog) - bta->bits = (val == AFMT_S8) ? 8 : 16; - else - bta->bits = 16; - if (bta->recording) { - mutex_lock(&bta->lock); - stop_recording(bta); - start_recording(bta); - mutex_unlock(&bta->lock); - } - } - if (debug) - printk(KERN_DEBUG "btaudio: fmt: bits=%d\n",bta->bits); - return put_user((bta->bits==16) ? AFMT_S16_LE : AFMT_S8, - p); - break; - case SOUND_PCM_READ_BITS: - return put_user(bta->bits, p); - - case SNDCTL_DSP_NONBLOCK: - file->f_flags |= O_NONBLOCK; - return 0; - - case SNDCTL_DSP_RESET: - if (bta->recording) { - mutex_lock(&bta->lock); - stop_recording(bta); - mutex_unlock(&bta->lock); - } - return 0; - case SNDCTL_DSP_GETBLKSIZE: - if (!bta->recording) { - if (0 != (ret = alloc_buffer(bta))) - return ret; - if (0 != (ret = make_risc(bta))) - return ret; - } - return put_user(bta->block_bytes>>bta->sampleshift,p); - - case SNDCTL_DSP_SYNC: - /* NOP */ - return 0; - case SNDCTL_DSP_GETISPACE: - { - audio_buf_info info; - if (!bta->recording) - return -EINVAL; - info.fragsize = bta->block_bytes>>bta->sampleshift; - info.fragstotal = bta->block_count; - info.bytes = bta->read_count; - info.fragments = info.bytes / info.fragsize; - if (debug) - printk(KERN_DEBUG "btaudio: SNDCTL_DSP_GETISPACE " - "returns %d/%d/%d/%d\n", - info.fragsize, info.fragstotal, - info.bytes, info.fragments); - if (copy_to_user(argp, &info, sizeof(info))) - return -EFAULT; - return 0; - } -#if 0 /* TODO */ - case SNDCTL_DSP_GETTRIGGER: - case SNDCTL_DSP_SETTRIGGER: - case SNDCTL_DSP_SETFRAGMENT: -#endif - default: - return -EINVAL; - } -} - -static unsigned int btaudio_dsp_poll(struct file *file, struct poll_table_struct *wait) -{ - struct btaudio *bta = file->private_data; - unsigned int mask = 0; - - poll_wait(file, &bta->readq, wait); - - if (0 != bta->read_count) - mask |= (POLLIN | POLLRDNORM); - - return mask; -} - -static const struct file_operations btaudio_digital_dsp_fops = { - .owner = THIS_MODULE, - .llseek = no_llseek, - .open = btaudio_dsp_open_digital, - .release = btaudio_dsp_release, - .read = btaudio_dsp_read, - .write = btaudio_dsp_write, - .ioctl = btaudio_dsp_ioctl, - .poll = btaudio_dsp_poll, -}; - -static const struct file_operations btaudio_analog_dsp_fops = { - .owner = THIS_MODULE, - .llseek = no_llseek, - .open = btaudio_dsp_open_analog, - .release = btaudio_dsp_release, - .read = btaudio_dsp_read, - .write = btaudio_dsp_write, - .ioctl = btaudio_dsp_ioctl, - .poll = btaudio_dsp_poll, -}; - -/* -------------------------------------------------------------- */ - -static char *irq_name[] = { "", "", "", "OFLOW", "", "", "", "", "", "", "", - "RISCI", "FBUS", "FTRGT", "FDSR", "PPERR", - "RIPERR", "PABORT", "OCERR", "SCERR" }; - -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19) -static irqreturn_t btaudio_irq(int irq, void *dev_id, struct pt_regs * regs) -#else -static irqreturn_t btaudio_irq(int irq, void *dev_id) -#endif -{ - int count = 0; - u32 stat,astat; - struct btaudio *bta = dev_id; - int handled = 0; - - for (;;) { - count++; - stat = btread(REG_INT_STAT); - astat = stat & btread(REG_INT_MASK); - if (!astat) - return IRQ_RETVAL(handled); - handled = 1; - btwrite(astat,REG_INT_STAT); - - if (irq_debug) { - int i; - printk(KERN_DEBUG "btaudio: irq loop=%d risc=%x, bits:", - count, stat>>28); - for (i = 0; i < (sizeof(irq_name)/sizeof(char*)); i++) { - if (stat & (1 << i)) - printk(" %s",irq_name[i]); - if (astat & (1 << i)) - printk("*"); - } - printk("\n"); - } - if (stat & IRQ_RISCI) { - int blocks; - blocks = (stat >> 28) - bta->dma_block; - if (blocks < 0) - blocks += bta->block_count; - bta->dma_block = stat >> 28; - if (bta->read_count + 2*bta->block_bytes > bta->buf_size) { - stop_recording(bta); - printk(KERN_INFO "btaudio: buffer overrun\n"); - } - if (blocks > 0) { - bta->read_count += blocks * bta->block_bytes; - wake_up_interruptible(&bta->readq); - } - } - if (count > 10) { - printk(KERN_WARNING - "btaudio: Oops - irq mask cleared\n"); - btwrite(0, REG_INT_MASK); - } - } - return IRQ_NONE; -} - -/* -------------------------------------------------------------- */ - -static unsigned int dsp1 = -1; -static unsigned int dsp2 = -1; -static unsigned int mixer = -1; -static int latency = -1; -static int digital = 1; -static int analog = 1; -static int rate; - -#define BTA_OSPREY200 1 - -static struct cardinfo cards[] = { - [0] = { - .name = "default", - .rate = 32000, - }, - [BTA_OSPREY200] = { - .name = "Osprey 200", - .rate = 44100, - }, -}; - -static int __devinit btaudio_probe(struct pci_dev *pci_dev, - const struct pci_device_id *pci_id) -{ - struct btaudio *bta; - struct cardinfo *card = &cards[pci_id->driver_data]; - unsigned char revision,lat; - int rc = -EBUSY; - - if (pci_enable_device(pci_dev)) - return -EIO; - if (!request_mem_region(pci_resource_start(pci_dev,0), - pci_resource_len(pci_dev,0), - "btaudio")) { - return -EBUSY; - } - - bta = kzalloc(sizeof(*bta),GFP_ATOMIC); - if (!bta) { - rc = -ENOMEM; - goto fail0; - } - - bta->pci = pci_dev; - bta->irq = pci_dev->irq; - bta->mem = pci_resource_start(pci_dev,0); - bta->mmio = ioremap(pci_resource_start(pci_dev,0), - pci_resource_len(pci_dev,0)); - - bta->source = 1; - bta->bits = 8; - bta->channels = 1; - if (bta->analog) { - bta->decimation = 15; - } else { - bta->decimation = 0; - bta->sampleshift = 1; - } - - /* sample rate */ - bta->rate = card->rate; - if (rate) - bta->rate = rate; - - mutex_init(&bta->lock); - init_waitqueue_head(&bta->readq); - - if (-1 != latency) { - printk(KERN_INFO "btaudio: setting pci latency timer to %d\n", - latency); - pci_write_config_byte(pci_dev, PCI_LATENCY_TIMER, latency); - } - pci_read_config_byte(pci_dev, PCI_CLASS_REVISION, &revision); - pci_read_config_byte(pci_dev, PCI_LATENCY_TIMER, &lat); - printk(KERN_INFO "btaudio: Bt%x (rev %d) at %02x:%02x.%x, ", - pci_dev->device,revision,pci_dev->bus->number, - PCI_SLOT(pci_dev->devfn),PCI_FUNC(pci_dev->devfn)); - printk("irq: %d, latency: %d, mmio: 0x%lx\n", - bta->irq, lat, bta->mem); - printk("btaudio: using card config \"%s\"\n", card->name); - - /* init hw */ - btwrite(0, REG_GPIO_DMA_CTL); - btwrite(0, REG_INT_MASK); - btwrite(~0U, REG_INT_STAT); - pci_set_master(pci_dev); - - if ((rc = request_irq(bta->irq, btaudio_irq, IRQF_SHARED|IRQF_DISABLED, - "btaudio",(void *)bta)) < 0) { - printk(KERN_WARNING - "btaudio: can't request irq (rc=%d)\n",rc); - goto fail1; - } - - /* register devices */ - if (digital) { - rc = bta->dsp_digital = -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 17) - register_sound_dsp(&btaudio_digital_dsp_fops, dsp1); -#else - register_sound_dsp((struct file_operations *)&btaudio_digital_dsp_fops, dsp1); -#endif - if (rc < 0) { - printk(KERN_WARNING - "btaudio: can't register digital dsp (rc=%d)\n",rc); - goto fail2; - } - printk(KERN_INFO "btaudio: registered device dsp%d [digital]\n", - bta->dsp_digital >> 4); - } - if (analog) { - rc = bta->dsp_analog = -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 17) - register_sound_dsp(&btaudio_analog_dsp_fops, dsp2); -#else - register_sound_dsp((struct file_operations *)&btaudio_analog_dsp_fops, dsp2); -#endif - if (rc < 0) { - printk(KERN_WARNING - "btaudio: can't register analog dsp (rc=%d)\n",rc); - goto fail3; - } - printk(KERN_INFO "btaudio: registered device dsp%d [analog]\n", - bta->dsp_analog >> 4); -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 17) - rc = bta->mixer_dev = register_sound_mixer(&btaudio_mixer_fops, mixer); -#else - rc = bta->mixer_dev = register_sound_mixer((struct file_operations *)&btaudio_mixer_fops, mixer); -#endif - if (rc < 0) { - printk(KERN_WARNING - "btaudio: can't register mixer (rc=%d)\n",rc); - goto fail4; - } - printk(KERN_INFO "btaudio: registered device mixer%d\n", - bta->mixer_dev >> 4); - } - - /* hook into linked list */ - bta->next = btaudios; - btaudios = bta; - - pci_set_drvdata(pci_dev,bta); - return 0; - - fail4: - unregister_sound_dsp(bta->dsp_analog); - fail3: - if (digital) - unregister_sound_dsp(bta->dsp_digital); - fail2: - free_irq(bta->irq,bta); - fail1: - iounmap(bta->mmio); - kfree(bta); - fail0: - release_mem_region(pci_resource_start(pci_dev,0), - pci_resource_len(pci_dev,0)); - return rc; -} - -static void __devexit btaudio_remove(struct pci_dev *pci_dev) -{ - struct btaudio *bta = pci_get_drvdata(pci_dev); - struct btaudio *walk; - - /* turn off all DMA / IRQs */ - btand(~15, REG_GPIO_DMA_CTL); - btwrite(0, REG_INT_MASK); - btwrite(~0U, REG_INT_STAT); - - /* unregister devices */ - if (digital) { - unregister_sound_dsp(bta->dsp_digital); - } - if (analog) { - unregister_sound_dsp(bta->dsp_analog); - unregister_sound_mixer(bta->mixer_dev); - } - - /* free resources */ - free_buffer(bta); - free_irq(bta->irq,bta); - release_mem_region(pci_resource_start(pci_dev,0), - pci_resource_len(pci_dev,0)); - iounmap(bta->mmio); - - /* remove from linked list */ - if (bta == btaudios) { - btaudios = NULL; - } else { - for (walk = btaudios; walk->next != bta; walk = walk->next) - ; /* if (NULL == walk->next) BUG(); */ - walk->next = bta->next; - } - - pci_set_drvdata(pci_dev, NULL); - kfree(bta); - return; -} - -/* -------------------------------------------------------------- */ - -static struct pci_device_id btaudio_pci_tbl[] = { - { - .vendor = PCI_VENDOR_ID_BROOKTREE, - .device = 0x0878, - .subvendor = 0x0070, - .subdevice = 0xff01, - .driver_data = BTA_OSPREY200, - },{ - .vendor = PCI_VENDOR_ID_BROOKTREE, - .device = 0x0878, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - },{ - .vendor = PCI_VENDOR_ID_BROOKTREE, - .device = 0x0878, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - },{ - /* --- end of list --- */ - } -}; - -static struct pci_driver btaudio_pci_driver = { - .name = "btaudio", - .id_table = btaudio_pci_tbl, - .probe = btaudio_probe, - .remove = __devexit_p(btaudio_remove), -}; - -static int btaudio_init_module(void) -{ - printk(KERN_INFO "btaudio: driver version 0.7 loaded [%s%s%s]\n", - digital ? "digital" : "", - analog && digital ? "+" : "", - analog ? "analog" : ""); - return pci_register_driver(&btaudio_pci_driver); -} - -static void btaudio_cleanup_module(void) -{ - pci_unregister_driver(&btaudio_pci_driver); - return; -} - -module_init(btaudio_init_module); -module_exit(btaudio_cleanup_module); - -module_param(dsp1, int, S_IRUGO); -module_param(dsp2, int, S_IRUGO); -module_param(mixer, int, S_IRUGO); -module_param(debug, int, S_IRUGO | S_IWUSR); -module_param(irq_debug, int, S_IRUGO | S_IWUSR); -module_param(digital, int, S_IRUGO); -module_param(analog, int, S_IRUGO); -module_param(rate, int, S_IRUGO); -module_param(latency, int, S_IRUGO); -MODULE_PARM_DESC(latency,"pci latency timer"); - -MODULE_DEVICE_TABLE(pci, btaudio_pci_tbl); -MODULE_DESCRIPTION("bt878 audio dma driver"); -MODULE_AUTHOR("Gerd Knorr"); -MODULE_LICENSE("GPL"); - -/* - * Local variables: - * c-basic-offset: 8 - * End: - */ |