diff options
88 files changed, 7147 insertions, 686 deletions
diff --git a/linux/Documentation/video4linux/CARDLIST.em28xx b/linux/Documentation/video4linux/CARDLIST.em28xx index a98a688c1..873630e7e 100644 --- a/linux/Documentation/video4linux/CARDLIST.em28xx +++ b/linux/Documentation/video4linux/CARDLIST.em28xx @@ -65,3 +65,4 @@ 67 -> Terratec Grabby (em2860) [0ccd:0096] 68 -> Terratec AV350 (em2860) [0ccd:0084] 69 -> KWorld ATSC 315U HDTV TV Box (em2882) [eb1a:a313] + 70 -> Evga inDtube (em2882) diff --git a/linux/Documentation/video4linux/v4l2-framework.txt b/linux/Documentation/video4linux/v4l2-framework.txt index d54c1e4c6..ba4706afc 100644 --- a/linux/Documentation/video4linux/v4l2-framework.txt +++ b/linux/Documentation/video4linux/v4l2-framework.txt @@ -390,6 +390,30 @@ later date. It differs between i2c drivers and as such can be confusing. To see which chip variants are supported you can look in the i2c driver code for the i2c_device_id table. This lists all the possibilities. +There are two more helper functions: + +v4l2_i2c_new_subdev_cfg: this function adds new irq and platform_data +arguments and has both 'addr' and 'probed_addrs' arguments: if addr is not +0 then that will be used (non-probing variant), otherwise the probed_addrs +are probed. + +For example: this will probe for address 0x10: + +struct v4l2_subdev *sd = v4l2_i2c_new_subdev_cfg(v4l2_dev, adapter, + "module_foo", "chipid", 0, NULL, 0, I2C_ADDRS(0x10)); + +v4l2_i2c_new_subdev_board uses an i2c_board_info struct which is passed +to the i2c driver and replaces the irq, platform_data and addr arguments. + +If the subdev supports the s_config core ops, then that op is called with +the irq and platform_data arguments after the subdev was setup. The older +v4l2_i2c_new_(probed_)subdev functions will call s_config as well, but with +irq set to 0 and platform_data set to NULL. + +Note that in the next kernel release the functions v4l2_i2c_new_subdev, +v4l2_i2c_new_probed_subdev and v4l2_i2c_new_probed_subdev_addr will all be +replaced by a single v4l2_i2c_new_subdev that is identical to +v4l2_i2c_new_subdev_cfg but without the irq and platform_data arguments. struct video_device ------------------- diff --git a/linux/drivers/media/common/ir-keymaps.c b/linux/drivers/media/common/ir-keymaps.c index d5fd71968..56fa64b4c 100644 --- a/linux/drivers/media/common/ir-keymaps.c +++ b/linux/drivers/media/common/ir-keymaps.c @@ -2800,3 +2800,26 @@ IR_KEYTAB_TYPE ir_codes_dm1105_nec[IR_KEYTAB_SIZE] = { [0x1b] = KEY_B, /*recall*/ }; EXPORT_SYMBOL_GPL(ir_codes_dm1105_nec); + +/* EVGA inDtube + Devin Heitmueller <devin.heitmueller@gmail.com> + */ +IR_KEYTAB_TYPE ir_codes_evga_indtube[IR_KEYTAB_SIZE] = { + [0x12] = KEY_POWER, + [0x02] = KEY_MODE, /* TV */ + [0x14] = KEY_MUTE, + [0x1a] = KEY_CHANNELUP, + [0x16] = KEY_TV2, /* PIP */ + [0x1d] = KEY_VOLUMEUP, + [0x05] = KEY_CHANNELDOWN, + [0x0f] = KEY_PLAYPAUSE, + [0x19] = KEY_VOLUMEDOWN, + [0x1c] = KEY_REWIND, + [0x0d] = KEY_RECORD, + [0x18] = KEY_FORWARD, + [0x1e] = KEY_PREVIOUS, + [0x1b] = KEY_STOP, + [0x1f] = KEY_NEXT, + [0x13] = KEY_CAMERA, +}; +EXPORT_SYMBOL_GPL(ir_codes_evga_indtube); diff --git a/linux/drivers/media/dvb/frontends/stv0900.h b/linux/drivers/media/dvb/frontends/stv0900.h index 8a1332c20..bf4e9b633 100644 --- a/linux/drivers/media/dvb/frontends/stv0900.h +++ b/linux/drivers/media/dvb/frontends/stv0900.h @@ -29,6 +29,11 @@ #include <linux/dvb/frontend.h> #include "dvb_frontend.h" +struct stv0900_reg { + u16 addr; + u8 val; +}; + struct stv0900_config { u8 demod_address; u32 xtal; @@ -38,7 +43,7 @@ struct stv0900_config { u8 path1_mode; u8 path2_mode; - + struct stv0900_reg *ts_config_regs; u8 tun1_maddress;/* 0, 1, 2, 3 for 0xc0, 0xc2, 0xc4, 0xc6 */ u8 tun2_maddress; u8 tun1_adc;/* 1 for stv6110, 2 for stb6100 */ diff --git a/linux/drivers/media/dvb/frontends/stv0900_core.c b/linux/drivers/media/dvb/frontends/stv0900_core.c index 899b1e7ed..84bf35edb 100644 --- a/linux/drivers/media/dvb/frontends/stv0900_core.c +++ b/linux/drivers/media/dvb/frontends/stv0900_core.c @@ -149,31 +149,31 @@ void stv0900_write_reg(struct stv0900_internal *i_params, u16 reg_addr, dprintk(KERN_ERR "%s: i2c error %d\n", __func__, ret); } -u8 stv0900_read_reg(struct stv0900_internal *i_params, u16 reg_addr) +u8 stv0900_read_reg(struct stv0900_internal *i_params, u16 reg) { - u8 data[2]; int ret; - struct i2c_msg i2cmsg = { - .addr = i_params->i2c_addr, - .flags = 0, - .len = 2, - .buf = data, + u8 b0[] = { MSB(reg), LSB(reg) }; + u8 buf = 0; + struct i2c_msg msg[] = { + { + .addr = i_params->i2c_addr, + .flags = 0, + .buf = b0, + .len = 2, + }, { + .addr = i_params->i2c_addr, + .flags = I2C_M_RD, + .buf = &buf, + .len = 1, + }, }; - data[0] = MSB(reg_addr); - data[1] = LSB(reg_addr); - - ret = i2c_transfer(i_params->i2c_adap, &i2cmsg, 1); - if (ret != 1) - dprintk(KERN_ERR "%s: i2c error %d\n", __func__, ret); - - i2cmsg.flags = I2C_M_RD; - i2cmsg.len = 1; - ret = i2c_transfer(i_params->i2c_adap, &i2cmsg, 1); - if (ret != 1) - dprintk(KERN_ERR "%s: i2c error %d\n", __func__, ret); + ret = i2c_transfer(i_params->i2c_adap, msg, 2); + if (ret != 2) + dprintk(KERN_ERR "%s: i2c error %d, reg[0x%02x]\n", + __func__, ret, reg); - return data[0]; + return buf; } void extract_mask_pos(u32 label, u8 *mask, u8 *pos) @@ -716,6 +716,44 @@ static s32 stv0900_carr_get_quality(struct dvb_frontend *fe, return c_n; } +static int stv0900_read_ucblocks(struct dvb_frontend *fe, u32 * ucblocks) +{ + struct stv0900_state *state = fe->demodulator_priv; + struct stv0900_internal *i_params = state->internal; + enum fe_stv0900_demod_num demod = state->demod; + u8 err_val1, err_val0; + s32 err_field1, err_field0; + u32 header_err_val = 0; + + *ucblocks = 0x0; + if (stv0900_get_standard(fe, demod) == STV0900_DVBS2_STANDARD) { + /* DVB-S2 delineator errors count */ + + /* retreiving number for errnous headers */ + dmd_reg(err_field0, R0900_P1_BBFCRCKO0, + R0900_P2_BBFCRCKO0); + dmd_reg(err_field1, R0900_P1_BBFCRCKO1, + R0900_P2_BBFCRCKO1); + + err_val1 = stv0900_read_reg(i_params, err_field1); + err_val0 = stv0900_read_reg(i_params, err_field0); + header_err_val = (err_val1<<8) | err_val0; + + /* retreiving number for errnous packets */ + dmd_reg(err_field0, R0900_P1_UPCRCKO0, + R0900_P2_UPCRCKO0); + dmd_reg(err_field1, R0900_P1_UPCRCKO1, + R0900_P2_UPCRCKO1); + + err_val1 = stv0900_read_reg(i_params, err_field1); + err_val0 = stv0900_read_reg(i_params, err_field0); + *ucblocks = (err_val1<<8) | err_val0; + *ucblocks += header_err_val; + } + + return 0; +} + static int stv0900_read_snr(struct dvb_frontend *fe, u16 *snr) { *snr = stv0900_carr_get_quality(fe, @@ -1359,7 +1397,7 @@ static enum fe_stv0900_error stv0900_init_internal(struct dvb_frontend *fe, struct stv0900_state *state = fe->demodulator_priv; enum fe_stv0900_error error = STV0900_NO_ERROR; enum fe_stv0900_error demodError = STV0900_NO_ERROR; - int selosci; + int selosci, i; struct stv0900_inode *temp_int = find_inode(state->i2c_adap, state->config->demod_address); @@ -1406,7 +1444,23 @@ static enum fe_stv0900_error stv0900_init_internal(struct dvb_frontend *fe, stv0900_write_bits(state->internal, F0900_P1_ROLLOFF_CONTROL, p_init->rolloff); stv0900_write_bits(state->internal, F0900_P2_ROLLOFF_CONTROL, p_init->rolloff); - stv0900_set_ts_parallel_serial(state->internal, p_init->path1_ts_clock, p_init->path2_ts_clock); + state->internal->ts_config = p_init->ts_config; + if (state->internal->ts_config == NULL) + stv0900_set_ts_parallel_serial(state->internal, + p_init->path1_ts_clock, + p_init->path2_ts_clock); + else { + for (i = 0; state->internal->ts_config[i].addr != 0xffff; i++) + stv0900_write_reg(state->internal, + state->internal->ts_config[i].addr, + state->internal->ts_config[i].val); + + stv0900_write_bits(state->internal, F0900_P2_RST_HWARE, 1); + stv0900_write_bits(state->internal, F0900_P2_RST_HWARE, 0); + stv0900_write_bits(state->internal, F0900_P1_RST_HWARE, 1); + stv0900_write_bits(state->internal, F0900_P1_RST_HWARE, 0); + } + stv0900_write_bits(state->internal, F0900_P1_TUN_MADDRESS, p_init->tun1_maddress); switch (p_init->tuner1_adc) { case 1: @@ -1897,6 +1951,7 @@ static struct dvb_frontend_ops stv0900_ops = { .read_ber = stv0900_read_ber, .read_signal_strength = stv0900_read_signal_strength, .read_snr = stv0900_read_snr, + .read_ucblocks = stv0900_read_ucblocks, }; struct dvb_frontend *stv0900_attach(const struct stv0900_config *config, @@ -1930,6 +1985,7 @@ struct dvb_frontend *stv0900_attach(const struct stv0900_config *config, init_params.tun1_iq_inversion = STV0900_IQ_NORMAL; init_params.tuner1_adc = config->tun1_adc; init_params.path2_ts_clock = config->path2_mode; + init_params.ts_config = config->ts_config_regs; init_params.tun2_maddress = config->tun2_maddress; init_params.tuner2_adc = config->tun2_adc; init_params.tun2_iq_inversion = STV0900_IQ_SWAPPED; diff --git a/linux/drivers/media/dvb/frontends/stv0900_priv.h b/linux/drivers/media/dvb/frontends/stv0900_priv.h index 67dc8ec63..5ed7a145c 100644 --- a/linux/drivers/media/dvb/frontends/stv0900_priv.h +++ b/linux/drivers/media/dvb/frontends/stv0900_priv.h @@ -271,6 +271,7 @@ struct stv0900_init_params{ /* IQ from the tuner2 to the demod */ enum stv0900_iq_inversion tun2_iq_inversion; + struct stv0900_reg *ts_config; }; struct stv0900_search_params { @@ -363,6 +364,7 @@ struct stv0900_internal{ u8 i2c_addr; u8 clkmode;/* 0 for CLKI, 2 for XTALI */ u8 chip_id; + struct stv0900_reg *ts_config; enum fe_stv0900_error errs; int dmds_used; }; diff --git a/linux/drivers/media/dvb/frontends/tda10048.c b/linux/drivers/media/dvb/frontends/tda10048.c index 04596baa9..edf5a19a3 100644 --- a/linux/drivers/media/dvb/frontends/tda10048.c +++ b/linux/drivers/media/dvb/frontends/tda10048.c @@ -210,6 +210,7 @@ static struct pll_tab { { TDA10048_CLK_4000, TDA10048_IF_36130, 10, 0, 0 }, { TDA10048_CLK_16000, TDA10048_IF_3300, 10, 3, 0 }, { TDA10048_CLK_16000, TDA10048_IF_3500, 10, 3, 0 }, + { TDA10048_CLK_16000, TDA10048_IF_3800, 10, 3, 0 }, { TDA10048_CLK_16000, TDA10048_IF_4000, 10, 3, 0 }, { TDA10048_CLK_16000, TDA10048_IF_4300, 10, 3, 0 }, { TDA10048_CLK_16000, TDA10048_IF_36130, 10, 3, 0 }, diff --git a/linux/drivers/media/dvb/siano/smscoreapi.c b/linux/drivers/media/dvb/siano/smscoreapi.c index e513408fc..54f59f24e 100644 --- a/linux/drivers/media/dvb/siano/smscoreapi.c +++ b/linux/drivers/media/dvb/siano/smscoreapi.c @@ -1477,8 +1477,8 @@ int smscore_gpio_configure(struct smscore_device_t *coredev, u8 PinNum, struct smscore_gpio_config *pGpioConfig) { u32 totalLen; - u32 TranslatedPinNum; - u32 GroupNum; + u32 TranslatedPinNum = 0; + u32 GroupNum = 0; u32 ElectricChar; u32 groupCfg; void *buffer; diff --git a/linux/drivers/media/radio/radio-tea5764.c b/linux/drivers/media/radio/radio-tea5764.c index 9194a70aa..83a5aff7b 100644 --- a/linux/drivers/media/radio/radio-tea5764.c +++ b/linux/drivers/media/radio/radio-tea5764.c @@ -326,7 +326,9 @@ static int vidioc_g_tuner(struct file *file, void *priv, v->rangehigh = FREQ_MAX * FREQ_MUL; v->capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO; if (r->tunchk & TEA5764_TUNCHK_STEREO) - v->rxsubchans = V4L2_TUNER_SUB_STEREO; + v->rxsubchans = V4L2_TUNER_SUB_STEREO; + else + v->rxsubchans = V4L2_TUNER_SUB_MONO; v->audmode = tea5764_get_audout_mode(radio); v->signal = TEA5764_TUNCHK_LEVEL(r->tunchk) * 0xffff / 0xf; v->afc = TEA5764_TUNCHK_IFCNT(r->tunchk); diff --git a/linux/drivers/media/video/Kconfig b/linux/drivers/media/video/Kconfig index 94f440535..061e147f6 100644 --- a/linux/drivers/media/video/Kconfig +++ b/linux/drivers/media/video/Kconfig @@ -866,9 +866,13 @@ config USB_W9968CF module will be called w9968cf. config USB_OV511 - tristate "USB OV511 Camera support" + tristate "USB OV511 Camera support (DEPRECATED)" depends on VIDEO_V4L1 ---help--- + This driver is DEPRECATED please use the gspca ov519 module + instead. Note that for the ov511 / ov518 support of the gspca module + you need atleast version 0.6.0 of libv4l. + Say Y here if you want to connect this type of camera to your computer's USB port. See <file:Documentation/video4linux/ov511.txt> for more information and for a list of supported cameras. diff --git a/linux/drivers/media/video/cx18/cx18-controls.c b/linux/drivers/media/video/cx18/cx18-controls.c index 8e35c3aed..5136df198 100644 --- a/linux/drivers/media/video/cx18/cx18-controls.c +++ b/linux/drivers/media/video/cx18/cx18-controls.c @@ -61,6 +61,8 @@ int cx18_queryctrl(struct file *file, void *fh, struct v4l2_queryctrl *qctrl) switch (qctrl->id) { /* Standard V4L2 controls */ + case V4L2_CID_USER_CLASS: + return v4l2_ctrl_query_fill(qctrl, 0, 0, 0, 0); case V4L2_CID_BRIGHTNESS: case V4L2_CID_HUE: case V4L2_CID_SATURATION: diff --git a/linux/drivers/media/video/cx231xx/cx231xx-avcore.c b/linux/drivers/media/video/cx231xx/cx231xx-avcore.c index bbbb3f50e..28f48f41f 100644 --- a/linux/drivers/media/video/cx231xx/cx231xx-avcore.c +++ b/linux/drivers/media/video/cx231xx/cx231xx-avcore.c @@ -2046,7 +2046,7 @@ int cx231xx_initialize_stream_xfer(struct cx231xx *dev, u32 media_type) int cx231xx_capture_start(struct cx231xx *dev, int start, u8 media_type) { - int rc; + int rc = -1; u32 ep_mask = -1; struct pcb_config *pcb_config; diff --git a/linux/drivers/media/video/cx2341x.c b/linux/drivers/media/video/cx2341x.c index 2cf0c5fee..8c468fc86 100644 --- a/linux/drivers/media/video/cx2341x.c +++ b/linux/drivers/media/video/cx2341x.c @@ -501,6 +501,8 @@ int cx2341x_ctrl_query(const struct cx2341x_mpeg_params *params, int err; switch (qctrl->id) { + case V4L2_CID_MPEG_CLASS: + return v4l2_ctrl_query_fill(qctrl, 0, 0, 0, 0); case V4L2_CID_MPEG_STREAM_TYPE: return v4l2_ctrl_query_fill(qctrl, V4L2_MPEG_STREAM_TYPE_MPEG2_PS, diff --git a/linux/drivers/media/video/cx23885/cx23885-dvb.c b/linux/drivers/media/video/cx23885/cx23885-dvb.c index 9c3259b22..71f196ec5 100644 --- a/linux/drivers/media/video/cx23885/cx23885-dvb.c +++ b/linux/drivers/media/video/cx23885/cx23885-dvb.c @@ -46,6 +46,7 @@ #include "dibx000_common.h" #include "zl10353.h" #include "stv0900.h" +#include "stv0900_reg.h" #include "stv6110.h" #include "lnbh24.h" #include "cx24116.h" @@ -243,12 +244,22 @@ static struct tda18271_std_map hauppauge_tda18271_std_map = { .if_lvl = 6, .rfagc_top = 0x37 }, }; +static struct tda18271_std_map hauppauge_hvr1200_tda18271_std_map = { + .dvbt_6 = { .if_freq = 3300, .agc_mode = 3, .std = 4, + .if_lvl = 1, .rfagc_top = 0x37, }, + .dvbt_7 = { .if_freq = 3800, .agc_mode = 3, .std = 5, + .if_lvl = 1, .rfagc_top = 0x37, }, + .dvbt_8 = { .if_freq = 4300, .agc_mode = 3, .std = 6, + .if_lvl = 1, .rfagc_top = 0x37, }, +}; + static struct tda18271_config hauppauge_tda18271_config = { .std_map = &hauppauge_tda18271_std_map, .gate = TDA18271_GATE_ANALOG, }; static struct tda18271_config hauppauge_hvr1200_tuner_config = { + .std_map = &hauppauge_hvr1200_tda18271_std_map, .gate = TDA18271_GATE_ANALOG, }; @@ -371,13 +382,25 @@ static struct zl10353_config dvico_fusionhdtv_xc3028 = { .disable_i2c_gate_ctrl = 1, }; +static struct stv0900_reg stv0900_ts_regs[] = { + { R0900_TSGENERAL, 0x00 }, + { R0900_P1_TSSPEED, 0x40 }, + { R0900_P2_TSSPEED, 0x40 }, + { R0900_P1_TSCFGM, 0xc0 }, + { R0900_P2_TSCFGM, 0xc0 }, + { R0900_P1_TSCFGH, 0xe0 }, + { R0900_P2_TSCFGH, 0xe0 }, + { R0900_P1_TSCFGL, 0x20 }, + { R0900_P2_TSCFGL, 0x20 }, + { 0xffff, 0xff }, /* terminate */ +}; + static struct stv0900_config netup_stv0900_config = { .demod_address = 0x68, .xtal = 27000000, .clkmode = 3,/* 0-CLKI, 2-XTALI, else AUTO */ .diseqc_mode = 2,/* 2/3 PWM */ - .path1_mode = 2,/*Serial continues clock */ - .path2_mode = 2,/*Serial continues clock */ + .ts_config_regs = stv0900_ts_regs, .tun1_maddress = 0,/* 0x60 */ .tun2_maddress = 3,/* 0x63 */ .tun1_adc = 1,/* 1 Vpp */ @@ -737,7 +760,8 @@ static int dvb_register(struct cx23885_tsport *port) if (!dvb_attach(lnbh24_attach, fe0->dvb.frontend, &i2c_bus->i2c_adap, - LNBH24_PCL, 0, 0x09)) + LNBH24_PCL, + LNBH24_TTX, 0x09)) printk(KERN_ERR "No LNBH24 found!\n"); @@ -757,7 +781,8 @@ static int dvb_register(struct cx23885_tsport *port) if (!dvb_attach(lnbh24_attach, fe0->dvb.frontend, &i2c_bus->i2c_adap, - LNBH24_PCL, 0, 0x0a)) + LNBH24_PCL, + LNBH24_TTX, 0x0a)) printk(KERN_ERR "No LNBH24 found!\n"); diff --git a/linux/drivers/media/video/em28xx/em28xx-cards.c b/linux/drivers/media/video/em28xx/em28xx-cards.c index 16093680b..b824eed46 100644 --- a/linux/drivers/media/video/em28xx/em28xx-cards.c +++ b/linux/drivers/media/video/em28xx/em28xx-cards.c @@ -152,6 +152,24 @@ static struct em28xx_reg_seq kworld_330u_digital[] = { { -1, -1, -1, -1}, }; +/* Evga inDtube + GPIO0 - Enable digital power (s5h1409) - low to enable + GPIO1 - Enable analog power (tvp5150/emp202) - low to enable + GPIO4 - xc3028 reset + GOP3 - s5h1409 reset + */ +static struct em28xx_reg_seq evga_indtube_analog[] = { + {EM28XX_R08_GPIO, 0x79, 0xff, 60}, + { -1, -1, -1, -1}, +}; + +static struct em28xx_reg_seq evga_indtube_digital[] = { + {EM28XX_R08_GPIO, 0x7a, 0xff, 1}, + {EM2880_R04_GPO, 0x04, 0xff, 10}, + {EM2880_R04_GPO, 0x0c, 0xff, 1}, + { -1, -1, -1, -1}, +}; + /* Callback for the most boards */ static struct em28xx_reg_seq default_tuner_gpio[] = { {EM28XX_R08_GPIO, EM_GPIO_4, EM_GPIO_4, 10}, @@ -1514,6 +1532,33 @@ struct em28xx_board em28xx_boards[] = { .gpio = terratec_av350_unmute_gpio, } }, }, + [EM2882_BOARD_EVGA_INDTUBE] = { + .name = "Evga inDtube", + .tuner_type = TUNER_XC2028, + .tuner_gpio = default_tuner_gpio, + .decoder = EM28XX_TVP5150, + .xclk = EM28XX_XCLK_FREQUENCY_12MHZ, /* NEC IR */ + .mts_firmware = 1, + .has_dvb = 1, + .dvb_gpio = evga_indtube_digital, + .ir_codes = ir_codes_evga_indtube, + .input = { { + .type = EM28XX_VMUX_TELEVISION, + .vmux = TVP5150_COMPOSITE0, + .amux = EM28XX_AMUX_VIDEO, + .gpio = evga_indtube_analog, + }, { + .type = EM28XX_VMUX_COMPOSITE1, + .vmux = TVP5150_COMPOSITE1, + .amux = EM28XX_AMUX_LINE_IN, + .gpio = evga_indtube_analog, + }, { + .type = EM28XX_VMUX_SVIDEO, + .vmux = TVP5150_SVIDEO, + .amux = EM28XX_AMUX_LINE_IN, + .gpio = evga_indtube_analog, + } }, + }, }; const unsigned int em28xx_bcount = ARRAY_SIZE(em28xx_boards); @@ -1640,6 +1685,7 @@ static struct em28xx_hash_table em28xx_eeprom_hash[] = { {0x72cc5a8b, EM2820_BOARD_PROLINK_PLAYTV_BOX4_USB2, TUNER_YMEC_TVF_5533MF}, {0x966a0441, EM2880_BOARD_KWORLD_DVB_310U, TUNER_XC2028}, {0x9567eb1a, EM2880_BOARD_EMPIRE_DUAL_TV, TUNER_XC2028}, + {0xcee44a99, EM2882_BOARD_EVGA_INDTUBE, TUNER_XC2028}, }; /* I2C devicelist hash table for devices with generic USB IDs */ @@ -1903,6 +1949,10 @@ static void em28xx_setup_xc3028(struct em28xx *dev, struct xc2028_ctrl *ctl) ctl->demod = XC3028_FE_CHINA; ctl->fname = XC2028_DEFAULT_FIRMWARE; break; + case EM2882_BOARD_EVGA_INDTUBE: + ctl->demod = XC3028_FE_CHINA; + ctl->fname = XC3028L_DEFAULT_FIRMWARE; + break; default: ctl->demod = XC3028_FE_OREN538; } @@ -2204,6 +2254,12 @@ void em28xx_card_setup(struct em28xx *dev) case EM2880_BOARD_MSI_DIGIVOX_AD: if (!em28xx_hint_board(dev)) em28xx_set_model(dev); + + /* In cases where we had to use a board hint, the call to + em28xx_set_mode() in em28xx_pre_card_setup() was a no-op, + so make the call now so the analog GPIOs are set properly + before probing the i2c bus. */ + em28xx_set_mode(dev, EM28XX_ANALOG_MODE); break; } diff --git a/linux/drivers/media/video/em28xx/em28xx-dvb.c b/linux/drivers/media/video/em28xx/em28xx-dvb.c index 722cc709f..85afa0be0 100644 --- a/linux/drivers/media/video/em28xx/em28xx-dvb.c +++ b/linux/drivers/media/video/em28xx/em28xx-dvb.c @@ -446,6 +446,7 @@ static int dvb_init(struct em28xx *dev) } break; case EM2883_BOARD_KWORLD_HYBRID_330U: + case EM2882_BOARD_EVGA_INDTUBE: dvb->frontend = dvb_attach(s5h1409_attach, &em28xx_s5h1409_with_xc3028, &dev->i2c_adap); diff --git a/linux/drivers/media/video/em28xx/em28xx.h b/linux/drivers/media/video/em28xx/em28xx.h index aafc4a2d0..6fb890efa 100644 --- a/linux/drivers/media/video/em28xx/em28xx.h +++ b/linux/drivers/media/video/em28xx/em28xx.h @@ -107,6 +107,7 @@ #define EM2860_BOARD_TERRATEC_GRABBY 67 #define EM2860_BOARD_TERRATEC_AV350 68 #define EM2882_BOARD_KWORLD_ATSC_315U 69 +#define EM2882_BOARD_EVGA_INDTUBE 70 /* Limits minimum and default number of buffers */ #define EM28XX_MIN_BUF 4 diff --git a/linux/drivers/media/video/gspca/gspca.c b/linux/drivers/media/video/gspca/gspca.c index e6ab4bb8c..f06aac676 100644 --- a/linux/drivers/media/video/gspca/gspca.c +++ b/linux/drivers/media/video/gspca/gspca.c @@ -1054,13 +1054,11 @@ static int vidioc_queryctrl(struct file *file, void *priv, for (i = 0; i < gspca_dev->sd_desc->nctrls; i++) { if (gspca_dev->ctrl_dis & (1 << i)) continue; - if (ctrls->qctrl.id < id) + if (gspca_dev->sd_desc->ctrls[i].qctrl.id < id) continue; - if (ctrls != NULL) { - if (gspca_dev->sd_desc->ctrls[i].qctrl.id + if (ctrls && gspca_dev->sd_desc->ctrls[i].qctrl.id > ctrls->qctrl.id) - continue; - } + continue; ctrls = &gspca_dev->sd_desc->ctrls[i]; } } else { diff --git a/linux/drivers/media/video/gspca/ov519.c b/linux/drivers/media/video/gspca/ov519.c index 2bd42f4e0..cde3a0177 100644 --- a/linux/drivers/media/video/gspca/ov519.c +++ b/linux/drivers/media/video/gspca/ov519.c @@ -50,12 +50,18 @@ static int i2c_detect_tries = 10; struct sd { struct gspca_dev gspca_dev; /* !! must be the first item */ + __u8 packet_nr; + char bridge; #define BRIDGE_OV511 0 #define BRIDGE_OV511PLUS 1 #define BRIDGE_OV518 2 #define BRIDGE_OV518PLUS 3 #define BRIDGE_OV519 4 +#define BRIDGE_MASK 7 + + char invert_led; +#define BRIDGE_INVERT_LED 8 /* Determined by sensor type */ __u8 sif; @@ -65,22 +71,25 @@ struct sd { __u8 colors; __u8 hflip; __u8 vflip; + __u8 autobrightness; + __u8 freq; __u8 stopped; /* Streaming is temporarily paused */ - __u8 frame_rate; /* current Framerate (OV519 only) */ - __u8 clockdiv; /* clockdiv override for OV519 only */ + __u8 frame_rate; /* current Framerate */ + __u8 clockdiv; /* clockdiv override */ char sensor; /* Type of image sensor chip (SEN_*) */ #define SEN_UNKNOWN 0 #define SEN_OV6620 1 #define SEN_OV6630 2 -#define SEN_OV7610 3 -#define SEN_OV7620 4 -#define SEN_OV7640 5 -#define SEN_OV7670 6 -#define SEN_OV76BE 7 -#define SEN_OV8610 8 +#define SEN_OV66308AF 3 +#define SEN_OV7610 4 +#define SEN_OV7620 5 +#define SEN_OV7640 6 +#define SEN_OV7670 7 +#define SEN_OV76BE 8 +#define SEN_OV8610 9 }; /* V4L2 controls supported by the driver */ @@ -94,11 +103,17 @@ static int sd_sethflip(struct gspca_dev *gspca_dev, __s32 val); static int sd_gethflip(struct gspca_dev *gspca_dev, __s32 *val); static int sd_setvflip(struct gspca_dev *gspca_dev, __s32 val); static int sd_getvflip(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setautobrightness(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getautobrightness(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setfreq(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getfreq(struct gspca_dev *gspca_dev, __s32 *val); static void setbrightness(struct gspca_dev *gspca_dev); static void setcontrast(struct gspca_dev *gspca_dev); static void setcolors(struct gspca_dev *gspca_dev); +static void setautobrightness(struct sd *sd); +static void setfreq(struct sd *sd); -static struct ctrl sd_ctrls[] = { +static const struct ctrl sd_ctrls[] = { { { .id = V4L2_CID_BRIGHTNESS, @@ -141,7 +156,7 @@ static struct ctrl sd_ctrls[] = { .set = sd_setcolors, .get = sd_getcolors, }, -/* next controls work with ov7670 only */ +/* The flip controls work with ov7670 only */ #define HFLIP_IDX 3 { { @@ -172,6 +187,51 @@ static struct ctrl sd_ctrls[] = { .set = sd_setvflip, .get = sd_getvflip, }, +#define AUTOBRIGHT_IDX 5 + { + { + .id = V4L2_CID_AUTOBRIGHTNESS, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Auto Brightness", + .minimum = 0, + .maximum = 1, + .step = 1, +#define AUTOBRIGHT_DEF 1 + .default_value = AUTOBRIGHT_DEF, + }, + .set = sd_setautobrightness, + .get = sd_getautobrightness, + }, +#define FREQ_IDX 6 + { + { + .id = V4L2_CID_POWER_LINE_FREQUENCY, + .type = V4L2_CTRL_TYPE_MENU, + .name = "Light frequency filter", + .minimum = 0, + .maximum = 2, /* 0: 0, 1: 50Hz, 2:60Hz */ + .step = 1, +#define FREQ_DEF 0 + .default_value = FREQ_DEF, + }, + .set = sd_setfreq, + .get = sd_getfreq, + }, +#define OV7670_FREQ_IDX 7 + { + { + .id = V4L2_CID_POWER_LINE_FREQUENCY, + .type = V4L2_CTRL_TYPE_MENU, + .name = "Light frequency filter", + .minimum = 0, + .maximum = 3, /* 0: 0, 1: 50Hz, 2:60Hz 3: Auto Hz */ + .step = 1, +#define OV7670_FREQ_DEF 3 + .default_value = OV7670_FREQ_DEF, + }, + .set = sd_setfreq, + .get = sd_getfreq, + }, }; static const struct v4l2_pix_format ov519_vga_mode[] = { @@ -187,11 +247,21 @@ static const struct v4l2_pix_format ov519_vga_mode[] = { .priv = 0}, }; static const struct v4l2_pix_format ov519_sif_mode[] = { + {160, 120, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 160, + .sizeimage = 160 * 120 * 3 / 8 + 590, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 3}, {176, 144, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, .bytesperline = 176, .sizeimage = 176 * 144 * 3 / 8 + 590, .colorspace = V4L2_COLORSPACE_JPEG, .priv = 1}, + {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 320, + .sizeimage = 320 * 240 * 3 / 8 + 590, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 2}, {352, 288, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, .bytesperline = 352, .sizeimage = 352 * 288 * 3 / 8 + 590, @@ -199,42 +269,118 @@ static const struct v4l2_pix_format ov519_sif_mode[] = { .priv = 0}, }; +/* Note some of the sizeimage values for the ov511 / ov518 may seem + larger then necessary, however they need to be this big as the ov511 / + ov518 always fills the entire isoc frame, using 0 padding bytes when + it doesn't have any data. So with low framerates the amount of data + transfered can become quite large (libv4l will remove all the 0 padding + in userspace). */ static const struct v4l2_pix_format ov518_vga_mode[] = { {320, 240, V4L2_PIX_FMT_OV518, V4L2_FIELD_NONE, .bytesperline = 320, - .sizeimage = 320 * 240 * 3 / 8 + 590, + .sizeimage = 320 * 240 * 3, .colorspace = V4L2_COLORSPACE_JPEG, .priv = 1}, {640, 480, V4L2_PIX_FMT_OV518, V4L2_FIELD_NONE, .bytesperline = 640, - .sizeimage = 640 * 480 * 3 / 8 + 590, + .sizeimage = 640 * 480 * 2, .colorspace = V4L2_COLORSPACE_JPEG, .priv = 0}, }; static const struct v4l2_pix_format ov518_sif_mode[] = { + {160, 120, V4L2_PIX_FMT_OV518, V4L2_FIELD_NONE, + .bytesperline = 160, + .sizeimage = 70000, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 3}, {176, 144, V4L2_PIX_FMT_OV518, V4L2_FIELD_NONE, .bytesperline = 176, - .sizeimage = 40000, + .sizeimage = 70000, .colorspace = V4L2_COLORSPACE_JPEG, .priv = 1}, + {320, 240, V4L2_PIX_FMT_OV518, V4L2_FIELD_NONE, + .bytesperline = 320, + .sizeimage = 320 * 240 * 3, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 2}, {352, 288, V4L2_PIX_FMT_OV518, V4L2_FIELD_NONE, .bytesperline = 352, - .sizeimage = 352 * 288 * 3 / 8 + 590, + .sizeimage = 352 * 288 * 3, .colorspace = V4L2_COLORSPACE_JPEG, .priv = 0}, }; +static const struct v4l2_pix_format ov511_vga_mode[] = { + {320, 240, V4L2_PIX_FMT_OV511, V4L2_FIELD_NONE, + .bytesperline = 320, + .sizeimage = 320 * 240 * 3, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 1}, + {640, 480, V4L2_PIX_FMT_OV511, V4L2_FIELD_NONE, + .bytesperline = 640, + .sizeimage = 640 * 480 * 2, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 0}, +}; +static const struct v4l2_pix_format ov511_sif_mode[] = { + {160, 120, V4L2_PIX_FMT_OV511, V4L2_FIELD_NONE, + .bytesperline = 160, + .sizeimage = 70000, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 3}, + {176, 144, V4L2_PIX_FMT_OV511, V4L2_FIELD_NONE, + .bytesperline = 176, + .sizeimage = 70000, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 1}, + {320, 240, V4L2_PIX_FMT_OV511, V4L2_FIELD_NONE, + .bytesperline = 320, + .sizeimage = 320 * 240 * 3, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 2}, + {352, 288, V4L2_PIX_FMT_OV511, V4L2_FIELD_NONE, + .bytesperline = 352, + .sizeimage = 352 * 288 * 3, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 0}, +}; /* Registers common to OV511 / OV518 */ +#define R51x_FIFO_PSIZE 0x30 /* 2 bytes wide w/ OV518(+) */ #define R51x_SYS_RESET 0x50 + /* Reset type flags */ + #define OV511_RESET_OMNICE 0x08 #define R51x_SYS_INIT 0x53 #define R51x_SYS_SNAP 0x52 #define R51x_SYS_CUST_ID 0x5F #define R51x_COMP_LUT_BEGIN 0x80 /* OV511 Camera interface register numbers */ +#define R511_CAM_DELAY 0x10 +#define R511_CAM_EDGE 0x11 +#define R511_CAM_PXCNT 0x12 +#define R511_CAM_LNCNT 0x13 +#define R511_CAM_PXDIV 0x14 +#define R511_CAM_LNDIV 0x15 +#define R511_CAM_UV_EN 0x16 +#define R511_CAM_LINE_MODE 0x17 +#define R511_CAM_OPTS 0x18 + +#define R511_SNAP_FRAME 0x19 +#define R511_SNAP_PXCNT 0x1A +#define R511_SNAP_LNCNT 0x1B +#define R511_SNAP_PXDIV 0x1C +#define R511_SNAP_LNDIV 0x1D +#define R511_SNAP_UV_EN 0x1E +#define R511_SNAP_UV_EN 0x1E +#define R511_SNAP_OPTS 0x1F + +#define R511_DRAM_FLOW_CTL 0x20 +#define R511_FIFO_OPTS 0x31 +#define R511_I2C_CTL 0x40 #define R511_SYS_LED_CTL 0x55 /* OV511+ only */ -#define OV511_RESET_NOREGS 0x3F /* All but OV511 & regs */ +#define R511_COMP_EN 0x78 +#define R511_COMP_LUT_EN 0x79 /* OV518 Camera interface register numbers */ #define R518_GPIO_OUT 0x56 /* OV518(+) only */ @@ -383,7 +529,7 @@ static const struct ov_i2c_regvals norm_6x20[] = { { 0x28, 0x05 }, { 0x2a, 0x04 }, /* Disable framerate adjust */ /* { 0x2b, 0xac }, * Framerate; Set 2a[7] first */ - { 0x2d, 0x99 }, + { 0x2d, 0x85 }, { 0x33, 0xa0 }, /* Color Processing Parameter */ { 0x34, 0xd2 }, /* Max A/D range */ { 0x38, 0x8b }, @@ -416,7 +562,7 @@ static const struct ov_i2c_regvals norm_6x30[] = { { 0x07, 0x2d }, /* Sharpness */ { 0x0c, 0x20 }, { 0x0d, 0x20 }, - { 0x0e, 0x20 }, + { 0x0e, 0xa0 }, /* Was 0x20, bit7 enables a 2x gain which we need */ { 0x0f, 0x05 }, { 0x10, 0x9a }, { 0x11, 0x00 }, /* Pixel clock = fastest */ @@ -558,7 +704,7 @@ static const struct ov_i2c_regvals norm_7620[] = { { 0x23, 0x00 }, { 0x26, 0xa2 }, { 0x27, 0xea }, - { 0x28, 0x20 }, + { 0x28, 0x22 }, /* Was 0x20, bit1 enables a 2x gain which we need */ { 0x29, 0x00 }, { 0x2a, 0x10 }, { 0x2b, 0x00 }, @@ -999,13 +1145,132 @@ static int ov518_reg_w32(struct sd *sd, __u16 index, u32 value, int n) return ret; } +static int ov511_i2c_w(struct sd *sd, __u8 reg, __u8 value) +{ + int rc, retries; + + PDEBUG(D_USBO, "i2c 0x%02x -> [0x%02x]", value, reg); + + /* Three byte write cycle */ + for (retries = 6; ; ) { + /* Select camera register */ + rc = reg_w(sd, R51x_I2C_SADDR_3, reg); + if (rc < 0) + return rc; + + /* Write "value" to I2C data port of OV511 */ + rc = reg_w(sd, R51x_I2C_DATA, value); + if (rc < 0) + return rc; + + /* Initiate 3-byte write cycle */ + rc = reg_w(sd, R511_I2C_CTL, 0x01); + if (rc < 0) + return rc; + + do + rc = reg_r(sd, R511_I2C_CTL); + while (rc > 0 && ((rc & 1) == 0)); /* Retry until idle */ + + if (rc < 0) + return rc; + + if ((rc & 2) == 0) /* Ack? */ + break; +#if 0 + /* I2C abort */ + reg_w(sd, R511_I2C_CTL, 0x10); +#endif + if (--retries < 0) { + PDEBUG(D_USBO, "i2c write retries exhausted"); + return -1; + } + } + + return 0; +} + +static int ov511_i2c_r(struct sd *sd, __u8 reg) +{ + int rc, value, retries; + + /* Two byte write cycle */ + for (retries = 6; ; ) { + /* Select camera register */ + rc = reg_w(sd, R51x_I2C_SADDR_2, reg); + if (rc < 0) + return rc; + + /* Initiate 2-byte write cycle */ + rc = reg_w(sd, R511_I2C_CTL, 0x03); + if (rc < 0) + return rc; + + do + rc = reg_r(sd, R511_I2C_CTL); + while (rc > 0 && ((rc & 1) == 0)); /* Retry until idle */ + + if (rc < 0) + return rc; + + if ((rc & 2) == 0) /* Ack? */ + break; + + /* I2C abort */ + reg_w(sd, R511_I2C_CTL, 0x10); + + if (--retries < 0) { + PDEBUG(D_USBI, "i2c write retries exhausted"); + return -1; + } + } + + /* Two byte read cycle */ + for (retries = 6; ; ) { + /* Initiate 2-byte read cycle */ + rc = reg_w(sd, R511_I2C_CTL, 0x05); + if (rc < 0) + return rc; + + do + rc = reg_r(sd, R511_I2C_CTL); + while (rc > 0 && ((rc & 1) == 0)); /* Retry until idle */ + + if (rc < 0) + return rc; + + if ((rc & 2) == 0) /* Ack? */ + break; + + /* I2C abort */ + rc = reg_w(sd, R511_I2C_CTL, 0x10); + if (rc < 0) + return rc; + + if (--retries < 0) { + PDEBUG(D_USBI, "i2c read retries exhausted"); + return -1; + } + } + + value = reg_r(sd, R51x_I2C_DATA); + + PDEBUG(D_USBI, "i2c [0x%02X] -> 0x%02X", reg, value); + + /* This is needed to make i2c_w() work */ + rc = reg_w(sd, R511_I2C_CTL, 0x05); + if (rc < 0) + return rc; + + return value; +} /* * The OV518 I2C I/O procedure is different, hence, this function. * This is normally only called from i2c_w(). Note that this function * always succeeds regardless of whether the sensor is present and working. */ -static int i2c_w(struct sd *sd, +static int ov518_i2c_w(struct sd *sd, __u8 reg, __u8 value) { @@ -1040,7 +1305,7 @@ static int i2c_w(struct sd *sd, * This is normally only called from i2c_r(). Note that this function * always succeeds regardless of whether the sensor is present and working. */ -static int i2c_r(struct sd *sd, __u8 reg) +static int ov518_i2c_r(struct sd *sd, __u8 reg) { int rc, value; @@ -1063,6 +1328,34 @@ static int i2c_r(struct sd *sd, __u8 reg) return value; } +static int i2c_w(struct sd *sd, __u8 reg, __u8 value) +{ + switch (sd->bridge) { + case BRIDGE_OV511: + case BRIDGE_OV511PLUS: + return ov511_i2c_w(sd, reg, value); + case BRIDGE_OV518: + case BRIDGE_OV518PLUS: + case BRIDGE_OV519: + return ov518_i2c_w(sd, reg, value); + } + return -1; /* Should never happen */ +} + +static int i2c_r(struct sd *sd, __u8 reg) +{ + switch (sd->bridge) { + case BRIDGE_OV511: + case BRIDGE_OV511PLUS: + return ov511_i2c_r(sd, reg); + case BRIDGE_OV518: + case BRIDGE_OV518PLUS: + case BRIDGE_OV519: + return ov518_i2c_r(sd, reg); + } + return -1; /* Should never happen */ +} + /* Writes bits at positions specified by mask to an I2C reg. Bits that are in * the same position as 1's in "mask" are cleared and set to "value". Bits * that are in the same position as 0's in "mask" are preserved, regardless @@ -1242,7 +1535,6 @@ static int ov8xx0_configure(struct sd *sd) } /* Set sensor-specific vars */ -/* sd->sif = 0; already done */ return 0; } @@ -1279,15 +1571,13 @@ static int ov7xx0_configure(struct sd *sd) } } else if ((rc & 3) == 1) { /* I don't know what's different about the 76BE yet. */ - if (i2c_r(sd, 0x15) & 1) + if (i2c_r(sd, 0x15) & 1) { PDEBUG(D_PROBE, "Sensor is an OV7620AE"); - else + sd->sensor = SEN_OV7620; + } else { PDEBUG(D_PROBE, "Sensor is an OV76BE"); - - /* OV511+ will return all zero isoc data unless we - * configure the sensor as a 7620. Someone needs to - * find the exact reg. setting that causes this. */ - sd->sensor = SEN_OV76BE; + sd->sensor = SEN_OV76BE; + } } else if ((rc & 3) == 0) { /* try to read product id registers */ high = i2c_r(sd, 0x0a); @@ -1333,7 +1623,6 @@ static int ov7xx0_configure(struct sd *sd) } /* Set sensor-specific vars */ -/* sd->sif = 0; already done */ return 0; } @@ -1362,13 +1651,14 @@ static int ov6xx0_configure(struct sd *sd) break; case 0x01: sd->sensor = SEN_OV6620; + PDEBUG(D_PROBE, "Sensor is an OV6620"); break; case 0x02: sd->sensor = SEN_OV6630; PDEBUG(D_PROBE, "Sensor is an OV66308AE"); break; case 0x03: - sd->sensor = SEN_OV6630; + sd->sensor = SEN_OV66308AF; PDEBUG(D_PROBE, "Sensor is an OV66308AF"); break; case 0x90: @@ -1391,6 +1681,9 @@ static int ov6xx0_configure(struct sd *sd) /* Turns on or off the LED. Only has an effect with OV511+/OV518(+)/OV519 */ static void ov51x_led_control(struct sd *sd, int on) { + if (sd->invert_led) + on = !on; + switch (sd->bridge) { /* OV511 has no LED control */ case BRIDGE_OV511PLUS: @@ -1406,9 +1699,31 @@ static void ov51x_led_control(struct sd *sd, int on) } } -/* OV518 quantization tables are 8x4 (instead of 8x8) */ -static int ov518_upload_quan_tables(struct sd *sd) +static int ov51x_upload_quan_tables(struct sd *sd) { + const unsigned char yQuanTable511[] = { + 0, 1, 1, 2, 2, 3, 3, 4, + 1, 1, 1, 2, 2, 3, 4, 4, + 1, 1, 2, 2, 3, 4, 4, 4, + 2, 2, 2, 3, 4, 4, 4, 4, + 2, 2, 3, 4, 4, 5, 5, 5, + 3, 3, 4, 4, 5, 5, 5, 5, + 3, 4, 4, 4, 5, 5, 5, 5, + 4, 4, 4, 4, 5, 5, 5, 5 + }; + + const unsigned char uvQuanTable511[] = { + 0, 2, 2, 3, 4, 4, 4, 4, + 2, 2, 2, 4, 4, 4, 4, 4, + 2, 2, 3, 4, 4, 4, 4, 4, + 3, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4 + }; + + /* OV518 quantization tables are 8x4 (instead of 8x8) */ const unsigned char yQuanTable518[] = { 5, 4, 5, 6, 6, 7, 7, 7, 5, 5, 5, 5, 6, 7, 7, 7, @@ -1423,14 +1738,23 @@ static int ov518_upload_quan_tables(struct sd *sd) 7, 7, 7, 7, 7, 7, 8, 8 }; - const unsigned char *pYTable = yQuanTable518; - const unsigned char *pUVTable = uvQuanTable518; + const unsigned char *pYTable, *pUVTable; unsigned char val0, val1; - int i, rc, reg = R51x_COMP_LUT_BEGIN; + int i, size, rc, reg = R51x_COMP_LUT_BEGIN; PDEBUG(D_PROBE, "Uploading quantization tables"); - for (i = 0; i < 16; i++) { + if (sd->bridge == BRIDGE_OV511 || sd->bridge == BRIDGE_OV511PLUS) { + pYTable = yQuanTable511; + pUVTable = uvQuanTable511; + size = 32; + } else { + pYTable = yQuanTable518; + pUVTable = uvQuanTable518; + size = 16; + } + + for (i = 0; i < size; i++) { val0 = *pYTable++; val1 = *pYTable++; val0 &= 0x0f; @@ -1445,7 +1769,7 @@ static int ov518_upload_quan_tables(struct sd *sd) val0 &= 0x0f; val1 &= 0x0f; val0 |= val1 << 4; - rc = reg_w(sd, reg + 16, val0); + rc = reg_w(sd, reg + size, val0); if (rc < 0) return rc; @@ -1455,6 +1779,87 @@ static int ov518_upload_quan_tables(struct sd *sd) return 0; } +/* This initializes the OV511/OV511+ and the sensor */ +static int ov511_configure(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int rc; + + /* For 511 and 511+ */ + const struct ov_regvals init_511[] = { + { R51x_SYS_RESET, 0x7f }, + { R51x_SYS_INIT, 0x01 }, + { R51x_SYS_RESET, 0x7f }, + { R51x_SYS_INIT, 0x01 }, + { R51x_SYS_RESET, 0x3f }, + { R51x_SYS_INIT, 0x01 }, + { R51x_SYS_RESET, 0x3d }, + }; + + const struct ov_regvals norm_511[] = { + { R511_DRAM_FLOW_CTL, 0x01 }, + { R51x_SYS_SNAP, 0x00 }, + { R51x_SYS_SNAP, 0x02 }, + { R51x_SYS_SNAP, 0x00 }, + { R511_FIFO_OPTS, 0x1f }, + { R511_COMP_EN, 0x00 }, + { R511_COMP_LUT_EN, 0x03 }, + }; + + const struct ov_regvals norm_511_p[] = { + { R511_DRAM_FLOW_CTL, 0xff }, + { R51x_SYS_SNAP, 0x00 }, + { R51x_SYS_SNAP, 0x02 }, + { R51x_SYS_SNAP, 0x00 }, + { R511_FIFO_OPTS, 0xff }, + { R511_COMP_EN, 0x00 }, + { R511_COMP_LUT_EN, 0x03 }, + }; + + const struct ov_regvals compress_511[] = { + { 0x70, 0x1f }, + { 0x71, 0x05 }, + { 0x72, 0x06 }, + { 0x73, 0x06 }, + { 0x74, 0x14 }, + { 0x75, 0x03 }, + { 0x76, 0x04 }, + { 0x77, 0x04 }, + }; + + PDEBUG(D_PROBE, "Device custom id %x", reg_r(sd, R51x_SYS_CUST_ID)); + + rc = write_regvals(sd, init_511, ARRAY_SIZE(init_511)); + if (rc < 0) + return rc; + + switch (sd->bridge) { + case BRIDGE_OV511: + rc = write_regvals(sd, norm_511, ARRAY_SIZE(norm_511)); + if (rc < 0) + return rc; + break; + case BRIDGE_OV511PLUS: + rc = write_regvals(sd, norm_511_p, ARRAY_SIZE(norm_511_p)); + if (rc < 0) + return rc; + break; + } + + /* Init compression */ + rc = write_regvals(sd, compress_511, ARRAY_SIZE(compress_511)); + if (rc < 0) + return rc; + + rc = ov51x_upload_quan_tables(sd); + if (rc < 0) { + PDEBUG(D_ERR, "Error uploading quantization tables"); + return rc; + } + + return 0; +} + /* This initializes the OV518/OV518+ and the sensor */ static int ov518_configure(struct gspca_dev *gspca_dev) { @@ -1462,7 +1867,7 @@ static int ov518_configure(struct gspca_dev *gspca_dev) int rc; /* For 518 and 518+ */ - static struct ov_regvals init_518[] = { + const struct ov_regvals init_518[] = { { R51x_SYS_RESET, 0x40 }, { R51x_SYS_INIT, 0xe1 }, { R51x_SYS_RESET, 0x3e }, @@ -1473,7 +1878,7 @@ static int ov518_configure(struct gspca_dev *gspca_dev) { 0x5d, 0x03 }, }; - static struct ov_regvals norm_518[] = { + const struct ov_regvals norm_518[] = { { R51x_SYS_SNAP, 0x02 }, /* Reset */ { R51x_SYS_SNAP, 0x01 }, /* Enable */ { 0x31, 0x0f }, @@ -1486,7 +1891,7 @@ static int ov518_configure(struct gspca_dev *gspca_dev) { 0x2f, 0x80 }, }; - static struct ov_regvals norm_518_p[] = { + const struct ov_regvals norm_518_p[] = { { R51x_SYS_SNAP, 0x02 }, /* Reset */ { R51x_SYS_SNAP, 0x01 }, /* Enable */ { 0x31, 0x0f }, @@ -1531,7 +1936,7 @@ static int ov518_configure(struct gspca_dev *gspca_dev) break; } - rc = ov518_upload_quan_tables(sd); + rc = ov51x_upload_quan_tables(sd); if (rc < 0) { PDEBUG(D_ERR, "Error uploading quantization tables"); return rc; @@ -1573,9 +1978,14 @@ static int sd_config(struct gspca_dev *gspca_dev, struct cam *cam; int ret = 0; - sd->bridge = id->driver_info; + sd->bridge = id->driver_info & BRIDGE_MASK; + sd->invert_led = id->driver_info & BRIDGE_INVERT_LED; switch (sd->bridge) { + case BRIDGE_OV511: + case BRIDGE_OV511PLUS: + ret = ov511_configure(gspca_dev); + break; case BRIDGE_OV518: case BRIDGE_OV518PLUS: ret = ov518_configure(gspca_dev); @@ -1634,6 +2044,16 @@ static int sd_config(struct gspca_dev *gspca_dev, cam = &gspca_dev->cam; switch (sd->bridge) { + case BRIDGE_OV511: + case BRIDGE_OV511PLUS: + if (!sd->sif) { + cam->cam_mode = ov511_vga_mode; + cam->nmodes = ARRAY_SIZE(ov511_vga_mode); + } else { + cam->cam_mode = ov511_sif_mode; + cam->nmodes = ARRAY_SIZE(ov511_sif_mode); + } + break; case BRIDGE_OV518: case BRIDGE_OV518PLUS: if (!sd->sif) { @@ -1655,13 +2075,28 @@ static int sd_config(struct gspca_dev *gspca_dev, break; } sd->brightness = BRIGHTNESS_DEF; - sd->contrast = CONTRAST_DEF; + if (sd->sensor == SEN_OV6630 || sd->sensor == SEN_OV66308AF) + sd->contrast = 200; /* The default is too low for the ov6630 */ + else + sd->contrast = CONTRAST_DEF; sd->colors = COLOR_DEF; sd->hflip = HFLIP_DEF; sd->vflip = VFLIP_DEF; - if (sd->sensor != SEN_OV7670) - gspca_dev->ctrl_dis = (1 << HFLIP_IDX) - | (1 << VFLIP_IDX); + sd->autobrightness = AUTOBRIGHT_DEF; + if (sd->sensor == SEN_OV7670) { + sd->freq = OV7670_FREQ_DEF; + gspca_dev->ctrl_dis = 1 << FREQ_IDX; + } else { + sd->freq = FREQ_DEF; + gspca_dev->ctrl_dis = (1 << HFLIP_IDX) | (1 << VFLIP_IDX) | + (1 << OV7670_FREQ_IDX); + } + if (sd->sensor == SEN_OV7640 || sd->sensor == SEN_OV7670) + gspca_dev->ctrl_dis |= 1 << AUTOBRIGHT_IDX; + /* OV8610 Frequency filter control should work but needs testing */ + if (sd->sensor == SEN_OV8610) + gspca_dev->ctrl_dis |= 1 << FREQ_IDX; + return 0; error: PDEBUG(D_ERR, "OV519 Config failed"); @@ -1680,6 +2115,7 @@ static int sd_init(struct gspca_dev *gspca_dev) return -EIO; break; case SEN_OV6630: + case SEN_OV66308AF: if (write_i2c_regvals(sd, norm_6x30, ARRAY_SIZE(norm_6x30))) return -EIO; break; @@ -1688,6 +2124,8 @@ static int sd_init(struct gspca_dev *gspca_dev) /* case SEN_OV76BE: */ if (write_i2c_regvals(sd, norm_7610, ARRAY_SIZE(norm_7610))) return -EIO; + if (i2c_w_mask(sd, 0x0e, 0x00, 0x40)) + return -EIO; break; case SEN_OV7620: if (write_i2c_regvals(sd, norm_7620, ARRAY_SIZE(norm_7620))) @@ -1709,6 +2147,126 @@ static int sd_init(struct gspca_dev *gspca_dev) return 0; } +/* Set up the OV511/OV511+ with the given image parameters. + * + * Do not put any sensor-specific code in here (including I2C I/O functions) + */ +static int ov511_mode_init_regs(struct sd *sd) +{ + int hsegs, vsegs, packet_size, fps, needed; + int interlaced = 0; + struct usb_host_interface *alt; + struct usb_interface *intf; + + intf = usb_ifnum_to_if(sd->gspca_dev.dev, sd->gspca_dev.iface); + alt = usb_altnum_to_altsetting(intf, sd->gspca_dev.alt); + if (!alt) { + PDEBUG(D_ERR, "Couldn't get altsetting"); + return -EIO; + } + + packet_size = le16_to_cpu(alt->endpoint[0].desc.wMaxPacketSize); + reg_w(sd, R51x_FIFO_PSIZE, packet_size >> 5); + + reg_w(sd, R511_CAM_UV_EN, 0x01); + reg_w(sd, R511_SNAP_UV_EN, 0x01); + reg_w(sd, R511_SNAP_OPTS, 0x03); + + /* Here I'm assuming that snapshot size == image size. + * I hope that's always true. --claudio + */ + hsegs = (sd->gspca_dev.width >> 3) - 1; + vsegs = (sd->gspca_dev.height >> 3) - 1; + + reg_w(sd, R511_CAM_PXCNT, hsegs); + reg_w(sd, R511_CAM_LNCNT, vsegs); + reg_w(sd, R511_CAM_PXDIV, 0x00); + reg_w(sd, R511_CAM_LNDIV, 0x00); + + /* YUV420, low pass filter on */ + reg_w(sd, R511_CAM_OPTS, 0x03); + + /* Snapshot additions */ + reg_w(sd, R511_SNAP_PXCNT, hsegs); + reg_w(sd, R511_SNAP_LNCNT, vsegs); + reg_w(sd, R511_SNAP_PXDIV, 0x00); + reg_w(sd, R511_SNAP_LNDIV, 0x00); + + /******** Set the framerate ********/ + if (frame_rate > 0) + sd->frame_rate = frame_rate; + + switch (sd->sensor) { + case SEN_OV6620: + /* No framerate control, doesn't like higher rates yet */ + sd->clockdiv = 3; + break; + + /* Note once the FIXME's in mode_init_ov_sensor_regs() are fixed + for more sensors we need to do this for them too */ + case SEN_OV7620: + case SEN_OV7640: + case SEN_OV76BE: + if (sd->gspca_dev.width == 320) + interlaced = 1; + /* Fall through */ + case SEN_OV6630: + case SEN_OV7610: + case SEN_OV7670: + switch (sd->frame_rate) { + case 30: + case 25: + /* Not enough bandwidth to do 640x480 @ 30 fps */ + if (sd->gspca_dev.width != 640) { + sd->clockdiv = 0; + break; + } + /* Fall through for 640x480 case */ + default: +/* case 20: */ +/* case 15: */ + sd->clockdiv = 1; + break; + case 10: + sd->clockdiv = 2; + break; + case 5: + sd->clockdiv = 5; + break; + } + if (interlaced) { + sd->clockdiv = (sd->clockdiv + 1) * 2 - 1; + /* Higher then 10 does not work */ + if (sd->clockdiv > 10) + sd->clockdiv = 10; + } + break; + + case SEN_OV8610: + /* No framerate control ?? */ + sd->clockdiv = 0; + break; + } + + /* Check if we have enough bandwidth to disable compression */ + fps = (interlaced ? 60 : 30) / (sd->clockdiv + 1) + 1; + needed = fps * sd->gspca_dev.width * sd->gspca_dev.height * 3 / 2; + /* 1400 is a conservative estimate of the max nr of isoc packets/sec */ + if (needed > 1400 * packet_size) { + /* Enable Y and UV quantization and compression */ + reg_w(sd, R511_COMP_EN, 0x07); + reg_w(sd, R511_COMP_LUT_EN, 0x03); + } else { + reg_w(sd, R511_COMP_EN, 0x06); + reg_w(sd, R511_COMP_LUT_EN, 0x00); + } + + reg_w(sd, R51x_SYS_RESET, OV511_RESET_OMNICE); + reg_w(sd, R51x_SYS_RESET, 0); + + return 0; +} + /* Sets up the OV518/OV518+ with the given image parameters * * OV518 needs a completely different approach, until we can figure out what @@ -1718,7 +2276,19 @@ static int sd_init(struct gspca_dev *gspca_dev) */ static int ov518_mode_init_regs(struct sd *sd) { - int hsegs, vsegs; + int hsegs, vsegs, packet_size; + struct usb_host_interface *alt; + struct usb_interface *intf; + + intf = usb_ifnum_to_if(sd->gspca_dev.dev, sd->gspca_dev.iface); + alt = usb_altnum_to_altsetting(intf, sd->gspca_dev.alt); + if (!alt) { + PDEBUG(D_ERR, "Couldn't get altsetting"); + return -EIO; + } + + packet_size = le16_to_cpu(alt->endpoint[0].desc.wMaxPacketSize); + ov518_reg_w32(sd, R51x_FIFO_PSIZE, packet_size & ~7, 2); /******** Set the mode ********/ @@ -1755,20 +2325,30 @@ static int ov518_mode_init_regs(struct sd *sd) /* Windows driver does this here; who knows why */ reg_w(sd, 0x2f, 0x80); - /******** Set the framerate (to 30 FPS) ********/ - if (sd->bridge == BRIDGE_OV518PLUS) - sd->clockdiv = 1; - else - sd->clockdiv = 0; + /******** Set the framerate ********/ + sd->clockdiv = 1; /* Mode independent, but framerate dependent, regs */ - reg_w(sd, 0x51, 0x04); /* Clock divider; lower==faster */ + /* 0x51: Clock divider; Only works on some cams which use 2 crystals */ + reg_w(sd, 0x51, 0x04); reg_w(sd, 0x22, 0x18); reg_w(sd, 0x23, 0xff); - if (sd->bridge == BRIDGE_OV518PLUS) - reg_w(sd, 0x21, 0x19); - else + if (sd->bridge == BRIDGE_OV518PLUS) { + switch (sd->sensor) { + case SEN_OV7620: + if (sd->gspca_dev.width == 320) { + reg_w(sd, 0x20, 0x00); + reg_w(sd, 0x21, 0x19); + } else { + reg_w(sd, 0x20, 0x60); + reg_w(sd, 0x21, 0x1f); + } + break; + default: + reg_w(sd, 0x21, 0x19); + } + } else reg_w(sd, 0x71, 0x17); /* Compression-related? */ /* FIXME: Sensor-specific */ @@ -1879,7 +2459,11 @@ static int ov519_mode_init_regs(struct sd *sd) reg_w(sd, OV519_R10_H_SIZE, sd->gspca_dev.width >> 4); reg_w(sd, OV519_R11_V_SIZE, sd->gspca_dev.height >> 3); - reg_w(sd, OV519_R12_X_OFFSETL, 0x00); + if (sd->sensor == SEN_OV7670 && + sd->gspca_dev.cam.cam_mode[sd->gspca_dev.curr_mode].priv) + reg_w(sd, OV519_R12_X_OFFSETL, 0x04); + else + reg_w(sd, OV519_R12_X_OFFSETL, 0x00); reg_w(sd, OV519_R13_X_OFFSETH, 0x00); reg_w(sd, OV519_R14_Y_OFFSETL, 0x00); reg_w(sd, OV519_R15_Y_OFFSETH, 0x00); @@ -1971,7 +2555,7 @@ static int mode_init_ov_sensor_regs(struct sd *sd) int qvga; gspca_dev = &sd->gspca_dev; - qvga = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv; + qvga = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv & 1; /******** Mode (VGA/QVGA) and sensor specific regs ********/ switch (sd->sensor) { @@ -1999,30 +2583,16 @@ static int mode_init_ov_sensor_regs(struct sd *sd) #endif break; case SEN_OV7620: -/* i2c_w(sd, 0x2b, 0x00); */ - i2c_w_mask(sd, 0x14, qvga ? 0x20 : 0x00, 0x20); - i2c_w_mask(sd, 0x28, qvga ? 0x00 : 0x20, 0x20); - i2c_w(sd, 0x24, qvga ? 0x20 : 0x3a); - i2c_w(sd, 0x25, qvga ? 0x30 : 0x60); - i2c_w_mask(sd, 0x2d, qvga ? 0x40 : 0x00, 0x40); - i2c_w_mask(sd, 0x67, qvga ? 0xf0 : 0x90, 0xf0); - i2c_w_mask(sd, 0x74, qvga ? 0x20 : 0x00, 0x20); - break; case SEN_OV76BE: -/* i2c_w(sd, 0x2b, 0x00); */ i2c_w_mask(sd, 0x14, qvga ? 0x20 : 0x00, 0x20); -#if 0 - /* FIXME: Enable this once 7620AE uses 7620 initial settings */ i2c_w_mask(sd, 0x28, qvga ? 0x00 : 0x20, 0x20); i2c_w(sd, 0x24, qvga ? 0x20 : 0x3a); i2c_w(sd, 0x25, qvga ? 0x30 : 0x60); i2c_w_mask(sd, 0x2d, qvga ? 0x40 : 0x00, 0x40); i2c_w_mask(sd, 0x67, qvga ? 0xb0 : 0x90, 0xf0); i2c_w_mask(sd, 0x74, qvga ? 0x20 : 0x00, 0x20); -#endif break; case SEN_OV7640: -/* i2c_w(sd, 0x2b, 0x00); */ i2c_w_mask(sd, 0x14, qvga ? 0x20 : 0x00, 0x20); i2c_w_mask(sd, 0x28, qvga ? 0x00 : 0x20, 0x20); /* i2c_w(sd, 0x24, qvga ? 0x20 : 0x3a); */ @@ -2041,6 +2611,7 @@ static int mode_init_ov_sensor_regs(struct sd *sd) break; case SEN_OV6620: case SEN_OV6630: + case SEN_OV66308AF: i2c_w_mask(sd, 0x14, qvga ? 0x20 : 0x00, 0x20); break; default: @@ -2048,10 +2619,6 @@ static int mode_init_ov_sensor_regs(struct sd *sd) } /******** Palette-specific regs ********/ - if (sd->sensor == SEN_OV7610 || sd->sensor == SEN_OV76BE) { - /* not valid on the OV6620/OV7620/6630? */ - i2c_w_mask(sd, 0x0e, 0x00, 0x40); - } /* The OV518 needs special treatment. Although both the OV518 * and the OV6630 support a 16-bit video bus, only the 8 bit Y @@ -2061,25 +2628,12 @@ static int mode_init_ov_sensor_regs(struct sd *sd) /* OV7640 is 8-bit only */ - if (sd->sensor != SEN_OV6630 && sd->sensor != SEN_OV7640) + if (sd->sensor != SEN_OV6630 && sd->sensor != SEN_OV66308AF && + sd->sensor != SEN_OV7640) i2c_w_mask(sd, 0x13, 0x00, 0x20); /******** Clock programming ********/ - /* The OV6620 needs special handling. This prevents the - * severe banding that normally occurs */ - if (sd->sensor == SEN_OV6620) { - - /* Clock down */ - i2c_w(sd, 0x2a, 0x04); - i2c_w(sd, 0x11, sd->clockdiv); - i2c_w(sd, 0x2a, 0x84); - /* This next setting is critical. It seems to improve - * the gain or the contrast. The "reserved" bits seem - * to have some effect in this case. */ - i2c_w(sd, 0x2d, 0x85); - } else { - i2c_w(sd, 0x11, sd->clockdiv); - } + i2c_w(sd, 0x11, sd->clockdiv); /******** Special Features ********/ /* no evidence this is possible with OV7670, either */ @@ -2123,13 +2677,14 @@ static void sethvflip(struct sd *sd) static int set_ov_sensor_window(struct sd *sd) { struct gspca_dev *gspca_dev; - int qvga; + int qvga, crop; int hwsbase, hwebase, vwsbase, vwebase, hwscale, vwscale; int ret, hstart, hstop, vstop, vstart; __u8 v; gspca_dev = &sd->gspca_dev; - qvga = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv; + qvga = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv & 1; + crop = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv & 2; /* The different sensor ICs handle setting up of window differently. * IF YOU SET IT WRONG, YOU WILL GET ALL ZERO ISOC DATA FROM OV51x!! */ @@ -2148,14 +2703,19 @@ static int set_ov_sensor_window(struct sd *sd) break; case SEN_OV6620: case SEN_OV6630: + case SEN_OV66308AF: hwsbase = 0x38; hwebase = 0x3a; vwsbase = 0x05; vwebase = 0x06; - if (qvga) { + if (sd->sensor == SEN_OV66308AF && qvga) /* HDG: this fixes U and V getting swapped */ - hwsbase--; - vwsbase--; + hwsbase++; + if (crop) { + hwsbase += 8; + hwebase += 8; + vwsbase += 11; + vwebase += 11; } break; case SEN_OV7620: @@ -2180,6 +2740,7 @@ static int set_ov_sensor_window(struct sd *sd) switch (sd->sensor) { case SEN_OV6620: case SEN_OV6630: + case SEN_OV66308AF: if (qvga) { /* QCIF */ hwscale = 0; vwscale = 0; @@ -2232,7 +2793,7 @@ static int set_ov_sensor_window(struct sd *sd) if (qvga) { /* QVGA from ov7670.c by * Jonathan Corbet */ hstart = 164; - hstop = 20; + hstop = 28; vstart = 14; vstop = 494; } else { /* VGA */ @@ -2258,7 +2819,6 @@ static int set_ov_sensor_window(struct sd *sd) msleep(10); /* need to sleep between read and write to * same reg! */ i2c_w(sd, OV7670_REG_VREF, v); - sethvflip(sd); } else { i2c_w(sd, 0x17, hwsbase); i2c_w(sd, 0x18, hwebase + (sd->gspca_dev.width >> hwscale)); @@ -2275,6 +2835,10 @@ static int sd_start(struct gspca_dev *gspca_dev) int ret = 0; switch (sd->bridge) { + case BRIDGE_OV511: + case BRIDGE_OV511PLUS: + ret = ov511_mode_init_regs(sd); + break; case BRIDGE_OV518: case BRIDGE_OV518PLUS: ret = ov518_mode_init_regs(sd); @@ -2293,6 +2857,9 @@ static int sd_start(struct gspca_dev *gspca_dev) setcontrast(gspca_dev); setbrightness(gspca_dev); setcolors(gspca_dev); + sethvflip(sd); + setautobrightness(sd); + setfreq(sd); ret = ov51x_restart(sd); if (ret < 0) @@ -2312,23 +2879,88 @@ static void sd_stopN(struct gspca_dev *gspca_dev) ov51x_led_control(sd, 0); } -static void ov518_pkt_scan(struct gspca_dev *gspca_dev, +static void ov511_pkt_scan(struct gspca_dev *gspca_dev, struct gspca_frame *frame, /* target */ - __u8 *data, /* isoc packet */ + __u8 *in, /* isoc packet */ int len) /* iso packet length */ { - PDEBUG(D_STREAM, "ov518_pkt_scan: %d bytes", len); + struct sd *sd = (struct sd *) gspca_dev; - if (len & 7) { - len--; - PDEBUG(D_STREAM, "packet number: %d\n", (int)data[len]); + /* SOF/EOF packets have 1st to 8th bytes zeroed and the 9th + * byte non-zero. The EOF packet has image width/height in the + * 10th and 11th bytes. The 9th byte is given as follows: + * + * bit 7: EOF + * 6: compression enabled + * 5: 422/420/400 modes + * 4: 422/420/400 modes + * 3: 1 + * 2: snapshot button on + * 1: snapshot frame + * 0: even/odd field + */ + if (!(in[0] | in[1] | in[2] | in[3] | in[4] | in[5] | in[6] | in[7]) && + (in[8] & 0x08)) { + if (in[8] & 0x80) { + /* Frame end */ + if ((in[9] + 1) * 8 != gspca_dev->width || + (in[10] + 1) * 8 != gspca_dev->height) { + PDEBUG(D_ERR, "Invalid frame size, got: %dx%d," + " requested: %dx%d\n", + (in[9] + 1) * 8, (in[10] + 1) * 8, + gspca_dev->width, gspca_dev->height); + gspca_dev->last_packet_type = DISCARD_PACKET; + return; + } + /* Add 11 byte footer to frame, might be usefull */ + gspca_frame_add(gspca_dev, LAST_PACKET, frame, in, 11); + return; + } else { + /* Frame start */ + gspca_frame_add(gspca_dev, FIRST_PACKET, frame, in, 0); + sd->packet_nr = 0; + } } + /* Ignore the packet number */ + len--; + + /* intermediate packet */ + gspca_frame_add(gspca_dev, INTER_PACKET, frame, in, len); +} + +static void ov518_pkt_scan(struct gspca_dev *gspca_dev, + struct gspca_frame *frame, /* target */ + __u8 *data, /* isoc packet */ + int len) /* iso packet length */ +{ + struct sd *sd = (struct sd *) gspca_dev; + /* A false positive here is likely, until OVT gives me * the definitive SOF/EOF format */ if ((!(data[0] | data[1] | data[2] | data[3] | data[5])) && data[6]) { gspca_frame_add(gspca_dev, LAST_PACKET, frame, data, 0); gspca_frame_add(gspca_dev, FIRST_PACKET, frame, data, 0); + sd->packet_nr = 0; + } + + if (gspca_dev->last_packet_type == DISCARD_PACKET) + return; + + /* Does this device use packet numbers ? */ + if (len & 7) { + len--; + if (sd->packet_nr == data[len]) + sd->packet_nr++; + /* The last few packets of the frame (which are all 0's + except that they may contain part of the footer), are + numbered 0 */ + else if (sd->packet_nr == 0 || data[len]) { + PDEBUG(D_ERR, "Invalid packet nr: %d (expect: %d)", + (int)data[len], (int)sd->packet_nr); + gspca_dev->last_packet_type = DISCARD_PACKET; + return; + } } /* intermediate packet */ @@ -2389,6 +3021,7 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, switch (sd->bridge) { case BRIDGE_OV511: case BRIDGE_OV511PLUS: + ov511_pkt_scan(gspca_dev, frame, data, len); break; case BRIDGE_OV518: case BRIDGE_OV518PLUS: @@ -2414,13 +3047,13 @@ static void setbrightness(struct gspca_dev *gspca_dev) case SEN_OV76BE: case SEN_OV6620: case SEN_OV6630: + case SEN_OV66308AF: case SEN_OV7640: i2c_w(sd, OV7610_REG_BRT, val); break; case SEN_OV7620: /* 7620 doesn't like manual changes when in auto mode */ -/*fixme - * if (!sd->auto_brt) */ + if (!sd->autobrightness) i2c_w(sd, OV7610_REG_BRT, val); break; case SEN_OV7670: @@ -2443,6 +3076,7 @@ static void setcontrast(struct gspca_dev *gspca_dev) i2c_w(sd, OV7610_REG_CNT, val); break; case SEN_OV6630: + case SEN_OV66308AF: i2c_w_mask(sd, OV7610_REG_CNT, val >> 4, 0x0f); break; case SEN_OV8610: { @@ -2487,6 +3121,7 @@ static void setcolors(struct gspca_dev *gspca_dev) case SEN_OV76BE: case SEN_OV6620: case SEN_OV6630: + case SEN_OV66308AF: i2c_w(sd, OV7610_REG_SAT, val); break; case SEN_OV7620: @@ -2507,6 +3142,72 @@ static void setcolors(struct gspca_dev *gspca_dev) } } +static void setautobrightness(struct sd *sd) +{ + if (sd->sensor == SEN_OV7640 || sd->sensor == SEN_OV7670) + return; + + i2c_w_mask(sd, 0x2d, sd->autobrightness ? 0x10 : 0x00, 0x10); +} + +static void setfreq(struct sd *sd) +{ + if (sd->sensor == SEN_OV7670) { + switch (sd->freq) { + case 0: /* Banding filter disabled */ + i2c_w_mask(sd, OV7670_REG_COM8, 0, OV7670_COM8_BFILT); + break; + case 1: /* 50 hz */ + i2c_w_mask(sd, OV7670_REG_COM8, OV7670_COM8_BFILT, + OV7670_COM8_BFILT); + i2c_w_mask(sd, OV7670_REG_COM11, 0x08, 0x18); + break; + case 2: /* 60 hz */ + i2c_w_mask(sd, OV7670_REG_COM8, OV7670_COM8_BFILT, + OV7670_COM8_BFILT); + i2c_w_mask(sd, OV7670_REG_COM11, 0x00, 0x18); + break; + case 3: /* Auto hz */ + i2c_w_mask(sd, OV7670_REG_COM8, OV7670_COM8_BFILT, + OV7670_COM8_BFILT); + i2c_w_mask(sd, OV7670_REG_COM11, OV7670_COM11_HZAUTO, + 0x18); + break; + } + } else { + switch (sd->freq) { + case 0: /* Banding filter disabled */ + i2c_w_mask(sd, 0x2d, 0x00, 0x04); + i2c_w_mask(sd, 0x2a, 0x00, 0x80); + break; + case 1: /* 50 hz (filter on and framerate adj) */ + i2c_w_mask(sd, 0x2d, 0x04, 0x04); + i2c_w_mask(sd, 0x2a, 0x80, 0x80); + /* 20 fps -> 16.667 fps */ + if (sd->sensor == SEN_OV6620 || + sd->sensor == SEN_OV6630 || + sd->sensor == SEN_OV66308AF) + i2c_w(sd, 0x2b, 0x5e); + else + i2c_w(sd, 0x2b, 0xac); + break; + case 2: /* 60 hz (filter on, ...) */ + i2c_w_mask(sd, 0x2d, 0x04, 0x04); + if (sd->sensor == SEN_OV6620 || + sd->sensor == SEN_OV6630 || + sd->sensor == SEN_OV66308AF) { + /* 20 fps -> 15 fps */ + i2c_w_mask(sd, 0x2a, 0x80, 0x80); + i2c_w(sd, 0x2b, 0xa8); + } else { + /* no framerate adj. */ + i2c_w_mask(sd, 0x2a, 0x00, 0x80); + } + break; + } + } +} + static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val) { struct sd *sd = (struct sd *) gspca_dev; @@ -2597,6 +3298,71 @@ static int sd_getvflip(struct gspca_dev *gspca_dev, __s32 *val) return 0; } +static int sd_setautobrightness(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->autobrightness = val; + if (gspca_dev->streaming) + setautobrightness(sd); + return 0; +} + +static int sd_getautobrightness(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->autobrightness; + return 0; +} + +static int sd_setfreq(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->freq = val; + if (gspca_dev->streaming) + setfreq(sd); + return 0; +} + +static int sd_getfreq(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->freq; + return 0; +} + +static int sd_querymenu(struct gspca_dev *gspca_dev, + struct v4l2_querymenu *menu) +{ + struct sd *sd = (struct sd *) gspca_dev; + + switch (menu->id) { + case V4L2_CID_POWER_LINE_FREQUENCY: + switch (menu->index) { + case 0: /* V4L2_CID_POWER_LINE_FREQUENCY_DISABLED */ + strcpy((char *) menu->name, "NoFliker"); + return 0; + case 1: /* V4L2_CID_POWER_LINE_FREQUENCY_50HZ */ + strcpy((char *) menu->name, "50 Hz"); + return 0; + case 2: /* V4L2_CID_POWER_LINE_FREQUENCY_60HZ */ + strcpy((char *) menu->name, "60 Hz"); + return 0; + case 3: + if (sd->sensor != SEN_OV7670) + return -EINVAL; + + strcpy((char *) menu->name, "Automatic"); + return 0; + } + break; + } + return -EINVAL; +} + /* sub-driver description */ static const struct sd_desc sd_desc = { .name = MODULE_NAME, @@ -2607,6 +3373,7 @@ static const struct sd_desc sd_desc = { .start = sd_start, .stopN = sd_stopN, .pkt_scan = sd_pkt_scan, + .querymenu = sd_querymenu, }; /* -- module initialisation -- */ @@ -2615,17 +3382,22 @@ static const __devinitdata struct usb_device_id device_table[] = { {USB_DEVICE(0x041e, 0x405f), .driver_info = BRIDGE_OV519 }, {USB_DEVICE(0x041e, 0x4060), .driver_info = BRIDGE_OV519 }, {USB_DEVICE(0x041e, 0x4061), .driver_info = BRIDGE_OV519 }, - {USB_DEVICE(0x041e, 0x4064), .driver_info = BRIDGE_OV519 }, - {USB_DEVICE(0x041e, 0x4068), .driver_info = BRIDGE_OV519 }, + {USB_DEVICE(0x041e, 0x4064), + .driver_info = BRIDGE_OV519 | BRIDGE_INVERT_LED }, + {USB_DEVICE(0x041e, 0x4068), + .driver_info = BRIDGE_OV519 | BRIDGE_INVERT_LED }, {USB_DEVICE(0x045e, 0x028c), .driver_info = BRIDGE_OV519 }, {USB_DEVICE(0x054c, 0x0154), .driver_info = BRIDGE_OV519 }, {USB_DEVICE(0x054c, 0x0155), .driver_info = BRIDGE_OV519 }, + {USB_DEVICE(0x05a9, 0x0511), .driver_info = BRIDGE_OV511 }, {USB_DEVICE(0x05a9, 0x0518), .driver_info = BRIDGE_OV518 }, {USB_DEVICE(0x05a9, 0x0519), .driver_info = BRIDGE_OV519 }, {USB_DEVICE(0x05a9, 0x0530), .driver_info = BRIDGE_OV519 }, {USB_DEVICE(0x05a9, 0x4519), .driver_info = BRIDGE_OV519 }, {USB_DEVICE(0x05a9, 0x8519), .driver_info = BRIDGE_OV519 }, + {USB_DEVICE(0x05a9, 0xa511), .driver_info = BRIDGE_OV511PLUS }, {USB_DEVICE(0x05a9, 0xa518), .driver_info = BRIDGE_OV518PLUS }, + {USB_DEVICE(0x0813, 0x0002), .driver_info = BRIDGE_OV511PLUS }, {} }; diff --git a/linux/drivers/media/video/gspca/sonixj.c b/linux/drivers/media/video/gspca/sonixj.c index a4c935d69..a2fb94eb7 100644 --- a/linux/drivers/media/video/gspca/sonixj.c +++ b/linux/drivers/media/video/gspca/sonixj.c @@ -46,6 +46,7 @@ struct sd { u8 gamma; u8 vflip; /* ov7630/ov7648 only */ u8 infrared; /* mt9v111 only */ + u8 freq; /* ov76xx only */ u8 quality; /* image quality */ #define QUALITY_MIN 60 #define QUALITY_MAX 95 @@ -96,8 +97,11 @@ static int sd_setvflip(struct gspca_dev *gspca_dev, __s32 val); static int sd_getvflip(struct gspca_dev *gspca_dev, __s32 *val); static int sd_setinfrared(struct gspca_dev *gspca_dev, __s32 val); static int sd_getinfrared(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setfreq(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getfreq(struct gspca_dev *gspca_dev, __s32 *val); static struct ctrl sd_ctrls[] = { +#define BRIGHTNESS_IDX 0 { { .id = V4L2_CID_BRIGHTNESS, @@ -113,6 +117,7 @@ static struct ctrl sd_ctrls[] = { .set = sd_setbrightness, .get = sd_getbrightness, }, +#define CONTRAST_IDX 1 { { .id = V4L2_CID_CONTRAST, @@ -128,20 +133,22 @@ static struct ctrl sd_ctrls[] = { .set = sd_setcontrast, .get = sd_getcontrast, }, +#define COLOR_IDX 2 { { .id = V4L2_CID_SATURATION, .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Color", + .name = "Saturation", .minimum = 0, .maximum = 40, .step = 1, -#define COLOR_DEF 32 +#define COLOR_DEF 25 .default_value = COLOR_DEF, }, .set = sd_setcolors, .get = sd_getcolors, }, +#define BLUE_BALANCE_IDX 3 { { .id = V4L2_CID_BLUE_BALANCE, @@ -156,6 +163,7 @@ static struct ctrl sd_ctrls[] = { .set = sd_setblue_balance, .get = sd_getblue_balance, }, +#define RED_BALANCE_IDX 4 { { .id = V4L2_CID_RED_BALANCE, @@ -170,6 +178,7 @@ static struct ctrl sd_ctrls[] = { .set = sd_setred_balance, .get = sd_getred_balance, }, +#define GAMMA_IDX 5 { { .id = V4L2_CID_GAMMA, @@ -184,7 +193,7 @@ static struct ctrl sd_ctrls[] = { .set = sd_setgamma, .get = sd_getgamma, }, -#define AUTOGAIN_IDX 5 +#define AUTOGAIN_IDX 6 { { .id = V4L2_CID_AUTOGAIN, @@ -200,7 +209,7 @@ static struct ctrl sd_ctrls[] = { .get = sd_getautogain, }, /* ov7630/ov7648 only */ -#define VFLIP_IDX 6 +#define VFLIP_IDX 7 { { .id = V4L2_CID_VFLIP, @@ -209,14 +218,14 @@ static struct ctrl sd_ctrls[] = { .minimum = 0, .maximum = 1, .step = 1, -#define VFLIP_DEF 0 /* vflip def = 1 for ov7630 */ +#define VFLIP_DEF 0 .default_value = VFLIP_DEF, }, .set = sd_setvflip, .get = sd_getvflip, }, /* mt9v111 only */ -#define INFRARED_IDX 7 +#define INFRARED_IDX 8 { { .id = V4L2_CID_INFRARED, @@ -231,32 +240,48 @@ static struct ctrl sd_ctrls[] = { .set = sd_setinfrared, .get = sd_getinfrared, }, +/* ov7630/ov7648/ov7660 only */ +#define FREQ_IDX 9 + { + { + .id = V4L2_CID_POWER_LINE_FREQUENCY, + .type = V4L2_CTRL_TYPE_MENU, + .name = "Light frequency filter", + .minimum = 0, + .maximum = 2, /* 0: 0, 1: 50Hz, 2:60Hz */ + .step = 1, +#define FREQ_DEF 2 + .default_value = FREQ_DEF, + }, + .set = sd_setfreq, + .get = sd_getfreq, + }, }; /* table of the disabled controls */ static __u32 ctrl_dis[] = { - (1 << INFRARED_IDX) | (1 << VFLIP_IDX), + (1 << INFRARED_IDX) | (1 << VFLIP_IDX) | (1 << FREQ_IDX), /* SENSOR_HV7131R 0 */ - (1 << INFRARED_IDX) | (1 << VFLIP_IDX), + (1 << INFRARED_IDX) | (1 << VFLIP_IDX) | (1 << FREQ_IDX), /* SENSOR_MI0360 1 */ - (1 << INFRARED_IDX) | (1 << VFLIP_IDX), + (1 << INFRARED_IDX) | (1 << VFLIP_IDX) | (1 << FREQ_IDX), /* SENSOR_MO4000 2 */ #if 1 - (1 << VFLIP_IDX), + (1 << VFLIP_IDX) | (1 << FREQ_IDX), #else - (1 << AUTOGAIN_IDX) | (1 << VFLIP_IDX), + (1 << AUTOGAIN_IDX) | (1 << VFLIP_IDX) | (1 << FREQ_IDX), #endif /* SENSOR_MT9V111 3 */ - (1 << INFRARED_IDX) | (1 << VFLIP_IDX), + (1 << INFRARED_IDX) | (1 << VFLIP_IDX) | (1 << FREQ_IDX), /* SENSOR_OM6802 4 */ - (1 << AUTOGAIN_IDX) | (1 << INFRARED_IDX), + (1 << INFRARED_IDX), /* SENSOR_OV7630 5 */ (1 << INFRARED_IDX), /* SENSOR_OV7648 6 */ (1 << AUTOGAIN_IDX) | (1 << INFRARED_IDX) | (1 << VFLIP_IDX), /* SENSOR_OV7660 7 */ - (1 << AUTOGAIN_IDX) | (1 << INFRARED_IDX) | (1 << VFLIP_IDX), - /* SENSOR_SP80708 8 */ + (1 << AUTOGAIN_IDX) | (1 << INFRARED_IDX) | (1 << VFLIP_IDX) | + (1 << FREQ_IDX), /* SENSOR_SP80708 8 */ }; static const struct v4l2_pix_format vga_mode[] = { @@ -272,7 +297,8 @@ static const struct v4l2_pix_format vga_mode[] = { .priv = 1}, {640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, .bytesperline = 640, - .sizeimage = 640 * 480 * 3 / 8 + 590, + /* Note 3 / 8 is not large enough, not even 5 / 8 is ?! */ + .sizeimage = 640 * 480 * 3 / 4 + 590, .colorspace = V4L2_COLORSPACE_JPEG, .priv = 0}, }; @@ -612,7 +638,9 @@ static const u8 ov7630_sensor_init[][8] = { /* win: i2c_r from 00 to 80 */ {0xd1, 0x21, 0x03, 0x80, 0x10, 0x20, 0x80, 0x10}, {0xb1, 0x21, 0x0c, 0x20, 0x20, 0x00, 0x00, 0x10}, - {0xd1, 0x21, 0x11, 0x00, 0x48, 0xc0, 0x00, 0x10}, +/* HDG: 0x11 was 0x00 change to 0x01 for better exposure (15 fps instead of 30) + 0x13 was 0xc0 change to 0xc3 for auto gain and exposure */ + {0xd1, 0x21, 0x11, 0x01, 0x48, 0xc3, 0x00, 0x10}, {0xb1, 0x21, 0x15, 0x80, 0x03, 0x00, 0x00, 0x10}, {0xd1, 0x21, 0x17, 0x1b, 0xbd, 0x05, 0xf6, 0x10}, {0xa1, 0x21, 0x1b, 0x04, 0x00, 0x00, 0x00, 0x10}, @@ -646,9 +674,8 @@ static const u8 ov7630_sensor_init[][8] = { {0xa1, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10}, {0xb1, 0x21, 0x01, 0x80, 0x80, 0x00, 0x00, 0x10}, /* */ - {0xa1, 0x21, 0x11, 0x00, 0x00, 0x00, 0x00, 0x10}, - {0xa1, 0x21, 0x2a, 0x88, 0x00, 0x00, 0x00, 0x10}, - {0xa1, 0x21, 0x2b, 0x34, 0x00, 0x00, 0x00, 0x10}, +/* {0xa1, 0x21, 0x2a, 0x88, 0x00, 0x00, 0x00, 0x10}, * set by setfreq */ +/* {0xa1, 0x21, 0x2b, 0x34, 0x00, 0x00, 0x00, 0x10}, * set by setfreq */ /* */ {0xa1, 0x21, 0x10, 0x83, 0x00, 0x00, 0x00, 0x10}, /* {0xb1, 0x21, 0x01, 0x88, 0x70, 0x00, 0x00, 0x10}, */ @@ -681,7 +708,7 @@ static const u8 ov7648_sensor_init[][8] = { {0xd1, 0x21, 0x21, 0x86, 0x00, 0xde, 0xa0, 0x10}, /* {0xd1, 0x21, 0x25, 0x80, 0x32, 0xfe, 0xa0, 0x10}, jfm done */ /* {0xd1, 0x21, 0x29, 0x00, 0x91, 0x00, 0x88, 0x10}, jfm done */ - {0xb1, 0x21, 0x2d, 0x85, 0x00, 0x00, 0x00, 0x10}, +/* {0xb1, 0x21, 0x2d, 0x85, 0x00, 0x00, 0x00, 0x10}, set by setfreq */ /*...*/ /* {0xa1, 0x21, 0x12, 0x08, 0x00, 0x00, 0x00, 0x10}, jfm done */ /* {0xa1, 0x21, 0x75, 0x06, 0x00, 0x00, 0x00, 0x10}, * COMN @@ -1310,11 +1337,9 @@ static int sd_config(struct gspca_dev *gspca_dev, sd->gamma = GAMMA_DEF; sd->autogain = AUTOGAIN_DEF; sd->ag_cnt = -1; - if (sd->sensor != SENSOR_OV7630) - sd->vflip = 0; - else - sd->vflip = 1; + sd->vflip = VFLIP_DEF; sd->infrared = INFRARED_DEF; + sd->freq = FREQ_DEF; sd->quality = QUALITY_DEF; sd->jpegqual = 80; @@ -1595,7 +1620,7 @@ static void setautogain(struct gspca_dev *gspca_dev) else comb = 0xa0; if (sd->autogain) - comb |= 0x02; + comb |= 0x03; i2c_w1(&sd->gspca_dev, 0x13, comb); return; } @@ -1611,12 +1636,15 @@ static void setvflip(struct sd *sd) { u8 comn; - if (sd->sensor == SENSOR_OV7630) + if (sd->sensor == SENSOR_OV7630) { comn = 0x02; - else + if (!sd->vflip) + comn |= 0x80; + } else { comn = 0x06; - if (sd->vflip) - comn |= 0x80; + if (sd->vflip) + comn |= 0x80; + } i2c_w1(&sd->gspca_dev, 0x75, comn); } @@ -1634,6 +1662,58 @@ static void setinfrared(struct sd *sd) #endif } +static void setfreq(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + if (sd->sensor == SENSOR_OV7660) { + switch (sd->freq) { + case 0: /* Banding filter disabled */ + i2c_w1(gspca_dev, 0x13, 0xdf); + break; + case 1: /* 50 hz */ + i2c_w1(gspca_dev, 0x13, 0xff); + i2c_w1(gspca_dev, 0x3b, 0x0a); + break; + case 2: /* 60 hz */ + i2c_w1(gspca_dev, 0x13, 0xff); + i2c_w1(gspca_dev, 0x3b, 0x02); + break; + } + } else { + u8 reg2a = 0, reg2b = 0, reg2d = 0; + + /* Get reg2a / reg2d base values */ + switch (sd->sensor) { + case SENSOR_OV7630: + reg2a = 0x08; + reg2d = 0x01; + break; + case SENSOR_OV7648: + reg2a = 0x11; + reg2d = 0x81; + break; + } + + switch (sd->freq) { + case 0: /* Banding filter disabled */ + break; + case 1: /* 50 hz (filter on and framerate adj) */ + reg2a |= 0x80; + reg2b = 0xac; + reg2d |= 0x04; + break; + case 2: /* 60 hz (filter on, no framerate adj) */ + reg2a |= 0x80; + reg2d |= 0x04; + break; + } + i2c_w1(gspca_dev, 0x2a, reg2a); + i2c_w1(gspca_dev, 0x2b, reg2b); + i2c_w1(gspca_dev, 0x2d, reg2d); + } +} + static void setjpegqual(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; @@ -1865,6 +1945,7 @@ static int sd_start(struct gspca_dev *gspca_dev) setbrightness(gspca_dev); setcontrast(gspca_dev); setautogain(gspca_dev); + setfreq(gspca_dev); return 0; } @@ -2168,6 +2249,24 @@ static int sd_getinfrared(struct gspca_dev *gspca_dev, __s32 *val) return 0; } +static int sd_setfreq(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->freq = val; + if (gspca_dev->streaming) + setfreq(gspca_dev); + return 0; +} + +static int sd_getfreq(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->freq; + return 0; +} + static int sd_set_jcomp(struct gspca_dev *gspca_dev, struct v4l2_jpegcompression *jcomp) { @@ -2196,6 +2295,27 @@ static int sd_get_jcomp(struct gspca_dev *gspca_dev, return 0; } +static int sd_querymenu(struct gspca_dev *gspca_dev, + struct v4l2_querymenu *menu) +{ + switch (menu->id) { + case V4L2_CID_POWER_LINE_FREQUENCY: + switch (menu->index) { + case 0: /* V4L2_CID_POWER_LINE_FREQUENCY_DISABLED */ + strcpy((char *) menu->name, "NoFliker"); + return 0; + case 1: /* V4L2_CID_POWER_LINE_FREQUENCY_50HZ */ + strcpy((char *) menu->name, "50 Hz"); + return 0; + case 2: /* V4L2_CID_POWER_LINE_FREQUENCY_60HZ */ + strcpy((char *) menu->name, "60 Hz"); + return 0; + } + break; + } + return -EINVAL; +} + /* sub-driver description */ static const struct sd_desc sd_desc = { .name = MODULE_NAME, @@ -2210,6 +2330,7 @@ static const struct sd_desc sd_desc = { .dq_callback = do_autogain, .get_jcomp = sd_get_jcomp, .set_jcomp = sd_set_jcomp, + .querymenu = sd_querymenu, }; /* -- module initialisation -- */ @@ -2270,7 +2391,7 @@ static const __devinitdata struct usb_device_id device_table[] = { {USB_DEVICE(0x0c45, 0x613b), BSI(SN9C120, OV7660, 0x21)}, #endif {USB_DEVICE(0x0c45, 0x613c), BSI(SN9C120, HV7131R, 0x11)}, -/* {USB_DEVICE(0x0c45, 0x613e), BSI(SN9C120, OV7630, 0x??)}, */ + {USB_DEVICE(0x0c45, 0x613e), BSI(SN9C120, OV7630, 0x21)}, {USB_DEVICE(0x0c45, 0x6143), BSI(SN9C120, SP80708, 0x18)}, {} }; diff --git a/linux/drivers/media/video/gspca/stv06xx/Makefile b/linux/drivers/media/video/gspca/stv06xx/Makefile index feeaa94ab..2f3c3a606 100644 --- a/linux/drivers/media/video/gspca/stv06xx/Makefile +++ b/linux/drivers/media/video/gspca/stv06xx/Makefile @@ -3,7 +3,8 @@ obj-$(CONFIG_USB_STV06XX) += gspca_stv06xx.o gspca_stv06xx-objs := stv06xx.o \ stv06xx_vv6410.o \ stv06xx_hdcs.o \ - stv06xx_pb0100.o + stv06xx_pb0100.o \ + stv06xx_st6422.o EXTRA_CFLAGS += -Idrivers/media/video/gspca diff --git a/linux/drivers/media/video/gspca/stv06xx/stv06xx.c b/linux/drivers/media/video/gspca/stv06xx/stv06xx.c index e573c3406..0da8e0de0 100644 --- a/linux/drivers/media/video/gspca/stv06xx/stv06xx.c +++ b/linux/drivers/media/video/gspca/stv06xx/stv06xx.c @@ -92,11 +92,10 @@ static int stv06xx_write_sensor_finish(struct sd *sd) { int err = 0; - if (IS_850(sd)) { + if (sd->bridge == BRIDGE_STV610) { struct usb_device *udev = sd->gspca_dev.dev; __u8 *buf = sd->gspca_dev.usb_buf; - /* Quickam Web needs an extra packet */ buf[0] = 0; err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x04, 0x40, 0x1704, 0, buf, 1, @@ -253,7 +252,7 @@ static int stv06xx_init(struct gspca_dev *gspca_dev) err = sd->sensor->init(sd); - if (dump_sensor) + if (dump_sensor && sd->sensor->dump) sd->sensor->dump(sd); return (err < 0) ? err : 0; @@ -318,6 +317,8 @@ static void stv06xx_pkt_scan(struct gspca_dev *gspca_dev, __u8 *data, /* isoc packet */ int len) /* iso packet length */ { + struct sd *sd = (struct sd *) gspca_dev; + PDEBUG(D_PACK, "Packet of length %d arrived", len); /* A packet may contain several frames @@ -343,14 +344,29 @@ static void stv06xx_pkt_scan(struct gspca_dev *gspca_dev, if (len < chunk_len) { PDEBUG(D_ERR, "URB packet length is smaller" " than the specified chunk length"); + gspca_dev->last_packet_type = DISCARD_PACKET; return; } + /* First byte seem to be 02=data 2nd byte is unknown??? */ + if (sd->bridge == BRIDGE_ST6422 && (id & 0xFF00) == 0x0200) + goto frame_data; + switch (id) { case 0x0200: case 0x4200: +frame_data: PDEBUG(D_PACK, "Frame data packet detected"); + if (sd->to_skip) { + int skip = (sd->to_skip < chunk_len) ? + sd->to_skip : chunk_len; + data += skip; + len -= skip; + chunk_len -= skip; + sd->to_skip -= skip; + } + gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, chunk_len); break; @@ -365,6 +381,9 @@ static void stv06xx_pkt_scan(struct gspca_dev *gspca_dev, gspca_frame_add(gspca_dev, FIRST_PACKET, frame, data, 0); + if (sd->bridge == BRIDGE_ST6422) + sd->to_skip = gspca_dev->width * 4; + if (chunk_len) PDEBUG(D_ERR, "Chunk length is " "non-zero on a SOF"); @@ -395,8 +414,12 @@ static void stv06xx_pkt_scan(struct gspca_dev *gspca_dev, /* Unknown chunk with 2 bytes of data, occurs 2-3 times per USB interrupt */ break; + case 0x42ff: + PDEBUG(D_PACK, "Chunk 0x42ff detected"); + /* Special chunk seen sometimes on the ST6422 */ + break; default: - PDEBUG(D_PACK, "Unknown chunk %d detected", id); + PDEBUG(D_PACK, "Unknown chunk 0x%04x detected", id); /* Unknown chunk */ } data += chunk_len; @@ -428,11 +451,16 @@ static int stv06xx_config(struct gspca_dev *gspca_dev, cam = &gspca_dev->cam; sd->desc = sd_desc; + sd->bridge = id->driver_info; gspca_dev->sd_desc = &sd->desc; if (dump_bridge) stv06xx_dump_bridge(sd); + sd->sensor = &stv06xx_sensor_st6422; + if (!sd->sensor->probe(sd)) + return 0; + sd->sensor = &stv06xx_sensor_vv6410; if (!sd->sensor->probe(sd)) return 0; @@ -457,9 +485,20 @@ static int stv06xx_config(struct gspca_dev *gspca_dev, /* -- module initialisation -- */ static const __devinitdata struct usb_device_id device_table[] = { - {USB_DEVICE(0x046d, 0x0840)}, /* QuickCam Express */ - {USB_DEVICE(0x046d, 0x0850)}, /* LEGO cam / QuickCam Web */ - {USB_DEVICE(0x046d, 0x0870)}, /* Dexxa WebCam USB */ + /* QuickCam Express */ + {USB_DEVICE(0x046d, 0x0840), .driver_info = BRIDGE_STV600 }, + /* LEGO cam / QuickCam Web */ + {USB_DEVICE(0x046d, 0x0850), .driver_info = BRIDGE_STV610 }, + /* Dexxa WebCam USB */ + {USB_DEVICE(0x046d, 0x0870), .driver_info = BRIDGE_STV602 }, + /* QuickCam Messenger */ + {USB_DEVICE(0x046D, 0x08F0), .driver_info = BRIDGE_ST6422 }, + /* QuickCam Communicate */ + {USB_DEVICE(0x046D, 0x08F5), .driver_info = BRIDGE_ST6422 }, + /* QuickCam Messenger (new) */ + {USB_DEVICE(0x046D, 0x08F6), .driver_info = BRIDGE_ST6422 }, + /* QuickCam Messenger (new) */ + {USB_DEVICE(0x046D, 0x08DA), .driver_info = BRIDGE_ST6422 }, {} }; MODULE_DEVICE_TABLE(usb, device_table); diff --git a/linux/drivers/media/video/gspca/stv06xx/stv06xx.h b/linux/drivers/media/video/gspca/stv06xx/stv06xx.h index 1207e7d17..bb73c3288 100644 --- a/linux/drivers/media/video/gspca/stv06xx/stv06xx.h +++ b/linux/drivers/media/video/gspca/stv06xx/stv06xx.h @@ -31,6 +31,7 @@ #define STV06XX_H_ #include "gspca.h" +#include "compat.h" #define MODULE_NAME "STV06xx" @@ -93,6 +94,17 @@ struct sd { /* Sensor private data */ void *sensor_priv; + + /* The first 4 lines produced by the stv6422 are no good, this keeps + track of how many bytes we still need to skip during a frame */ + int to_skip; + + /* Bridge / Camera type */ + u8 bridge; + #define BRIDGE_STV600 0 + #define BRIDGE_STV602 1 + #define BRIDGE_STV610 2 + #define BRIDGE_ST6422 3 /* With integrated sensor */ }; int stv06xx_write_bridge(struct sd *sd, u16 address, u16 i2c_data); diff --git a/linux/drivers/media/video/gspca/stv06xx/stv06xx_hdcs.c b/linux/drivers/media/video/gspca/stv06xx/stv06xx_hdcs.c index b16903814..3039ec208 100644 --- a/linux/drivers/media/video/gspca/stv06xx/stv06xx_hdcs.c +++ b/linux/drivers/media/video/gspca/stv06xx/stv06xx_hdcs.c @@ -434,7 +434,7 @@ static int hdcs_probe_1x00(struct sd *sd) hdcs->exp.er = 100; /* - * Frame rate on HDCS-1000 0x46D:0x840 depends on PSMP: + * Frame rate on HDCS-1000 with STV600 depends on PSMP: * 4 = doesn't work at all * 5 = 7.8 fps, * 6 = 6.9 fps, @@ -443,7 +443,7 @@ static int hdcs_probe_1x00(struct sd *sd) * 15 = 4.4 fps, * 31 = 2.8 fps * - * Frame rate on HDCS-1000 0x46D:0x870 depends on PSMP: + * Frame rate on HDCS-1000 with STV602 depends on PSMP: * 15 = doesn't work at all * 18 = doesn't work at all * 19 = 7.3 fps @@ -453,7 +453,7 @@ static int hdcs_probe_1x00(struct sd *sd) * 24 = 6.3 fps * 30 = 5.4 fps */ - hdcs->psmp = IS_870(sd) ? 20 : 5; + hdcs->psmp = (sd->bridge == BRIDGE_STV602) ? 20 : 5; sd->sensor_priv = hdcs; @@ -530,7 +530,7 @@ static int hdcs_init(struct sd *sd) int i, err = 0; /* Set the STV0602AA in STV0600 emulation mode */ - if (IS_870(sd)) + if (sd->bridge == BRIDGE_STV602) stv06xx_write_bridge(sd, STV_STV0600_EMULATION, 1); /* Execute the bridge init */ @@ -558,7 +558,7 @@ static int hdcs_init(struct sd *sd) return err; /* Set PGA sample duration - (was 0x7E for IS_870, but caused slow framerate with HDCS-1020) */ + (was 0x7E for the STV602, but caused slow framerate with HDCS-1020) */ if (IS_1020(sd)) err = stv06xx_write_sensor(sd, HDCS_TCTRL, (HDCS_ADC_START_SIG_DUR << 6) | hdcs->psmp); diff --git a/linux/drivers/media/video/gspca/stv06xx/stv06xx_sensor.h b/linux/drivers/media/video/gspca/stv06xx/stv06xx_sensor.h index e88c42f7d..934b9cebc 100644 --- a/linux/drivers/media/video/gspca/stv06xx/stv06xx_sensor.h +++ b/linux/drivers/media/video/gspca/stv06xx/stv06xx_sensor.h @@ -32,14 +32,13 @@ #include "stv06xx.h" -#define IS_850(sd) ((sd)->gspca_dev.dev->descriptor.idProduct == 0x850) -#define IS_870(sd) ((sd)->gspca_dev.dev->descriptor.idProduct == 0x870) #define IS_1020(sd) ((sd)->sensor == &stv06xx_sensor_hdcs1020) extern const struct stv06xx_sensor stv06xx_sensor_vv6410; extern const struct stv06xx_sensor stv06xx_sensor_hdcs1x00; extern const struct stv06xx_sensor stv06xx_sensor_hdcs1020; extern const struct stv06xx_sensor stv06xx_sensor_pb0100; +extern const struct stv06xx_sensor stv06xx_sensor_st6422; struct stv06xx_sensor { /* Defines the name of a sensor */ diff --git a/linux/drivers/media/video/gspca/stv06xx/stv06xx_st6422.c b/linux/drivers/media/video/gspca/stv06xx/stv06xx_st6422.c new file mode 100644 index 000000000..87cb5b9dd --- /dev/null +++ b/linux/drivers/media/video/gspca/stv06xx/stv06xx_st6422.c @@ -0,0 +1,453 @@ +/* + * Support for the sensor part which is integrated (I think) into the + * st6422 stv06xx alike bridge, as its integrated there are no i2c writes + * but instead direct bridge writes. + * + * Copyright (c) 2009 Hans de Goede <hdegoede@redhat.com> + * + * Strongly based on qc-usb-messenger, which is: + * Copyright (c) 2001 Jean-Fredric Clere, Nikolas Zimmermann, Georg Acher + * Mark Cave-Ayland, Carlo E Prelz, Dick Streefland + * Copyright (c) 2002, 2003 Tuukka Toivonen + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "stv06xx_st6422.h" + +static struct v4l2_pix_format st6422_mode[] = { + /* Note we actually get 124 lines of data, of which we skip the 4st + 4 as they are garbage */ + { + 162, + 120, + V4L2_PIX_FMT_SGRBG8, + V4L2_FIELD_NONE, + .sizeimage = 162 * 120, + .bytesperline = 162, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 1 + }, + /* Note we actually get 248 lines of data, of which we skip the 4st + 4 as they are garbage, and we tell the app it only gets the + first 240 of the 244 lines it actually gets, so that it ignores + the last 4. */ + { + 324, + 240, + V4L2_PIX_FMT_SGRBG8, + V4L2_FIELD_NONE, + .sizeimage = 324 * 244, + .bytesperline = 324, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 0 + }, +}; + +static const struct ctrl st6422_ctrl[] = { +#define BRIGHTNESS_IDX 0 + { + { + .id = V4L2_CID_BRIGHTNESS, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Brightness", + .minimum = 0, + .maximum = 31, + .step = 1, + .default_value = 3 + }, + .set = st6422_set_brightness, + .get = st6422_get_brightness + }, +#define CONTRAST_IDX 1 + { + { + .id = V4L2_CID_CONTRAST, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Contrast", + .minimum = 0, + .maximum = 15, + .step = 1, + .default_value = 11 + }, + .set = st6422_set_contrast, + .get = st6422_get_contrast + }, +#define GAIN_IDX 2 + { + { + .id = V4L2_CID_GAIN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Gain", + .minimum = 0, + .maximum = 255, + .step = 1, + .default_value = 64 + }, + .set = st6422_set_gain, + .get = st6422_get_gain + }, +#define EXPOSURE_IDX 3 + { + { + .id = V4L2_CID_EXPOSURE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Exposure", + .minimum = 0, + .maximum = 1023, + .step = 1, + .default_value = 256 + }, + .set = st6422_set_exposure, + .get = st6422_get_exposure + }, +}; + +static int st6422_probe(struct sd *sd) +{ + int i; + s32 *sensor_settings; + + if (sd->bridge != BRIDGE_ST6422) + return -ENODEV; + + info("st6422 sensor detected"); + + sensor_settings = kmalloc(ARRAY_SIZE(st6422_ctrl) * sizeof(s32), + GFP_KERNEL); + if (!sensor_settings) + return -ENOMEM; + + sd->gspca_dev.cam.cam_mode = st6422_mode; + sd->gspca_dev.cam.nmodes = ARRAY_SIZE(st6422_mode); + sd->desc.ctrls = st6422_ctrl; + sd->desc.nctrls = ARRAY_SIZE(st6422_ctrl); + sd->sensor_priv = sensor_settings; + + for (i = 0; i < sd->desc.nctrls; i++) + sensor_settings[i] = st6422_ctrl[i].qctrl.default_value; + + return 0; +} + +static int st6422_init(struct sd *sd) +{ + int err = 0, i; + + const u16 st6422_bridge_init[][2] = { + { STV_ISO_ENABLE, 0x00 }, /* disable capture */ + { 0x1436, 0x00 }, + { 0x1432, 0x03 }, /* 0x00-0x1F brightness */ + { 0x143a, 0xF9 }, /* 0x00-0x0F contrast */ + { 0x0509, 0x38 }, /* R */ + { 0x050a, 0x38 }, /* G */ + { 0x050b, 0x38 }, /* B */ + { 0x050c, 0x2A }, + { 0x050d, 0x01 }, + + + { 0x1431, 0x00 }, /* 0x00-0x07 ??? */ + { 0x1433, 0x34 }, /* 160x120, 0x00-0x01 night filter */ + { 0x1438, 0x18 }, /* 640x480 */ +/* 18 bayes */ +/* 10 compressed? */ + + { 0x1439, 0x00 }, +/* antiflimmer?? 0xa2 ger perfekt bild mot monitor */ + + { 0x143b, 0x05 }, + { 0x143c, 0x00 }, /* 0x00-0x01 - ??? */ + + +/* shutter time 0x0000-0x03FF */ +/* low value give good picures on moving objects (but requires much light) */ +/* high value gives good picures in darkness (but tends to be overexposed) */ + { 0x143e, 0x01 }, + { 0x143d, 0x00 }, + + { 0x1442, 0xe2 }, +/* write: 1x1x xxxx */ +/* read: 1x1x xxxx */ +/* bit 5 == button pressed and hold if 0 */ +/* write 0xe2,0xea */ + +/* 0x144a */ +/* 0x00 init */ +/* bit 7 == button has been pressed, but not handled */ + +/* interrupt */ +/* if(urb->iso_frame_desc[i].status == 0x80) { */ +/* if(urb->iso_frame_desc[i].status == 0x88) { */ + + { 0x1500, 0xd0 }, + { 0x1500, 0xd0 }, + { 0x1500, 0x50 }, /* 0x00 - 0xFF 0x80 == compr ? */ + + { 0x1501, 0xaf }, +/* high val-> ljus area blir morkare. */ +/* low val -> ljus area blir ljusare. */ + { 0x1502, 0xc2 }, +/* high val-> ljus area blir morkare. */ +/* low val -> ljus area blir ljusare. */ + { 0x1503, 0x45 }, +/* high val-> ljus area blir morkare. */ +/* low val -> ljus area blir ljusare. */ + + { 0x1505, 0x02 }, +/* 2 : 324x248 80352 bytes */ +/* 7 : 248x162 40176 bytes */ +/* c+f: 162*124 20088 bytes */ + + { 0x150e, 0x8e }, + { 0x150f, 0x37 }, + { 0x15c0, 0x00 }, + { 0x15c1, 1023 }, /* 160x120, ISOC_PACKET_SIZE */ + { 0x15c3, 0x08 }, /* 0x04/0x14 ... test pictures ??? */ + + + { 0x143f, 0x01 }, /* commit settings */ + + }; + + for (i = 0; i < ARRAY_SIZE(st6422_bridge_init) && !err; i++) { + err = stv06xx_write_bridge(sd, st6422_bridge_init[i][0], + st6422_bridge_init[i][1]); + } + + return err; +} + +static void st6422_disconnect(struct sd *sd) +{ + sd->sensor = NULL; + kfree(sd->sensor_priv); +} + +static int st6422_start(struct sd *sd) +{ + int err, packet_size; + struct cam *cam = &sd->gspca_dev.cam; + s32 *sensor_settings = sd->sensor_priv; + struct usb_host_interface *alt; + struct usb_interface *intf; + + intf = usb_ifnum_to_if(sd->gspca_dev.dev, sd->gspca_dev.iface); + alt = usb_altnum_to_altsetting(intf, sd->gspca_dev.alt); + if (!alt) { + PDEBUG(D_ERR, "Couldn't get altsetting"); + return -EIO; + } + + packet_size = le16_to_cpu(alt->endpoint[0].desc.wMaxPacketSize); + err = stv06xx_write_bridge(sd, 0x15c1, packet_size); + if (err < 0) + return err; + + if (cam->cam_mode[sd->gspca_dev.curr_mode].priv) + err = stv06xx_write_bridge(sd, 0x1505, 0x0f); + else + err = stv06xx_write_bridge(sd, 0x1505, 0x02); + if (err < 0) + return err; + + err = st6422_set_brightness(&sd->gspca_dev, + sensor_settings[BRIGHTNESS_IDX]); + if (err < 0) + return err; + + err = st6422_set_contrast(&sd->gspca_dev, + sensor_settings[CONTRAST_IDX]); + if (err < 0) + return err; + + err = st6422_set_exposure(&sd->gspca_dev, + sensor_settings[EXPOSURE_IDX]); + if (err < 0) + return err; + + err = st6422_set_gain(&sd->gspca_dev, + sensor_settings[GAIN_IDX]); + if (err < 0) + return err; + + PDEBUG(D_STREAM, "Starting stream"); + + return 0; +} + +static int st6422_stop(struct sd *sd) +{ + PDEBUG(D_STREAM, "Halting stream"); + + return 0; +} + +static int st6422_get_brightness(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; + + *val = sensor_settings[BRIGHTNESS_IDX]; + + PDEBUG(D_V4L2, "Read brightness %d", *val); + + return 0; +} + +static int st6422_set_brightness(struct gspca_dev *gspca_dev, __s32 val) +{ + int err; + struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; + + sensor_settings[BRIGHTNESS_IDX] = val; + + if (!gspca_dev->streaming) + return 0; + + /* val goes from 0 -> 31 */ + PDEBUG(D_V4L2, "Set brightness to %d", val); + err = stv06xx_write_bridge(sd, 0x1432, val); + if (err < 0) + return err; + + /* commit settings */ + err = stv06xx_write_bridge(sd, 0x143f, 0x01); + return (err < 0) ? err : 0; +} + +static int st6422_get_contrast(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; + + *val = sensor_settings[CONTRAST_IDX]; + + PDEBUG(D_V4L2, "Read contrast %d", *val); + + return 0; +} + +static int st6422_set_contrast(struct gspca_dev *gspca_dev, __s32 val) +{ + int err; + struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; + + sensor_settings[CONTRAST_IDX] = val; + + if (!gspca_dev->streaming) + return 0; + + /* Val goes from 0 -> 15 */ + PDEBUG(D_V4L2, "Set contrast to %d\n", val); + err = stv06xx_write_bridge(sd, 0x143a, 0xf0 | val); + if (err < 0) + return err; + + /* commit settings */ + err = stv06xx_write_bridge(sd, 0x143f, 0x01); + return (err < 0) ? err : 0; +} + +static int st6422_get_gain(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; + + *val = sensor_settings[GAIN_IDX]; + + PDEBUG(D_V4L2, "Read gain %d", *val); + + return 0; +} + +static int st6422_set_gain(struct gspca_dev *gspca_dev, __s32 val) +{ + int err; + struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; + + sensor_settings[GAIN_IDX] = val; + + if (!gspca_dev->streaming) + return 0; + + PDEBUG(D_V4L2, "Set gain to %d", val); + + /* Set red, green, blue, gain */ + err = stv06xx_write_bridge(sd, 0x0509, val); + if (err < 0) + return err; + + err = stv06xx_write_bridge(sd, 0x050a, val); + if (err < 0) + return err; + + err = stv06xx_write_bridge(sd, 0x050b, val); + if (err < 0) + return err; + + /* 2 mystery writes */ + err = stv06xx_write_bridge(sd, 0x050c, 0x2a); + if (err < 0) + return err; + + err = stv06xx_write_bridge(sd, 0x050d, 0x01); + if (err < 0) + return err; + + /* commit settings */ + err = stv06xx_write_bridge(sd, 0x143f, 0x01); + return (err < 0) ? err : 0; +} + +static int st6422_get_exposure(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; + + *val = sensor_settings[EXPOSURE_IDX]; + + PDEBUG(D_V4L2, "Read exposure %d", *val); + + return 0; +} + +static int st6422_set_exposure(struct gspca_dev *gspca_dev, __s32 val) +{ + int err; + struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; + + sensor_settings[EXPOSURE_IDX] = val; + + if (!gspca_dev->streaming) + return 0; + + PDEBUG(D_V4L2, "Set exposure to %d\n", val); + err = stv06xx_write_bridge(sd, 0x143d, val & 0xff); + if (err < 0) + return err; + + err = stv06xx_write_bridge(sd, 0x143e, val >> 8); + if (err < 0) + return err; + + /* commit settings */ + err = stv06xx_write_bridge(sd, 0x143f, 0x01); + return (err < 0) ? err : 0; +} diff --git a/linux/drivers/media/video/gspca/stv06xx/stv06xx_st6422.h b/linux/drivers/media/video/gspca/stv06xx/stv06xx_st6422.h new file mode 100644 index 000000000..b2d45fe50 --- /dev/null +++ b/linux/drivers/media/video/gspca/stv06xx/stv06xx_st6422.h @@ -0,0 +1,59 @@ +/* + * Support for the sensor part which is integrated (I think) into the + * st6422 stv06xx alike bridge, as its integrated there are no i2c writes + * but instead direct bridge writes. + * + * Copyright (c) 2009 Hans de Goede <hdegoede@redhat.com> + * + * Strongly based on qc-usb-messenger, which is: + * Copyright (c) 2001 Jean-Fredric Clere, Nikolas Zimmermann, Georg Acher + * Mark Cave-Ayland, Carlo E Prelz, Dick Streefland + * Copyright (c) 2002, 2003 Tuukka Toivonen + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef STV06XX_ST6422_H_ +#define STV06XX_ST6422_H_ + +#include "stv06xx_sensor.h" + +static int st6422_probe(struct sd *sd); +static int st6422_start(struct sd *sd); +static int st6422_init(struct sd *sd); +static int st6422_stop(struct sd *sd); +static void st6422_disconnect(struct sd *sd); + +/* V4L2 controls supported by the driver */ +static int st6422_get_brightness(struct gspca_dev *gspca_dev, __s32 *val); +static int st6422_set_brightness(struct gspca_dev *gspca_dev, __s32 val); +static int st6422_get_contrast(struct gspca_dev *gspca_dev, __s32 *val); +static int st6422_set_contrast(struct gspca_dev *gspca_dev, __s32 val); +static int st6422_get_gain(struct gspca_dev *gspca_dev, __s32 *val); +static int st6422_set_gain(struct gspca_dev *gspca_dev, __s32 val); +static int st6422_get_exposure(struct gspca_dev *gspca_dev, __s32 *val); +static int st6422_set_exposure(struct gspca_dev *gspca_dev, __s32 val); + +const struct stv06xx_sensor stv06xx_sensor_st6422 = { + .name = "ST6422", + .init = st6422_init, + .probe = st6422_probe, + .start = st6422_start, + .stop = st6422_stop, + .disconnect = st6422_disconnect, +}; + +#endif diff --git a/linux/drivers/media/video/ivtv/ivtv-controls.c b/linux/drivers/media/video/ivtv/ivtv-controls.c index 84995bcf4..a3b77ed3f 100644 --- a/linux/drivers/media/video/ivtv/ivtv-controls.c +++ b/linux/drivers/media/video/ivtv/ivtv-controls.c @@ -60,6 +60,8 @@ int ivtv_queryctrl(struct file *file, void *fh, struct v4l2_queryctrl *qctrl) switch (qctrl->id) { /* Standard V4L2 controls */ + case V4L2_CID_USER_CLASS: + return v4l2_ctrl_query_fill(qctrl, 0, 0, 0, 0); case V4L2_CID_BRIGHTNESS: case V4L2_CID_HUE: case V4L2_CID_SATURATION: diff --git a/linux/drivers/media/video/ov511.c b/linux/drivers/media/video/ov511.c index 2839546b1..346eed722 100644 --- a/linux/drivers/media/video/ov511.c +++ b/linux/drivers/media/video/ov511.c @@ -211,8 +211,6 @@ static const int i2c_detect_tries = 5; static struct usb_device_id device_table [] = { { USB_DEVICE(VEND_OMNIVISION, PROD_OV511) }, { USB_DEVICE(VEND_OMNIVISION, PROD_OV511PLUS) }, - { USB_DEVICE(VEND_OMNIVISION, PROD_OV518) }, - { USB_DEVICE(VEND_OMNIVISION, PROD_OV518PLUS) }, { USB_DEVICE(VEND_MATTEL, PROD_ME2CAM) }, { } /* Terminating entry */ }; diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-audio.c b/linux/drivers/media/video/pvrusb2/pvrusb2-audio.c index 86049f412..42875ec21 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-audio.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-audio.c @@ -49,11 +49,13 @@ static const int routing_scheme0[] = { MSP_DSP_IN_SCART), }; -static const struct routing_scheme routing_schemes[] = { - [PVR2_ROUTING_SCHEME_HAUPPAUGE] = { - .def = routing_scheme0, - .cnt = ARRAY_SIZE(routing_scheme0), - }, +static const struct routing_scheme routing_def0 = { + .def = routing_scheme0, + .cnt = ARRAY_SIZE(routing_scheme0), +}; + +static const struct routing_scheme *routing_schemes[] = { + [PVR2_ROUTING_SCHEME_HAUPPAUGE] = &routing_def0, }; void pvr2_msp3400_subdev_update(struct pvr2_hdw *hdw, struct v4l2_subdev *sd) @@ -66,7 +68,7 @@ void pvr2_msp3400_subdev_update(struct pvr2_hdw *hdw, struct v4l2_subdev *sd) pvr2_trace(PVR2_TRACE_CHIPS, "subdev msp3400 v4l2 set_stereo"); if ((sid < ARRAY_SIZE(routing_schemes)) && - ((sp = routing_schemes + sid) != NULL) && + ((sp = routing_schemes[sid]) != NULL) && (hdw->input_val >= 0) && (hdw->input_val < sp->cnt)) { input = sp->def[hdw->input_val]; diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-cs53l32a.c b/linux/drivers/media/video/pvrusb2/pvrusb2-cs53l32a.c index f99b77c92..7bcaa0cd4 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-cs53l32a.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-cs53l32a.c @@ -50,11 +50,13 @@ static const int routing_scheme1[] = { [PVR2_CVAL_INPUT_SVIDEO] = 0, }; -static const struct routing_scheme routing_schemes[] = { - [PVR2_ROUTING_SCHEME_ONAIR] = { - .def = routing_scheme1, - .cnt = ARRAY_SIZE(routing_scheme1), - }, +static const struct routing_scheme routing_def1 = { + .def = routing_scheme1, + .cnt = ARRAY_SIZE(routing_scheme1), +}; + +static const struct routing_scheme *routing_schemes[] = { + [PVR2_ROUTING_SCHEME_ONAIR] = &routing_def1, }; @@ -66,12 +68,11 @@ void pvr2_cs53l32a_subdev_update(struct pvr2_hdw *hdw, struct v4l2_subdev *sd) u32 input; pvr2_trace(PVR2_TRACE_CHIPS, "subdev v4l2 set_input(%d)", hdw->input_val); - if ((sid < ARRAY_SIZE(routing_schemes)) && - ((sp = routing_schemes + sid) != NULL) && - (hdw->input_val >= 0) && - (hdw->input_val < sp->cnt)) { - input = sp->def[hdw->input_val]; - } else { + sp = (sid < ARRAY_SIZE(routing_schemes)) ? + routing_schemes[sid] : NULL; + if ((sp == NULL) || + (hdw->input_val < 0) || + (hdw->input_val >= sp->cnt)) { pvr2_trace(PVR2_TRACE_ERROR_LEGS, "*** WARNING *** subdev v4l2 set_input:" " Invalid routing scheme (%u)" @@ -79,6 +80,7 @@ void pvr2_cs53l32a_subdev_update(struct pvr2_hdw *hdw, struct v4l2_subdev *sd) sid, hdw->input_val); return; } + input = sp->def[hdw->input_val]; sd->ops->audio->s_routing(sd, input, 0, 0); } } diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c b/linux/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c index ef47c3874..e15d7fb6c 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c @@ -69,6 +69,11 @@ static const struct routing_scheme_item routing_scheme0[] = { }, }; +static const struct routing_scheme routing_def0 = { + .def = routing_scheme0, + .cnt = ARRAY_SIZE(routing_scheme0), +}; + /* Specific to gotview device */ static const struct routing_scheme_item routing_schemegv[] = { [PVR2_CVAL_INPUT_TV] = { @@ -91,15 +96,14 @@ static const struct routing_scheme_item routing_schemegv[] = { }, }; -static const struct routing_scheme routing_schemes[] = { - [PVR2_ROUTING_SCHEME_HAUPPAUGE] = { - .def = routing_scheme0, - .cnt = ARRAY_SIZE(routing_scheme0), - }, - [PVR2_ROUTING_SCHEME_GOTVIEW] = { - .def = routing_schemegv, - .cnt = ARRAY_SIZE(routing_schemegv), - }, +static const struct routing_scheme routing_defgv = { + .def = routing_schemegv, + .cnt = ARRAY_SIZE(routing_schemegv), +}; + +static const struct routing_scheme *routing_schemes[] = { + [PVR2_ROUTING_SCHEME_HAUPPAUGE] = &routing_def0, + [PVR2_ROUTING_SCHEME_GOTVIEW] = &routing_defgv, }; void pvr2_cx25840_subdev_update(struct pvr2_hdw *hdw, struct v4l2_subdev *sd) @@ -111,13 +115,11 @@ void pvr2_cx25840_subdev_update(struct pvr2_hdw *hdw, struct v4l2_subdev *sd) const struct routing_scheme *sp; unsigned int sid = hdw->hdw_desc->signal_routing_scheme; - if ((sid < ARRAY_SIZE(routing_schemes)) && - ((sp = routing_schemes + sid) != NULL) && - (hdw->input_val >= 0) && - (hdw->input_val < sp->cnt)) { - vid_input = sp->def[hdw->input_val].vid; - aud_input = sp->def[hdw->input_val].aud; - } else { + sp = (sid < ARRAY_SIZE(routing_schemes)) ? + routing_schemes[sid] : NULL; + if ((sp == NULL) || + (hdw->input_val < 0) || + (hdw->input_val >= sp->cnt)) { pvr2_trace(PVR2_TRACE_ERROR_LEGS, "*** WARNING *** subdev cx2584x set_input:" " Invalid routing scheme (%u)" @@ -125,7 +127,8 @@ void pvr2_cx25840_subdev_update(struct pvr2_hdw *hdw, struct v4l2_subdev *sd) sid, hdw->input_val); return; } - + vid_input = sp->def[hdw->input_val].vid; + aud_input = sp->def[hdw->input_val].aud; pvr2_trace(PVR2_TRACE_CHIPS, "subdev cx2584x set_input vid=0x%x aud=0x%x", vid_input, aud_input); diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c index 3411cb36a..0bbd20f13 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c @@ -86,8 +86,8 @@ MODULE_PARM_DESC(video_std,"specify initial video standard"); module_param_array(tolerance, int, NULL, 0444); MODULE_PARM_DESC(tolerance,"specify stream error tolerance"); -/* US Broadcast channel 7 (175.25 MHz) */ -static int default_tv_freq = 175250000L; +/* US Broadcast channel 3 (61.25 MHz), to help with testing */ +static int default_tv_freq = 61250000L; /* 104.3 MHz, a usable FM station for my area */ static int default_radio_freq = 104300000L; @@ -2004,6 +2004,34 @@ static unsigned int pvr2_copy_i2c_addr_list( } +static void pvr2_hdw_cx25840_vbi_hack(struct pvr2_hdw *hdw) +{ + /* + Mike Isely <isely@pobox.com> 19-Nov-2006 - This bit of nuttiness + for cx25840 causes that module to correctly set up its video + scaling. This is really a problem in the cx25840 module itself, + but we work around it here. The problem has not been seen in + ivtv because there VBI is supported and set up. We don't do VBI + here (at least not yet) and thus we never attempted to even set + it up. + */ + struct v4l2_format fmt; + if (hdw->decoder_client_id != PVR2_CLIENT_ID_CX25840) { + /* We're not using a cx25840 so don't enable the hack */ + return; + } + + pvr2_trace(PVR2_TRACE_INIT, + "Module ID %u:" + " Executing cx25840 VBI hack", + hdw->decoder_client_id); + memset(&fmt, 0, sizeof(fmt)); + fmt.type = V4L2_BUF_TYPE_SLICED_VBI_CAPTURE; + v4l2_device_call_all(&hdw->v4l2_dev, hdw->decoder_client_id, + video, s_fmt, &fmt); +} + + static int pvr2_hdw_load_subdev(struct pvr2_hdw *hdw, const struct pvr2_device_client_desc *cd) { @@ -2095,30 +2123,6 @@ static int pvr2_hdw_load_subdev(struct pvr2_hdw *hdw, /* client-specific setup... */ switch (mid) { case PVR2_CLIENT_ID_CX25840: - hdw->decoder_client_id = mid; - { - /* - Mike Isely <isely@pobox.com> 19-Nov-2006 - This - bit of nuttiness for cx25840 causes that module - to correctly set up its video scaling. This is - really a problem in the cx25840 module itself, - but we work around it here. The problem has not - been seen in ivtv because there VBI is supported - and set up. We don't do VBI here (at least not - yet) and thus we never attempted to even set it - up. - */ - struct v4l2_format fmt; - pvr2_trace(PVR2_TRACE_INIT, - "Module ID %u:" - " Executing cx25840 VBI hack", - mid); - memset(&fmt, 0, sizeof(fmt)); - fmt.type = V4L2_BUF_TYPE_SLICED_VBI_CAPTURE; - v4l2_device_call_all(&hdw->v4l2_dev, mid, - video, s_fmt, &fmt); - } - break; case PVR2_CLIENT_ID_SAA7115: hdw->decoder_client_id = mid; break; @@ -2219,6 +2223,8 @@ static void pvr2_hdw_setup_low(struct pvr2_hdw *hdw) cptr->info->set_value(cptr,~0,cptr->info->default_value); } + pvr2_hdw_cx25840_vbi_hack(hdw); + /* Set up special default values for the television and radio frequencies here. It's not really important what these defaults are, but I set them to something usable in the Chicago area just @@ -2976,6 +2982,7 @@ static void pvr2_subdev_update(struct pvr2_hdw *hdw) vs = hdw->std_mask_cur; v4l2_device_call_all(&hdw->v4l2_dev, 0, core, s_std, vs); + pvr2_hdw_cx25840_vbi_hack(hdw); } hdw->tuner_signal_stale = !0; hdw->cropcap_stale = !0; @@ -4170,6 +4177,7 @@ int pvr2_hdw_cmd_decoder_reset(struct pvr2_hdw *hdw) if (hdw->decoder_client_id) { v4l2_device_call_all(&hdw->v4l2_dev, hdw->decoder_client_id, core, reset, 0); + pvr2_hdw_cx25840_vbi_hack(hdw); return 0; } pvr2_trace(PVR2_TRACE_INIT, diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-video-v4l.c b/linux/drivers/media/video/pvrusb2/pvrusb2-video-v4l.c index d240331a6..4be489c45 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-video-v4l.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-video-v4l.c @@ -55,6 +55,11 @@ static const int routing_scheme0[] = { [PVR2_CVAL_INPUT_SVIDEO] = SAA7115_SVIDEO2, }; +static const struct routing_scheme routing_def0 = { + .def = routing_scheme0, + .cnt = ARRAY_SIZE(routing_scheme0), +}; + static const int routing_scheme1[] = { [PVR2_CVAL_INPUT_TV] = SAA7115_COMPOSITE4, [PVR2_CVAL_INPUT_RADIO] = SAA7115_COMPOSITE5, @@ -62,15 +67,14 @@ static const int routing_scheme1[] = { [PVR2_CVAL_INPUT_SVIDEO] = SAA7115_SVIDEO2, /* or SVIDEO0, it seems */ }; -static const struct routing_scheme routing_schemes[] = { - [PVR2_ROUTING_SCHEME_HAUPPAUGE] = { - .def = routing_scheme0, - .cnt = ARRAY_SIZE(routing_scheme0), - }, - [PVR2_ROUTING_SCHEME_ONAIR] = { - .def = routing_scheme1, - .cnt = ARRAY_SIZE(routing_scheme1), - }, +static const struct routing_scheme routing_def1 = { + .def = routing_scheme1, + .cnt = ARRAY_SIZE(routing_scheme1), +}; + +static const struct routing_scheme *routing_schemes[] = { + [PVR2_ROUTING_SCHEME_HAUPPAUGE] = &routing_def0, + [PVR2_ROUTING_SCHEME_ONAIR] = &routing_def1, }; void pvr2_saa7115_subdev_update(struct pvr2_hdw *hdw, struct v4l2_subdev *sd) @@ -82,12 +86,12 @@ void pvr2_saa7115_subdev_update(struct pvr2_hdw *hdw, struct v4l2_subdev *sd) pvr2_trace(PVR2_TRACE_CHIPS, "subdev v4l2 set_input(%d)", hdw->input_val); - if ((sid < ARRAY_SIZE(routing_schemes)) && - ((sp = routing_schemes + sid) != NULL) && - (hdw->input_val >= 0) && - (hdw->input_val < sp->cnt)) { - input = sp->def[hdw->input_val]; - } else { + + sp = (sid < ARRAY_SIZE(routing_schemes)) ? + routing_schemes[sid] : NULL; + if ((sp == NULL) || + (hdw->input_val < 0) || + (hdw->input_val >= sp->cnt)) { pvr2_trace(PVR2_TRACE_ERROR_LEGS, "*** WARNING *** subdev v4l2 set_input:" " Invalid routing scheme (%u)" @@ -95,6 +99,7 @@ void pvr2_saa7115_subdev_update(struct pvr2_hdw *hdw, struct v4l2_subdev *sd) sid, hdw->input_val); return; } + input = sp->def[hdw->input_val]; sd->ops->video->s_routing(sd, input, 0, 0); } } diff --git a/linux/drivers/media/video/pxa_camera.c b/linux/drivers/media/video/pxa_camera.c index 7578d0cfe..8964d60cd 100644 --- a/linux/drivers/media/video/pxa_camera.c +++ b/linux/drivers/media/video/pxa_camera.c @@ -1403,13 +1403,15 @@ static int pxa_camera_try_fmt(struct soc_camera_device *icd, return -EINVAL; } - /* Limit to pxa hardware capabilities. YUV422P planar format requires + /* + * Limit to pxa hardware capabilities. YUV422P planar format requires * images size to be a multiple of 16 bytes. If not, zeros will be * inserted between Y and U planes, and U and V planes, which violates - * the YUV422P standard. */ - v4l2_bound_align_image(&pix->width, 48, 2048, 1, - &pix->height, 32, 2048, 0, - xlate->host_fmt->fourcc == V4L2_PIX_FMT_YUV422P ? 4 : 0); + * the YUV422P standard. + */ + v4l_bound_align_image(&pix->width, 48, 2048, 1, + &pix->height, 32, 2048, 0, + xlate->host_fmt->fourcc == V4L2_PIX_FMT_YUV422P ? 4 : 0); pix->bytesperline = pix->width * DIV_ROUND_UP(xlate->host_fmt->depth, 8); diff --git a/linux/drivers/media/video/tcm825x.c b/linux/drivers/media/video/tcm825x.c index 559ade821..d778c41ef 100644 --- a/linux/drivers/media/video/tcm825x.c +++ b/linux/drivers/media/video/tcm825x.c @@ -882,7 +882,7 @@ static int tcm825x_probe(struct i2c_client *client, return rval; } -static int __exit tcm825x_remove(struct i2c_client *client) +static int tcm825x_remove(struct i2c_client *client) { struct tcm825x_sensor *sensor = i2c_get_clientdata(client); @@ -908,7 +908,7 @@ static struct i2c_driver tcm825x_i2c_driver = { .name = TCM825X_NAME, }, .probe = tcm825x_probe, - .remove = __exit_p(tcm825x_remove), + .remove = tcm825x_remove, #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26) .id_table = tcm825x_id, #endif diff --git a/linux/drivers/media/video/usbvideo/Kconfig b/linux/drivers/media/video/usbvideo/Kconfig index e4cb99c1f..adb1c044a 100644 --- a/linux/drivers/media/video/usbvideo/Kconfig +++ b/linux/drivers/media/video/usbvideo/Kconfig @@ -38,10 +38,13 @@ config USB_KONICAWC module will be called konicawc. config USB_QUICKCAM_MESSENGER - tristate "USB Logitech Quickcam Messenger" + tristate "USB Logitech Quickcam Messenger (DEPRECATED)" depends on VIDEO_V4L1 select VIDEO_USBVIDEO ---help--- + This driver is DEPRECATED please use the gspca stv06xx module + instead. + Say Y or M here to enable support for the USB Logitech Quickcam Messenger webcam. diff --git a/linux/drivers/media/video/v4l2-common.c b/linux/drivers/media/video/v4l2-common.c index ba3790a98..ab91734b1 100644 --- a/linux/drivers/media/video/v4l2-common.c +++ b/linux/drivers/media/video/v4l2-common.c @@ -868,6 +868,17 @@ struct v4l2_subdev *v4l2_i2c_new_subdev(struct v4l2_device *v4l2_dev, /* Decrease the module use count to match the first try_module_get. */ module_put(client->driver->driver.owner); + if (sd) { + /* We return errors from v4l2_subdev_call only if we have the + callback as the .s_config is not mandatory */ + int err = v4l2_subdev_call(sd, core, s_config, 0, NULL); + + if (err && err != -ENOIOCTLCMD) { + v4l2_device_unregister_subdev(sd); + sd = NULL; + } + } + error: #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26) /* If we have a client but no subdev, then something went wrong and @@ -931,6 +942,17 @@ struct v4l2_subdev *v4l2_i2c_new_probed_subdev(struct v4l2_device *v4l2_dev, /* Decrease the module use count to match the first try_module_get. */ module_put(client->driver->driver.owner); + if (sd) { + /* We return errors from v4l2_subdev_call only if we have the + callback as the .s_config is not mandatory */ + int err = v4l2_subdev_call(sd, core, s_config, 0, NULL); + + if (err && err != -ENOIOCTLCMD) { + v4l2_device_unregister_subdev(sd); + sd = NULL; + } + } + error: #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26) /* If we have a client but no subdev, then something went wrong and @@ -953,6 +975,150 @@ struct v4l2_subdev *v4l2_i2c_new_probed_subdev_addr(struct v4l2_device *v4l2_dev } EXPORT_SYMBOL_GPL(v4l2_i2c_new_probed_subdev_addr); +/* Load an i2c sub-device. */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26) +struct v4l2_subdev *v4l2_i2c_new_subdev_board(struct v4l2_device *v4l2_dev, + struct i2c_adapter *adapter, const char *module_name, + struct i2c_board_info *info, const unsigned short *probe_addrs) +{ + struct v4l2_subdev *sd = NULL; + struct i2c_client *client; + + BUG_ON(!v4l2_dev); + + if (module_name) + request_module(module_name); + + /* Create the i2c client */ + if (info->addr == 0 && probe_addrs) + client = i2c_new_probed_device(adapter, info, probe_addrs); + else + client = i2c_new_device(adapter, info); + + /* Note: by loading the module first we are certain that c->driver + will be set if the driver was found. If the module was not loaded + first, then the i2c core tries to delay-load the module for us, + and then c->driver is still NULL until the module is finally + loaded. This delay-load mechanism doesn't work if other drivers + want to use the i2c device, so explicitly loading the module + is the best alternative. */ + if (client == NULL || client->driver == NULL) + goto error; + + /* Lock the module so we can safely get the v4l2_subdev pointer */ + if (!try_module_get(client->driver->driver.owner)) + goto error; + sd = i2c_get_clientdata(client); + + /* Register with the v4l2_device which increases the module's + use count as well. */ + if (v4l2_device_register_subdev(v4l2_dev, sd)) + sd = NULL; + /* Decrease the module use count to match the first try_module_get. */ + module_put(client->driver->driver.owner); + + if (sd) { + /* We return errors from v4l2_subdev_call only if we have the + callback as the .s_config is not mandatory */ + int err = v4l2_subdev_call(sd, core, s_config, + info->irq, info->platform_data); + + if (err && err != -ENOIOCTLCMD) { + v4l2_device_unregister_subdev(sd); + sd = NULL; + } + } + +error: + /* If we have a client but no subdev, then something went wrong and + we must unregister the client. */ + if (client && sd == NULL) + i2c_unregister_device(client); + return sd; +} +EXPORT_SYMBOL_GPL(v4l2_i2c_new_subdev_board); + +struct v4l2_subdev *v4l2_i2c_new_subdev_cfg(struct v4l2_device *v4l2_dev, + struct i2c_adapter *adapter, + const char *module_name, const char *client_type, + int irq, void *platform_data, + u8 addr, const unsigned short *probe_addrs) +{ + struct i2c_board_info info; + + /* Setup the i2c board info with the device type and + the device address. */ + memset(&info, 0, sizeof(info)); + strlcpy(info.type, client_type, sizeof(info.type)); + info.addr = addr; + info.irq = irq; + info.platform_data = platform_data; + + return v4l2_i2c_new_subdev_board(v4l2_dev, adapter, module_name, + &info, probe_addrs); +} +EXPORT_SYMBOL_GPL(v4l2_i2c_new_subdev_cfg); +#else +struct v4l2_subdev *v4l2_i2c_new_subdev_cfg(struct v4l2_device *v4l2_dev, + struct i2c_adapter *adapter, + const char *module_name, const char *client_type, + int irq, void *platform_data, + u8 addr, const unsigned short *probe_addrs) +{ + struct v4l2_subdev *sd = NULL; + struct i2c_client *client; + + BUG_ON(!v4l2_dev); + + if (module_name) + request_module(module_name); + + if (addr == 0 && probe_addrs) { + /* Legacy code: loading the module should automatically + probe and create the i2c_client on the adapter. + Try to find the client by walking the adapter's client list + for each of the possible addresses. */ + while (!client && *probe_addrs != I2C_CLIENT_END) + client = v4l2_i2c_legacy_find_client(adapter, *probe_addrs++); + } else { + /* Legacy code: loading the module automatically probes and + creates the i2c_client on the adapter. Try to find the + client by walking the adapter's client list. */ + client = v4l2_i2c_legacy_find_client(adapter, addr); + } + if (client == NULL || client->driver == NULL) + goto error; + + /* Lock the module so we can safely get the v4l2_subdev pointer */ + if (!try_module_get(client->driver->driver.owner)) + goto error; + sd = i2c_get_clientdata(client); + + /* Register with the v4l2_device which increases the module's + use count as well. */ + if (v4l2_device_register_subdev(v4l2_dev, sd)) + sd = NULL; + /* Decrease the module use count to match the first try_module_get. */ + module_put(client->driver->driver.owner); + + if (sd) { + /* We return errors from v4l2_subdev_call only if we have the + callback as the .s_config is not mandatory */ + int err = v4l2_subdev_call(sd, core, s_config, + irq, platform_data); + + if (err && err != -ENOIOCTLCMD) { + v4l2_device_unregister_subdev(sd); + sd = NULL; + } + } + +error: + return sd; +} +EXPORT_SYMBOL_GPL(v4l2_i2c_new_subdev_cfg); +#endif + /* Return i2c client address of v4l2_subdev. */ unsigned short v4l2_i2c_subdev_addr(struct v4l2_subdev *sd) { @@ -1055,10 +1221,13 @@ void v4l_bound_align_image(u32 *w, unsigned int wmin, unsigned int wmax, if (walign + halign < salign) { /* Max walign where there is still a valid width */ unsigned int wmaxa = __fls(wmax ^ (wmin - 1)); + /* Max halign where there is still a valid height */ + unsigned int hmaxa = __fls(hmax ^ (hmin - 1)); /* up the smaller alignment until we have enough */ do { - if (walign <= halign && walign < wmaxa) { + if (halign >= hmaxa || + (walign <= halign && walign < wmaxa)) { *w = clamp_align(*w, wmin, wmax, walign + 1); walign = __ffs(*w); } else { diff --git a/linux/drivers/media/video/v4l2-device.c b/linux/drivers/media/video/v4l2-device.c index 4fa93189d..655d19cd6 100644 --- a/linux/drivers/media/video/v4l2-device.c +++ b/linux/drivers/media/video/v4l2-device.c @@ -86,8 +86,8 @@ void v4l2_device_unregister(struct v4l2_device *v4l2_dev) /* Unregister subdevs */ list_for_each_entry_safe(sd, next, &v4l2_dev->subdevs, list) { v4l2_device_unregister_subdev(sd); -#if defined(CONFIG_I2C) || (defined(CONFIG_I2C_MODULE) && defined(MODULE)) -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 29) +#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26) if (sd->flags & V4L2_SUBDEV_FL_IS_I2C) { struct i2c_client *client = v4l2_get_subdevdata(sd); diff --git a/linux/drivers/media/video/v4l2-ioctl.c b/linux/drivers/media/video/v4l2-ioctl.c index 22b380dfd..625fe7698 100644 --- a/linux/drivers/media/video/v4l2-ioctl.c +++ b/linux/drivers/media/video/v4l2-ioctl.c @@ -1718,24 +1718,29 @@ static long __video_do_ioctl(struct file *file, ret = ops->vidioc_enum_framesizes(file, fh, p); dbgarg(cmd, - "index=%d, pixelformat=%d, type=%d ", - p->index, p->pixel_format, p->type); + "index=%d, pixelformat=%c%c%c%c, type=%d ", + p->index, + (p->pixel_format & 0xff), + (p->pixel_format >> 8) & 0xff, + (p->pixel_format >> 16) & 0xff, + (p->pixel_format >> 24) & 0xff, + p->type); switch (p->type) { case V4L2_FRMSIZE_TYPE_DISCRETE: - dbgarg2("width = %d, height=%d\n", + printk("width = %d, height=%d\n", p->discrete.width, p->discrete.height); break; case V4L2_FRMSIZE_TYPE_STEPWISE: - dbgarg2("min %dx%d, max %dx%d, step %dx%d\n", + printk("min %dx%d, max %dx%d, step %dx%d\n", p->stepwise.min_width, p->stepwise.min_height, p->stepwise.step_width, p->stepwise.step_height, p->stepwise.max_width, p->stepwise.max_height); break; case V4L2_FRMSIZE_TYPE_CONTINUOUS: - dbgarg2("continuous\n"); + printk("continuous\n"); break; default: - dbgarg2("- Unknown type!\n"); + printk("- Unknown type!\n"); } break; diff --git a/linux/include/linux/videodev2.h b/linux/include/linux/videodev2.h index a018bb458..699ea62a7 100644 --- a/linux/include/linux/videodev2.h +++ b/linux/include/linux/videodev2.h @@ -348,6 +348,7 @@ struct v4l2_pix_format { #define V4L2_PIX_FMT_SQ905C v4l2_fourcc('9', '0', '5', 'C') /* compressed RGGB bayer */ #define V4L2_PIX_FMT_PJPG v4l2_fourcc('P', 'J', 'P', 'G') /* Pixart 73xx JPEG */ #define V4L2_PIX_FMT_YVYU v4l2_fourcc('Y', 'V', 'Y', 'U') /* 16 YVU 4:2:2 */ +#define V4L2_PIX_FMT_OV511 v4l2_fourcc('O', '5', '1', '1') /* ov511 JPEG */ #define V4L2_PIX_FMT_OV518 v4l2_fourcc('O', '5', '1', '8') /* ov518 JPEG */ /* @@ -894,9 +895,10 @@ enum v4l2_colorfx { V4L2_COLORFX_BW = 1, V4L2_COLORFX_SEPIA = 2, }; +#define V4L2_CID_AUTOBRIGHTNESS (V4L2_CID_BASE+32) /* last CID + 1 */ -#define V4L2_CID_LASTP1 (V4L2_CID_BASE+32) +#define V4L2_CID_LASTP1 (V4L2_CID_BASE+33) /* MPEG-class control IDs defined by V4L2 */ #define V4L2_CID_MPEG_BASE (V4L2_CTRL_CLASS_MPEG | 0x900) diff --git a/linux/include/media/ir-common.h b/linux/include/media/ir-common.h index 7b5b91f60..9dcb632f6 100644 --- a/linux/include/media/ir-common.h +++ b/linux/include/media/ir-common.h @@ -162,6 +162,8 @@ extern IR_KEYTAB_TYPE ir_codes_ati_tv_wonder_hd_600[IR_KEYTAB_SIZE]; extern IR_KEYTAB_TYPE ir_codes_kworld_plus_tv_analog[IR_KEYTAB_SIZE]; extern IR_KEYTAB_TYPE ir_codes_kaiomy[IR_KEYTAB_SIZE]; extern IR_KEYTAB_TYPE ir_codes_dm1105_nec[IR_KEYTAB_SIZE]; +extern IR_KEYTAB_TYPE ir_codes_evga_indtube[IR_KEYTAB_SIZE]; + #endif /* diff --git a/linux/include/media/v4l2-common.h b/linux/include/media/v4l2-common.h index 54728d4c0..831c4c038 100644 --- a/linux/include/media/v4l2-common.h +++ b/linux/include/media/v4l2-common.h @@ -158,6 +158,24 @@ struct v4l2_subdev *v4l2_i2c_new_probed_subdev(struct v4l2_device *v4l2_dev, struct v4l2_subdev *v4l2_i2c_new_probed_subdev_addr(struct v4l2_device *v4l2_dev, struct i2c_adapter *adapter, const char *module_name, const char *client_type, u8 addr); + +/* Load an i2c module and return an initialized v4l2_subdev struct. + Only call request_module if module_name != NULL. + The client_type argument is the name of the chip that's on the adapter. */ +struct v4l2_subdev *v4l2_i2c_new_subdev_cfg(struct v4l2_device *v4l2_dev, + struct i2c_adapter *adapter, + const char *module_name, const char *client_type, + int irq, void *platform_data, + u8 addr, const unsigned short *probe_addrs); + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26) +struct i2c_board_info; + +struct v4l2_subdev *v4l2_i2c_new_subdev_board(struct v4l2_device *v4l2_dev, + struct i2c_adapter *adapter, const char *module_name, + struct i2c_board_info *info, const unsigned short *probe_addrs); +#endif + /* Initialize an v4l2_subdev with data from an i2c_client struct */ void v4l2_i2c_subdev_init(struct v4l2_subdev *sd, struct i2c_client *client, const struct v4l2_subdev_ops *ops); diff --git a/linux/include/media/v4l2-i2c-drv.h b/linux/include/media/v4l2-i2c-drv.h index 121f0ada0..f5e4ffe27 100644 --- a/linux/include/media/v4l2-i2c-drv.h +++ b/linux/include/media/v4l2-i2c-drv.h @@ -22,7 +22,7 @@ */ /* NOTE: the full version of this header is in the v4l-dvb repository - * and allows v4l i2c drivers to be compiled on older kernels as well. + * and allows v4l i2c drivers to be compiled on pre-2.6.26 kernels. * The version of this header as it appears in the kernel is a stripped * version (without all the backwards compatibility stuff) and so it * looks a bit odd. @@ -30,6 +30,9 @@ * If you look at the full version then you will understand the reason * for introducing this header since you really don't want to have all * the tricky backwards compatibility code in each and every i2c driver. + * + * If the i2c driver will never be compiled for pre-2.6.26 kernels, then + * DO NOT USE this header! Just write it as a regular i2c driver. */ #ifndef __V4L2_I2C_DRV_H__ diff --git a/linux/include/media/v4l2-subdev.h b/linux/include/media/v4l2-subdev.h index a503e1cee..5dcb36785 100644 --- a/linux/include/media/v4l2-subdev.h +++ b/linux/include/media/v4l2-subdev.h @@ -79,7 +79,11 @@ struct v4l2_decode_vbi_line { not yet implemented) since ops provide proper type-checking. */ -/* init: initialize the sensor registors to some sort of reasonable default +/* s_config: if set, then it is always called by the v4l2_i2c_new_subdev* + functions after the v4l2_subdev was registered. It is used to pass + platform data to the subdev which can be used during initialization. + + init: initialize the sensor registors to some sort of reasonable default values. Do not use for new drivers and should be removed in existing drivers. @@ -96,6 +100,7 @@ struct v4l2_decode_vbi_line { struct v4l2_subdev_core_ops { int (*g_chip_ident)(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip); int (*log_status)(struct v4l2_subdev *sd); + int (*s_config)(struct v4l2_subdev *sd, int irq, void *platform_data); int (*init)(struct v4l2_subdev *sd, u32 val); int (*load_fw)(struct v4l2_subdev *sd); int (*reset)(struct v4l2_subdev *sd, u32 val); diff --git a/v4l/compat.h b/v4l/compat.h index ce28da615..56eb30853 100644 --- a/v4l/compat.h +++ b/v4l/compat.h @@ -146,6 +146,12 @@ do { \ ((dev->bus && 0 == strcmp(dev->bus->name, "i2c")) ? to_i2c_client(dev) : NULL) #endif +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31) +/* Construct an I2C_CLIENT_END-terminated array of i2c addresses */ +#define I2C_ADDRS(addr, addrs...) \ + ((const unsigned short []){ addr, ## addrs, I2C_CLIENT_END }) +#endif + #ifndef USB_DEVICE_AND_INTERFACE_INFO # define USB_DEVICE_AND_INTERFACE_INFO(vend,prod,cl,sc,pr) \ .match_flags = USB_DEVICE_ID_MATCH_INT_INFO \ @@ -283,10 +289,14 @@ static inline int dummy_algo_control(struct i2c_adapter *adapter, }) #endif -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 28) +#ifdef NEED_SND_BUG_ON #define snd_BUG_ON(cond) WARN((cond), "BUG? (%s)\n", __stringify(cond)) #endif +#ifdef NEED_BITOPS +#define BIT(x) (1UL<<((x)%BITS_PER_LONG)) +#endif + #ifndef PCI_DEVICE_ID_MARVELL_88ALP01_CCIC #define PCI_DEVICE_ID_MARVELL_88ALP01_CCIC 0x4102 #endif @@ -462,6 +472,7 @@ static inline int snd_card_create(int idx, const char *id, defined(__x86_64__) || \ (BITS_PER_LONG == 64 && LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26))) /* This define will prevent breakage if __fls was already defined. */ +#undef __fls #define __fls v4l_compat_fls static inline unsigned long v4l_compat_fls(unsigned long x) { diff --git a/v4l/scripts/make_config_compat.pl b/v4l/scripts/make_config_compat.pl index 3c2b623ca..7c7841459 100755 --- a/v4l/scripts/make_config_compat.pl +++ b/v4l/scripts/make_config_compat.pl @@ -280,9 +280,7 @@ sub check_snd_card_create() sub check_poll_schedule() { - - - my @files = ( "$kdir//include/linux/poll.h" ); + my @files = ( "$kdir/include/linux/poll.h" ); foreach my $file ( @files ) { open IN, "<$file" or die "File not found: $file"; @@ -300,6 +298,46 @@ sub check_poll_schedule() $out.= "\n#define NEED_POLL_SCHEDULE 1\n"; } +sub check_snd_BUG_ON() +{ + my @files = ( "$kdir/include/sound/core.h" ); + + foreach my $file ( @files ) { + open IN, "<$file" or die "File not found: $file"; + while (<IN>) { + if (m/snd_BUG_ON/) { + close IN; + # definition found. No need for compat + return; + } + } + close IN; + } + + # definition not found. This means that we need compat + $out.= "\n#define NEED_SND_BUG_ON 1\n"; +} + +sub check_bitops() +{ + my @files = ( "$kdir/include/linux/bitops.h" ); + + foreach my $file ( @files ) { + open IN, "<$file" or next; + while (<IN>) { + if (m/#define\s+BIT\(/) { + close IN; + # definition found. No need for compat + return; + } + } + close IN; + } + + # definition not found. This means that we need compat + $out.= "\n#define NEED_BITOPS 1\n"; +} + sub check_other_dependencies() { check_spin_lock(); @@ -317,6 +355,8 @@ sub check_other_dependencies() check_pci_ioremap_bar(); check_snd_card_create(); check_poll_schedule(); + check_snd_BUG_ON(); + check_bitops(); } # Do the basic rules diff --git a/v4l/versions.txt b/v4l/versions.txt index 3c57c14bd..7e8e2fa6e 100644 --- a/v4l/versions.txt +++ b/v4l/versions.txt @@ -5,9 +5,6 @@ # Needs defines that are only available from 2.6.29 VIDEO_PXA27x -[2.6.28] -USB_STV06XX - [2.6.26] # Requires struct i2c_device_id VIDEO_TVP514X diff --git a/v4l2-apps/libv4l/ChangeLog b/v4l2-apps/libv4l/ChangeLog index 11f6ca2fd..e69a8c8f4 100644 --- a/v4l2-apps/libv4l/ChangeLog +++ b/v4l2-apps/libv4l/ChangeLog @@ -1,3 +1,73 @@ +libv4l-0.6.0 +------------ +* Recognize disabled controls and replace with fake equivalents where + available +* Add support for decompressing ov511 and ov518 "JPEG", by piping data through + an external helper as I've failed to contact Mark W. McClelland to get + permission to relicense the code. If you know a working email address for + Mark W. McClelland, please let me know. +* Fix a bug in the always report widths which are a multiple of 8 code added + in 0.5.97 + +libv4l-0.5.99 +------------- +* Link libv4lconvert with -lm for powf by Gregor Jasny +* Fix black screen on devices with hardware gamma control +* Fix crash with devices on which we do not emulate fake controls +* Add a patch by Hans Petter Selasky <hselasky@freebsd.org>, which should + lead to allowing use of libv4l (and the Linux webcam drivers ported + to userspace usb drivers) on FreeBSD, this is a work in progress + +libv4l-0.5.98 +------------- +* Add software gamma correction +* Add software auto gain / exposure +* Add support for separate vflipping and hflipping +* Add fake controls controlling the software h- and v-flipping +* Add ability to determine upside down cams based on DMI info +* Add the capability to provide 320x240 to apps if the cam can only + do 320x232 (some zc3xx cams) by adding black borders +* Rewrite video processing code to make it easier to add more video filters + (and with little extra processing cost). As part of this the normalize + filter has been removed as it wasn't functioning satisfactory anyways +* Support V4L2_CTRL_FLAG_NEXT_CTRL for fake controls by Adam Baker +* Some makefile improvements by Gregor Jasny +* Various small bugfixes and tweaks +* The V4L2_ENABLE_ENUM_FMT_EMULATION v4l2_fd_open flag is obsolete, libv4l2 + now *always* reports emulated formats through the ENUM_FMT ioctl + +libv4l-0.5.97 +------------- +* As the version number shows this is a beta release of the 0.6.x series, + the big change here is the addition of video processing to libv4l + currently this only does whitebalance and normalizing (which turns out + to be useless for most cams) but the basic framework for doing video + processing, and being able to control it through fake v4l2 controls using + for example v4l2ucp is there. + The initial version of this code was written by 3 of my computer science + students: Elmar Kleijn, Sjoerd Piepenbrink and Radjnies Bhansingh +* Currently whitebalancing gets enabled based on USB-ID's and it only gets + enabled for Pixart webcam's. You can force it being enabled with other + webcams by setting the environment variable LIBV4LCONTROL_CONTROLS, this + sets a bitmask enabling certain v4l2 controls which control the video + processing set it to 15 to enable both whitebalancing and normalize. You + can then change the settings using a v4l2 control panel like v4l2ucp +* Only report / allow supported destination formats in enum_fmt / try_fmt / + g_fmt / s_fmt when processing, rotating or flipping. +* Some applications / libs (*cough* gstreamer *cough*) will not work + correctly with planar YUV formats when the width is not a multiple of 8, + so crop widths which are not a multiple of 8 to the nearest multiple of 8 + when converting to planar YUV +* Add dependency generation to libv4l by: Gilles Gigan <gilles.gigan@gmail.com> +* Add support to use orientation from VIDIOC_ENUMINPUT by: + Adam Baker <linux@baker-net.org.uk> +* sn9c20x cams have occasional bad jpeg frames, drop these to avoid the + flickering effect they cause, by: Brian Johnson <brijohn@gmail.com> +* adjust libv4l's upside down cam detection to also work with devices + which have the usb interface as parent instead of the usb device +* fix libv4l upside down detection for the new v4l minor numbering scheme +* fix reading outside of the source memory when doing yuv420->rgb conversion + libv4l-0.5.9 ------------ * Add support for MR97310A decompression by Kyle Guinn <elyk03@gmail.com> diff --git a/v4l2-apps/libv4l/Makefile b/v4l2-apps/libv4l/Makefile index 703f3298f..acec5c540 100644 --- a/v4l2-apps/libv4l/Makefile +++ b/v4l2-apps/libv4l/Makefile @@ -1,5 +1,5 @@ LIB_RELEASE=0 -V4L2_LIB_VERSION=$(LIB_RELEASE).5.9 +V4L2_LIB_VERSION=$(LIB_RELEASE).5.99 all install: $(MAKE) -C libv4lconvert V4L2_LIB_VERSION=$(V4L2_LIB_VERSION) $@ @@ -7,7 +7,7 @@ all install: $(MAKE) -C libv4l1 V4L2_LIB_VERSION=$(V4L2_LIB_VERSION) $@ clean: - rm -f *~ include/*~ + rm -f *~ include/*~ DEADJOE include/DEADJOE $(MAKE) -C libv4lconvert V4L2_LIB_VERSION=$(V4L2_LIB_VERSION) $@ $(MAKE) -C libv4l2 V4L2_LIB_VERSION=$(V4L2_LIB_VERSION) $@ $(MAKE) -C libv4l1 V4L2_LIB_VERSION=$(V4L2_LIB_VERSION) $@ diff --git a/v4l2-apps/libv4l/README b/v4l2-apps/libv4l/README index 3a2059224..481d5b92e 100644 --- a/v4l2-apps/libv4l/README +++ b/v4l2-apps/libv4l/README @@ -15,14 +15,26 @@ libv4l consists of 3 different libraries: libv4lconvert ------------- -libv4lconvert offers functions to convert from any (known) pixelformat -to V4l2_PIX_FMT_BGR24 or V4l2_PIX_FMT_YUV420. - -Currently the following source formats are supported: -jpeg, mjpeg, bayer (all 4 variants: bggr, rggb, gbrg, grbg), -spca501 (chip specific yuv 420 with interlaced components), -spca561 (chip specific compressed gbrg bayer) -For more details on the v4lconvert_ functions see libv4lconvert.h . +libv4lconvert started as a library to convert from any (known) pixelformat to +V4l2_PIX_FMT_BGR24, RGB24, YUV420 or YVU420. + +The list of know source formats is large and continually growing, so instead +of keeping an (almost always outdated) list here in the README, I refer you +to the source, see the list of defines at the top of +libv4lconvert/libv4lconvert.c for the full list. +For more details on the v4lconvert_ functions see libv4lconvert.h. + +Later on libv4lconvert was expanded to also be able to do various video +processing functions improve webcam video quality on a software basis. So +the name no longer 100% covers the functionality. The video processing is +split in to 2 parts, libv4lconvert/control and libv4lconvert/processing. + +The control part is used to offer video controls which can be used to control +the video processing functions made available by libv4lconvert/processing. +These controls are stored application wide (untill reboot) by using a +persistent shared memory object. + +libv4lconvert/processing offers the actual video processing functionality. libv4l1 diff --git a/v4l2-apps/libv4l/TODO b/v4l2-apps/libv4l/TODO index debb9c78c..8f7744337 100644 --- a/v4l2-apps/libv4l/TODO +++ b/v4l2-apps/libv4l/TODO @@ -8,3 +8,8 @@ necessary to implement CGMBUF in the kernel for each driver. -take the possibility of pitch != width into account everywhere + +-make updating of parameters happen based on time elapsed rather then + frames + +-get standardized CID for AUTOGAIN_TARGET upstream and switch to that diff --git a/v4l2-apps/libv4l/include/libv4l1.h b/v4l2-apps/libv4l/include/libv4l1.h index c878cc198..4ddf8efdb 100644 --- a/v4l2-apps/libv4l/include/libv4l1.h +++ b/v4l2-apps/libv4l/include/libv4l1.h @@ -63,7 +63,7 @@ LIBV4L_PUBLIC int v4l1_dup(int fd); LIBV4L_PUBLIC int v4l1_ioctl (int fd, unsigned long int request, ...); LIBV4L_PUBLIC ssize_t v4l1_read (int fd, void* buffer, size_t n); LIBV4L_PUBLIC void *v4l1_mmap(void *start, size_t length, int prot, int flags, int fd, - __off64_t offset); + int64_t offset); LIBV4L_PUBLIC int v4l1_munmap(void *_start, size_t length); #ifdef __cplusplus diff --git a/v4l2-apps/libv4l/include/libv4l2.h b/v4l2-apps/libv4l/include/libv4l2.h index b05b57cb6..7ecbb2cee 100644 --- a/v4l2-apps/libv4l/include/libv4l2.h +++ b/v4l2-apps/libv4l/include/libv4l2.h @@ -21,6 +21,7 @@ #include <stdio.h> #include <unistd.h> +#include <stdint.h> #ifdef __cplusplus extern "C" { @@ -41,18 +42,12 @@ LIBV4L_PUBLIC extern FILE *v4l2_log_file; format which is not supported by the cam, but is supported by libv4lconvert, then the try_fmt / set_fmt will succeed as if the cam supports the format and on dqbuf / read the data will be converted for you and returned in - the request format. + the request format. enum_fmt will also report support for the formats to + which conversion is possible. Another difference is that you can make v4l2_read() calls even on devices which do not support the regular read() method. - Note that libv4l2 normally does not interfere with enum_fmt, so enum_fmt - will still return the actual formats the hardware supports, and not any - formats which may be emulated on top of that. If you pass the - V4L2_ENABLE_ENUM_FMT_EMULATION flag to v4l2_fd_open (as the v4l2convert.so - wrapper does) then enum_fmt will also report support for the formats to - which conversion is possible. - Note the device name passed to v4l2_open must be of a video4linux2 device, if it is anything else (including a video4linux1 device), v4l2_open will fail. @@ -64,7 +59,7 @@ LIBV4L_PUBLIC int v4l2_dup(int fd); LIBV4L_PUBLIC int v4l2_ioctl (int fd, unsigned long int request, ...); LIBV4L_PUBLIC ssize_t v4l2_read (int fd, void* buffer, size_t n); LIBV4L_PUBLIC void *v4l2_mmap(void *start, size_t length, int prot, int flags, int fd, - __off64_t offset); + int64_t offset); LIBV4L_PUBLIC int v4l2_munmap(void *_start, size_t length); @@ -89,11 +84,14 @@ LIBV4L_PUBLIC int v4l2_get_control(int fd, int cid); /* Flags for v4l2_fd_open's v4l2_flags argument */ -/* Disable all format conversion done by libv4l2 (reduces libv4l2 functionality - to offering v4l2_read() even on devices which don't implement read()) */ +/* Disable all format conversion done by libv4l2, this includes the software + whitebalance, gamma correction, flipping, etc. libv4lconvert does. Use this + if you want raw frame data, but still want the additional error checks and + the read() emulation libv4l2 offers. */ #define V4L2_DISABLE_CONVERSION 0x01 -/* Report not only real but also emulated formats with the ENUM_FMT ioctl */ -#define V4L2_ENABLE_ENUM_FMT_EMULATION 02 +/* This flag is *OBSOLETE*, since version 0.5.98 libv4l *always* reports + emulated formats to ENUM_FMT, except when conversion is disabled. */ +#define V4L2_ENABLE_ENUM_FMT_EMULATION 0x02 /* v4l2_fd_open: open an already opened fd for further use through v4l2lib and possibly modify libv4l2's default behavior through the diff --git a/v4l2-apps/libv4l/include/libv4lconvert.h b/v4l2-apps/libv4l/include/libv4lconvert.h index 626c43473..b274c938b 100644 --- a/v4l2-apps/libv4l/include/libv4lconvert.h +++ b/v4l2-apps/libv4l/include/libv4lconvert.h @@ -21,10 +21,21 @@ /* These headers are not needed by us, but by linux/videodev2.h, which is broken on some systems and doesn't include them itself :( */ + +#ifdef linux #include <sys/time.h> #include <linux/types.h> #include <linux/ioctl.h> +#endif + +#ifdef __FreeBSD__ +#include <sys/time.h> +#include <sys/types.h> +#include <sys/ioctl.h> +#endif + /* end broken header workaround includes */ + #include <linux/videodev2.h> #ifdef __cplusplus @@ -42,18 +53,30 @@ struct v4lconvert_data; LIBV4L_PUBLIC struct v4lconvert_data *v4lconvert_create(int fd); LIBV4L_PUBLIC void v4lconvert_destroy(struct v4lconvert_data *data); +/* When doing flipping / rotating / video-processing, only supported + destination formats can be used (as flipping / rotating / video-processing + is not supported on other formats). This function can be used to query + if that is the case. */ +LIBV4L_PUBLIC int v4lconvert_supported_dst_fmt_only( + struct v4lconvert_data *data); + /* With regards to dest_fmt just like VIDIOC_TRY_FMT, except that the try format will succeed and return the requested V4L2_PIX_FMT_foo in dest_fmt if the cam has a format from which v4lconvert can convert to dest_fmt. The real format to which the cam should be set is returned through src_fmt - when not NULL. */ + when not NULL. + Note that just like the real VIDIOC_TRY_FMT this function will change the + dest_fmt when not supported. This includes changing it to a supported + destination format when trying a native format of the camera and + v4lconvert_supported_dst_fmt_only() returns true. */ LIBV4L_PUBLIC int v4lconvert_try_format(struct v4lconvert_data *data, struct v4l2_format *dest_fmt, /* in / out */ struct v4l2_format *src_fmt /* out */ ); -/* Just like VIDIOC_ENUM_FMT, except that the emulated formats are added at - the end of the list */ +/* Like VIDIOC_ENUM_FMT, but the emulated formats are added at the end of the + list, except if flipping / processing is active for the device, then only + supported destination formats are listed */ LIBV4L_PUBLIC int v4lconvert_enum_fmt(struct v4lconvert_data *data, struct v4l2_fmtdesc *fmt); /* Is conversion necessary or can the app use the data directly? */ @@ -81,6 +104,17 @@ LIBV4L_PUBLIC int v4lconvert_enum_framesizes(struct v4lconvert_data *data, LIBV4L_PUBLIC int v4lconvert_enum_frameintervals(struct v4lconvert_data *data, struct v4l2_frmivalenum *frmival); +/* Pass calls to query, get and set video controls to the libv4lcontrol class */ +LIBV4L_PUBLIC int v4lconvert_vidioc_queryctrl(struct v4lconvert_data *data, + void *arg); +LIBV4L_PUBLIC int v4lconvert_vidioc_g_ctrl(struct v4lconvert_data *data, + void *arg); +LIBV4L_PUBLIC int v4lconvert_vidioc_s_ctrl(struct v4lconvert_data *data, + void *arg); + +/* Is the passed in pixelformat supported as destination format ? */ +LIBV4L_PUBLIC int v4lconvert_supported_dst_format(unsigned int pixelformat); + #ifdef __cplusplus } #endif /* __cplusplus */ diff --git a/v4l2-apps/libv4l/libv4l1/Makefile b/v4l2-apps/libv4l/libv4l1/Makefile index 9f30cbb0f..e6d306055 100644 --- a/v4l2-apps/libv4l/libv4l1/Makefile +++ b/v4l2-apps/libv4l/libv4l1/Makefile @@ -13,9 +13,10 @@ INCLUDES = ../include/libv4l1.h ifeq ($(LINKTYPE),static) V4L1_LIB = libv4l1.a +V4L1_DEPS = $(V4L1_OBJS) else V4L1_LIB = libv4l1.so -V4L1_OBJS += ../libv4l2/libv4l2.so +V4L1_DEPS += $(V4L1_OBJS) ../libv4l2/libv4l2.so TARGETS += $(V4L1COMPAT) override CPPFLAGS += -fPIC endif @@ -34,8 +35,9 @@ endif all: $(TARGETS) +-include $(V4L1_OBJS:.o=.d) -$(V4L1_LIB): $(V4L1_OBJS) +$(V4L1_LIB): $(V4L1_DEPS) $(V4L1COMPAT): $(V4L1COMPAT_O) $(V4L1_LIB) @@ -69,10 +71,10 @@ endif install -m 644 libv4l1.pc $(DESTDIR)$(LIBDIR)/pkgconfig clean:: - rm -f *.a *.so* *.o *.d libv4l1.pc log *~ *.orig *.rej + rm -f *.a *.so* *.o *.d libv4l1.pc log *~ *.orig *.rej DEADJOE %.o: %.c - $(CC) -c -MMD $(CPPFLAGS) $(CFLAGS) -o $@ $< + $(CC) -Wp,-MMD,"$*.d",-MQ,"$@",-MP -c $(CPPFLAGS) $(CFLAGS) -o $@ $< %.so: $(CC) -shared $(LDFLAGS) -Wl,-soname,$@.$(LIB_RELEASE) -o $@.$(LIB_RELEASE) $^ $(LIBS_$*) diff --git a/v4l2-apps/libv4l/libv4l1/libv4l1-priv.h b/v4l2-apps/libv4l/libv4l1/libv4l1-priv.h index 651599255..370686b3a 100644 --- a/v4l2-apps/libv4l/libv4l1/libv4l1-priv.h +++ b/v4l2-apps/libv4l/libv4l1/libv4l1-priv.h @@ -22,14 +22,7 @@ #include <stdio.h> #include <pthread.h> -/* On 32 bits archs we always use mmap2, on 64 bits archs there is no mmap2 */ -#ifdef __NR_mmap2 -#define SYS_mmap2 __NR_mmap2 -#define MMAP2_PAGE_SHIFT 12 -#else -#define SYS_mmap2 SYS_mmap -#define MMAP2_PAGE_SHIFT 0 -#endif +#include "../libv4lconvert/libv4lsyscall-priv.h" #define V4L1_MAX_DEVICES 16 #define V4L1_NO_FRAMES 4 diff --git a/v4l2-apps/libv4l/libv4l1/libv4l1.c b/v4l2-apps/libv4l/libv4l1/libv4l1.c index 797c8768a..9b23926be 100644 --- a/v4l2-apps/libv4l/libv4l1/libv4l1.c +++ b/v4l2-apps/libv4l/libv4l1/libv4l1.c @@ -44,18 +44,12 @@ #include <stdarg.h> #include <stdio.h> #include <stdlib.h> -#include <syscall.h> #include <fcntl.h> #include <string.h> #include <unistd.h> #include <sys/types.h> #include <sys/mman.h> -/* These headers are not needed by us, but by linux/videodev2.h, - which is broken on some systems and doesn't include them itself :( */ -#include <sys/time.h> -#include <asm/types.h> -#include <linux/ioctl.h> -/* end broken header workaround includes */ +#include "../libv4lconvert/libv4lsyscall-priv.h" #include <linux/videodev.h> #include <linux/videodev2.h> #include <libv4l2.h> @@ -155,8 +149,10 @@ static int v4l1_set_format(int index, unsigned int width, /* Do we need to change the resolution / format ? */ if (width == devices[index].width && height == devices[index].height && - v4l2_pixfmt == devices[index].v4l2_pixfmt) + v4l2_pixfmt == devices[index].v4l2_pixfmt) { + devices[index].v4l1_pal = v4l1_pal; return 0; + } /* Get current settings, apply our changes and try the new setting */ if ((result = v4l2_ioctl(devices[index].fd, VIDIOC_G_FMT, &fmt2))) { @@ -223,14 +219,14 @@ static void v4l1_find_min_and_max_size(int index, struct v4l2_format *fmt2) for (i = 0; ; i++) { fmtdesc2.index = i; - if (syscall(SYS_ioctl, devices[index].fd, VIDIOC_ENUM_FMT, &fmtdesc2)) + if (v4l2_ioctl(devices[index].fd, VIDIOC_ENUM_FMT, &fmtdesc2)) break; fmt2->fmt.pix.pixelformat = fmtdesc2.pixelformat; fmt2->fmt.pix.width = 48; fmt2->fmt.pix.height = 32; - if (syscall(SYS_ioctl, devices[index].fd, VIDIOC_TRY_FMT, fmt2) == 0) { + if (v4l2_ioctl(devices[index].fd, VIDIOC_TRY_FMT, fmt2) == 0) { if (fmt2->fmt.pix.width < devices[index].min_width) devices[index].min_width = fmt2->fmt.pix.width; if (fmt2->fmt.pix.height < devices[index].min_height) @@ -241,7 +237,7 @@ static void v4l1_find_min_and_max_size(int index, struct v4l2_format *fmt2) fmt2->fmt.pix.width = 100000; fmt2->fmt.pix.height = 100000; - if (syscall(SYS_ioctl, devices[index].fd, VIDIOC_TRY_FMT, fmt2) == 0) { + if (v4l2_ioctl(devices[index].fd, VIDIOC_TRY_FMT, fmt2) == 0) { if (fmt2->fmt.pix.width > devices[index].max_width) devices[index].max_width = fmt2->fmt.pix.width; if (fmt2->fmt.pix.height > devices[index].max_height) @@ -278,11 +274,12 @@ int v4l1_open (const char *file, int oflag, ...) va_start (ap, oflag); mode = va_arg (ap, mode_t); - fd = syscall(SYS_open, file, oflag, mode); + fd = SYS_OPEN(file, oflag, mode); va_end(ap); } else - fd = syscall(SYS_open, file, oflag); + fd = SYS_OPEN(file, oflag, 0); + /* end of original open code */ if (fd == -1 || !v4l_device) @@ -290,7 +287,7 @@ int v4l1_open (const char *file, int oflag, ...) /* check that this is an v4l2 device, no need to emulate v4l1 on a v4l1 device */ - if (syscall(SYS_ioctl, fd, VIDIOC_QUERYCAP, &cap2)) + if (SYS_IOCTL(fd, VIDIOC_QUERYCAP, &cap2)) return fd; /* IMPROVEME */ @@ -308,21 +305,20 @@ int v4l1_open (const char *file, int oflag, ...) if (!v4l2_log_file) v4l2_log_file = v4l1_log_file; - /* Get initial width, height and pixelformat */ - fmt2.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - if (syscall(SYS_ioctl, fd, VIDIOC_G_FMT, &fmt2)) { + /* Register with libv4l2, as we use that todo format conversion and read() + emulation for us */ + if (v4l2_fd_open(fd, 0) == -1) { int saved_err = errno; - V4L1_LOG_ERR("getting pixformat: %s\n", strerror(errno)); - syscall(SYS_close, fd); + SYS_CLOSE(fd); errno = saved_err; return -1; } - /* Register with libv4l2, as we use that todo format conversion and read() - emulation for us */ - if (v4l2_fd_open(fd, V4L2_ENABLE_ENUM_FMT_EMULATION) == -1) { + /* Get initial width, height and pixelformat */ + fmt2.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + if (v4l2_ioctl(fd, VIDIOC_G_FMT, &fmt2)) { int saved_err = errno; - syscall(SYS_close, fd); + SYS_CLOSE(fd); errno = saved_err; return -1; } @@ -399,7 +395,7 @@ int v4l1_close(int fd) { int index, result; if ((index = v4l1_get_index(fd)) == -1) - return syscall(SYS_close, fd); + return SYS_CLOSE(fd); /* Abuse stream_lock to stop 2 closes from racing and trying to free the resources twice */ @@ -417,7 +413,7 @@ int v4l1_close(int fd) { V4L1_LOG("v4l1 capture buffer still mapped: %d times on close()\n", devices[index].v4l1_frame_buf_map_count); else - syscall(SYS_munmap, devices[index].v4l1_frame_pointer, + SYS_MUNMAP(devices[index].v4l1_frame_pointer, V4L1_NO_FRAMES * V4L1_FRAME_BUF_SIZE); devices[index].v4l1_frame_pointer = MAP_FAILED; } @@ -446,7 +442,6 @@ int v4l1_dup(int fd) return v4l2_dup(fd); } - int v4l1_ioctl (int fd, unsigned long int request, ...) { void *arg; @@ -458,7 +453,7 @@ int v4l1_ioctl (int fd, unsigned long int request, ...) va_end (ap); if ((index = v4l1_get_index(fd)) == -1) - return syscall(SYS_ioctl, fd, request, arg); + return SYS_IOCTL(fd, request, arg); /* Appearantly the kernel and / or glibc ignore the 32 most significant bits when long = 64 bits, and some applications pass an int holding the req to @@ -485,7 +480,7 @@ int v4l1_ioctl (int fd, unsigned long int request, ...) { struct video_capability *cap = arg; - result = syscall(SYS_ioctl, fd, request, arg); + result = SYS_IOCTL(fd, request, arg); /* override kernel v4l1 compat min / max size with our own more accurate values */ @@ -547,24 +542,27 @@ int v4l1_ioctl (int fd, unsigned long int request, ...) break; case VIDIOCSWIN: + case VIDIOCGWIN: { struct video_window *win = arg; devices[index].flags |= V4L1_PIX_SIZE_TOUCHED; - result = v4l1_set_format(index, win->width, win->height, -1, 1); + if (request == VIDIOCSWIN) + result = v4l1_set_format(index, win->width, win->height, -1, 1); + else + result = 0; + if (result == 0) { + win->x = 0; + win->y = 0; win->width = devices[index].width; win->height = devices[index].height; + win->flags = 0; } } break; - case VIDIOCGWIN: - devices[index].flags |= V4L1_PIX_SIZE_TOUCHED; - result = syscall(SYS_ioctl, fd, request, arg); - break; - case VIDIOCGCHAN: { struct v4l2_input input2; @@ -572,7 +570,7 @@ int v4l1_ioctl (int fd, unsigned long int request, ...) if ((devices[index].flags & V4L1_SUPPORTS_ENUMINPUT) && (devices[index].flags & V4L1_SUPPORTS_ENUMSTD)) { - result = syscall(SYS_ioctl, fd, request, arg); + result = SYS_IOCTL(fd, request, arg); break; } @@ -614,7 +612,7 @@ int v4l1_ioctl (int fd, unsigned long int request, ...) struct video_channel *chan = arg; if ((devices[index].flags & V4L1_SUPPORTS_ENUMINPUT) && (devices[index].flags & V4L1_SUPPORTS_ENUMSTD)) { - result = syscall(SYS_ioctl, fd, request, arg); + result = SYS_IOCTL(fd, request, arg); break; } /* In case of no ENUMSTD support, ignore the norm member of the @@ -650,7 +648,7 @@ int v4l1_ioctl (int fd, unsigned long int request, ...) } if (devices[index].v4l1_frame_pointer == MAP_FAILED) { - devices[index].v4l1_frame_pointer = (void *)syscall(SYS_mmap2, NULL, + devices[index].v4l1_frame_pointer = (void *)SYS_MMAP(NULL, (size_t)mbuf->size, PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_PRIVATE, -1, 0); @@ -747,7 +745,7 @@ ssize_t v4l1_read(int fd, void* buffer, size_t n) ssize_t result; if ((index = v4l1_get_index(fd)) == -1) - return syscall(SYS_read, fd, buffer, n); + return SYS_READ(fd, buffer, n); pthread_mutex_lock(&devices[index].stream_lock); result = v4l2_read(fd, buffer, n); @@ -758,7 +756,7 @@ ssize_t v4l1_read(int fd, void* buffer, size_t n) void *v4l1_mmap(void *start, size_t length, int prot, int flags, int fd, - __off64_t offset) + int64_t offset) { int index; void *result; diff --git a/v4l2-apps/libv4l/libv4l1/log.c b/v4l2-apps/libv4l/libv4l1/log.c index 9ff0cea46..fd095817f 100644 --- a/v4l2-apps/libv4l/libv4l1/log.c +++ b/v4l2-apps/libv4l/libv4l1/log.c @@ -18,12 +18,7 @@ #include <stdio.h> #include <stdlib.h> -#include <linux/ioctl.h> -/* These headers are not needed by us, but by linux/videodev2.h, - which is broken on some systems and doesn't include them itself :( */ -#include <sys/time.h> -#include <asm/types.h> -/* end broken header workaround includes */ +#include "../libv4lconvert/libv4lsyscall-priv.h" #include <linux/videodev.h> #include "libv4l1-priv.h" diff --git a/v4l2-apps/libv4l/libv4l1/v4l1compat.c b/v4l2-apps/libv4l/libv4l1/v4l1compat.c index e4293d2f9..704ec22dd 100644 --- a/v4l2-apps/libv4l/libv4l1/v4l1compat.c +++ b/v4l2-apps/libv4l/libv4l1/v4l1compat.c @@ -25,6 +25,7 @@ #include <stdarg.h> #include <fcntl.h> #include <libv4l1.h> +#include "../libv4lconvert/libv4lsyscall-priv.h" /* for __off_t */ #include <sys/ioctl.h> #include <sys/mman.h> @@ -61,6 +62,7 @@ LIBV4L_PUBLIC int open (const char *file, int oflag, ...) return fd; } +#ifdef linux LIBV4L_PUBLIC int open64 (const char *file, int oflag, ...) { int fd; @@ -81,6 +83,7 @@ LIBV4L_PUBLIC int open64 (const char *file, int oflag, ...) return fd; } +#endif LIBV4L_PUBLIC int close(int fd) { return v4l1_close(fd); @@ -114,11 +117,13 @@ LIBV4L_PUBLIC void *mmap(void *start, size_t length, int prot, int flags, int fd return v4l1_mmap(start, length, prot, flags, fd, offset); } +#ifdef linux LIBV4L_PUBLIC void *mmap64(void *start, size_t length, int prot, int flags, int fd, __off64_t offset) { return v4l1_mmap(start, length, prot, flags, fd, offset); } +#endif LIBV4L_PUBLIC int munmap(void *start, size_t length) { diff --git a/v4l2-apps/libv4l/libv4l2/Makefile b/v4l2-apps/libv4l/libv4l2/Makefile index 614b36cf8..06c010f3c 100644 --- a/v4l2-apps/libv4l/libv4l2/Makefile +++ b/v4l2-apps/libv4l/libv4l2/Makefile @@ -13,9 +13,10 @@ INCLUDES = ../include/libv4l2.h ifeq ($(LINKTYPE),static) V4L2_LIB = libv4l2.a +V4L2_DEPS = $(V4L2_OBJS) else V4L2_LIB = libv4l2.so -V4L2_OBJS += ../libv4lconvert/libv4lconvert.so +V4L2_DEPS += $(V4L2_OBJS) ../libv4lconvert/libv4lconvert.so TARGETS += $(V4L2CONVERT) override CPPFLAGS += -fPIC endif @@ -34,7 +35,9 @@ endif all: $(TARGETS) -$(V4L2_LIB): $(V4L2_OBJS) +-include $(V4L2_OBJS:.o=.d) + +$(V4L2_LIB): $(V4L2_DEPS) $(V4L2CONVERT): $(V4L2CONVERT_O) $(V4L2_LIB) @@ -68,10 +71,10 @@ endif install -m 644 libv4l2.pc $(DESTDIR)$(LIBDIR)/pkgconfig clean:: - rm -f *.a *.so* *.o *.d libv4l2.pc log *~ *.orig *.rej + rm -f *.a *.so* *.o *.d libv4l2.pc log *~ *.orig *.rej DEADJOE %.o: %.c - $(CC) -c -MMD $(CPPFLAGS) $(CFLAGS) -o $@ $< + $(CC) -Wp,-MMD,"$*.d",-MQ,"$@",-MP -c $(CPPFLAGS) $(CFLAGS) -o $@ $< %.so: $(CC) -shared $(LDFLAGS) -Wl,-soname,$@.$(LIB_RELEASE) -o $@.$(LIB_RELEASE) $^ $(LIBS_$*) @@ -79,4 +82,3 @@ clean:: %.a: $(AR) cqs $@ $^ - diff --git a/v4l2-apps/libv4l/libv4l2/libv4l2-priv.h b/v4l2-apps/libv4l/libv4l2/libv4l2-priv.h index 8724832e1..0bfd53e2a 100644 --- a/v4l2-apps/libv4l/libv4l2/libv4l2-priv.h +++ b/v4l2-apps/libv4l/libv4l2/libv4l2-priv.h @@ -23,14 +23,7 @@ #include <pthread.h> #include <libv4lconvert.h> /* includes videodev2.h for us */ -/* On 32 bits archs we always use mmap2, on 64 bits archs there is no mmap2 */ -#ifdef __NR_mmap2 -#define SYS_mmap2 __NR_mmap2 -#define MMAP2_PAGE_SHIFT 12 -#else -#define SYS_mmap2 SYS_mmap -#define MMAP2_PAGE_SHIFT 0 -#endif +#include "../libv4lconvert/libv4lsyscall-priv.h" #define V4L2_MAX_DEVICES 16 /* Warning when making this larger the frame_queued and frame_mapped members of diff --git a/v4l2-apps/libv4l/libv4l2/libv4l2.c b/v4l2-apps/libv4l/libv4l2/libv4l2.c index b6ddef6e8..b71c9b4c6 100644 --- a/v4l2-apps/libv4l/libv4l2/libv4l2.c +++ b/v4l2-apps/libv4l/libv4l2/libv4l2.c @@ -59,7 +59,6 @@ #include <stdarg.h> #include <stdio.h> #include <stdlib.h> -#include <syscall.h> #include <fcntl.h> #include <string.h> #include <unistd.h> @@ -77,6 +76,7 @@ #define V4L2_STREAM_CONTROLLED_BY_READ 0x0400 #define V4L2_SUPPORTS_READ 0x0800 #define V4L2_IS_UVC 0x1000 +#define V4L2_STREAM_TOUCHED 0x2000 #define V4L2_MMAP_OFFSET_MAGIC 0xABCDEF00u @@ -99,7 +99,7 @@ static int v4l2_request_read_buffers(int index) devices[index].nreadbuffers; req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; req.memory = V4L2_MEMORY_MMAP; - if ((result = syscall(SYS_ioctl, devices[index].fd, VIDIOC_REQBUFS, &req)) < 0){ + if ((result = SYS_IOCTL(devices[index].fd, VIDIOC_REQBUFS, &req)) < 0){ int saved_err = errno; V4L2_LOG_ERR("requesting %u buffers: %s\n", req.count, strerror(errno)); errno = saved_err; @@ -126,7 +126,7 @@ static void v4l2_unrequest_read_buffers(int index) req.count = 0; req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; req.memory = V4L2_MEMORY_MMAP; - if(syscall(SYS_ioctl, devices[index].fd, VIDIOC_REQBUFS, &req) < 0) + if(SYS_IOCTL(devices[index].fd, VIDIOC_REQBUFS, &req) < 0) return; devices[index].no_frames = MIN(req.count, V4L2_MAX_NO_FRAMES); @@ -147,7 +147,7 @@ static int v4l2_map_buffers(int index) buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; buf.memory = V4L2_MEMORY_MMAP; buf.index = i; - result = syscall(SYS_ioctl, devices[index].fd, VIDIOC_QUERYBUF, &buf); + result = SYS_IOCTL(devices[index].fd, VIDIOC_QUERYBUF, &buf); if (result) { int saved_err = errno; V4L2_LOG_ERR("querying buffer %u: %s\n", i, strerror(errno)); @@ -155,9 +155,9 @@ static int v4l2_map_buffers(int index) break; } - devices[index].frame_pointers[i] = (void *)syscall(SYS_mmap2, NULL, + devices[index].frame_pointers[i] = (void *)SYS_MMAP(NULL, (size_t)buf.length, PROT_READ|PROT_WRITE, MAP_SHARED, devices[index].fd, - (__off_t)(buf.m.offset >> MMAP2_PAGE_SHIFT)); + buf.m.offset); if (devices[index].frame_pointers[i] == MAP_FAILED) { int saved_err = errno; V4L2_LOG_ERR("mmapping buffer %u: %s\n", i, strerror(errno)); @@ -181,7 +181,7 @@ static void v4l2_unmap_buffers(int index) /* unmap the buffers */ for (i = 0; i < devices[index].no_frames; i++) { if (devices[index].frame_pointers[i] != MAP_FAILED) { - syscall(SYS_munmap, devices[index].frame_pointers[i], + SYS_MUNMAP(devices[index].frame_pointers[i], devices[index].frame_sizes[i]); devices[index].frame_pointers[i] = MAP_FAILED; V4L2_LOG("unmapped buffer %u\n", i); @@ -195,7 +195,7 @@ static int v4l2_streamon(int index) enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE; if (!(devices[index].flags & V4L2_STREAMON)) { - if ((result = syscall(SYS_ioctl, devices[index].fd, VIDIOC_STREAMON, + if ((result = SYS_IOCTL(devices[index].fd, VIDIOC_STREAMON, &type))) { int saved_err = errno; V4L2_LOG_ERR("turning on stream: %s\n", strerror(errno)); @@ -214,7 +214,7 @@ static int v4l2_streamoff(int index) enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE; if (devices[index].flags & V4L2_STREAMON) { - if ((result = syscall(SYS_ioctl, devices[index].fd, VIDIOC_STREAMOFF, + if ((result = SYS_IOCTL(devices[index].fd, VIDIOC_STREAMOFF, &type))) { int saved_err = errno; V4L2_LOG_ERR("turning off stream: %s\n", strerror(errno)); @@ -242,7 +242,7 @@ static int v4l2_queue_read_buffer(int index, int buffer_index) buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; buf.memory = V4L2_MEMORY_MMAP; buf.index = buffer_index; - if ((result = syscall(SYS_ioctl, devices[index].fd, VIDIOC_QBUF, &buf))) { + if ((result = SYS_IOCTL(devices[index].fd, VIDIOC_QBUF, &buf))) { int saved_err = errno; V4L2_LOG_ERR("queuing buf %d: %s\n", buffer_index, strerror(errno)); errno = saved_err; @@ -264,7 +264,7 @@ static int v4l2_dequeue_and_convert(int index, struct v4l2_buffer *buf, return result; do { - if ((result = syscall(SYS_ioctl, devices[index].fd, VIDIOC_DQBUF, buf))) { + if ((result = SYS_IOCTL(devices[index].fd, VIDIOC_DQBUF, buf))) { if (errno != EAGAIN) { int saved_err = errno; V4L2_LOG_ERR("dequeuing buf: %s\n", strerror(errno)); @@ -364,12 +364,20 @@ static int v4l2_deactivate_read_stream(int index) return 0; } +static int v4l2_needs_conversion(int index) +{ + if (!(devices[index].flags & V4L2_DISABLE_CONVERSION)) + return v4lconvert_needs_conversion(devices[index].convert, + &devices[index].src_fmt, &devices[index].dest_fmt); + + return 0; +} + static int v4l2_buffers_mapped(int index) { unsigned int i; - if (devices[index].src_fmt.fmt.pix.pixelformat == - devices[index].dest_fmt.fmt.pix.pixelformat) { + if (!v4l2_needs_conversion(index)) { /* Normal (no conversion) mode */ struct v4l2_buffer buf; @@ -377,7 +385,7 @@ static int v4l2_buffers_mapped(int index) buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; buf.memory = V4L2_MEMORY_MMAP; buf.index = i; - if (syscall(SYS_ioctl, devices[index].fd, VIDIOC_QUERYBUF, &buf)) { + if (SYS_IOCTL(devices[index].fd, VIDIOC_QUERYBUF, &buf)) { int saved_err = errno; V4L2_LOG_ERR("querying buffer %u: %s\n", i, strerror(errno)); errno = saved_err; @@ -413,12 +421,12 @@ int v4l2_open (const char *file, int oflag, ...) va_start (ap, oflag); mode = va_arg (ap, mode_t); - fd = syscall(SYS_open, file, oflag, mode); + fd = SYS_OPEN(file, oflag, mode); va_end(ap); } else - fd = syscall(SYS_open, file, oflag); + fd = SYS_OPEN(file, oflag, 0); /* end of original open code */ if (fd == -1) @@ -426,7 +434,7 @@ int v4l2_open (const char *file, int oflag, ...) if (v4l2_fd_open(fd, 0) == -1) { int saved_err = errno; - syscall(SYS_close, fd); + SYS_CLOSE(fd); errno = saved_err; return -1; } @@ -448,7 +456,7 @@ int v4l2_fd_open(int fd, int v4l2_flags) v4l2_log_file = fopen(lfname, "w"); /* check that this is an v4l2 device */ - if (syscall(SYS_ioctl, fd, VIDIOC_QUERYCAP, &cap)) { + if (SYS_IOCTL(fd, VIDIOC_QUERYCAP, &cap)) { int saved_err = errno; V4L2_LOG_ERR("getting capabilities: %s\n", strerror(errno)); errno = saved_err; @@ -463,7 +471,7 @@ int v4l2_fd_open(int fd, int v4l2_flags) /* Get current cam format */ fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - if (syscall(SYS_ioctl, fd, VIDIOC_G_FMT, &fmt)) { + if (SYS_IOCTL(fd, VIDIOC_G_FMT, &fmt)) { int saved_err = errno; V4L2_LOG_ERR("getting pixformat: %s\n", strerror(errno)); errno = saved_err; @@ -499,6 +507,16 @@ int v4l2_fd_open(int fd, int v4l2_flags) devices[index].src_fmt = fmt; devices[index].dest_fmt = fmt; + /* When a user does a try_fmt with the current dest_fmt and the dest_fmt + is a supported one we will align the resulution (see try_fmt for why). + Do the same here now, so that a try_fmt on the result of a get_fmt done + immediately after open leaves the fmt unchanged. */ + if (v4lconvert_supported_dst_format( + devices[index].dest_fmt.fmt.pix.pixelformat)) { + devices[index].dest_fmt.fmt.pix.width &= ~7; + devices[index].dest_fmt.fmt.pix.height &= ~1; + } + pthread_mutex_init(&devices[index].stream_lock, NULL); devices[index].no_frames = 0; @@ -544,7 +562,7 @@ int v4l2_close(int fd) int index, result; if ((index = v4l2_get_index(fd)) == -1) - return syscall(SYS_close, fd); + return SYS_CLOSE(fd); /* Abuse stream_lock to stop 2 closes from racing and trying to free the resources twice */ @@ -558,15 +576,15 @@ int v4l2_close(int fd) /* Free resources */ v4l2_unmap_buffers(index); - v4lconvert_destroy(devices[index].convert); if (devices[index].convert_mmap_buf != MAP_FAILED) { if (v4l2_buffers_mapped(index)) V4L2_LOG_WARN("v4l2 mmap buffers still mapped on close()\n"); else - syscall(SYS_munmap, devices[index].convert_mmap_buf, + SYS_MUNMAP(devices[index].convert_mmap_buf, devices[index].no_frames * V4L2_FRAME_BUF_SIZE); devices[index].convert_mmap_buf = MAP_FAILED; } + v4lconvert_destroy(devices[index].convert); /* Remove the fd from our list of managed fds before closing it, because as soon as we've done the actual close the fd maybe returned by an open in @@ -576,7 +594,7 @@ int v4l2_close(int fd) /* Since we've marked the fd as no longer used, and freed the resources, redo the close in case it was interrupted */ do { - result = syscall(SYS_close, fd); + result = SYS_CLOSE(fd); } while (result == -1 && errno == EINTR); V4L2_LOG("close: %d\n", fd); @@ -613,7 +631,7 @@ static int v4l2_check_buffer_change_ok(int index) /* We may change from convert to non conversion mode and v4l2_unrequest_read_buffers may change the no_frames, so free the convert mmap buffer */ - syscall(SYS_munmap, devices[index].convert_mmap_buf, + SYS_MUNMAP(devices[index].convert_mmap_buf, devices[index].no_frames * V4L2_FRAME_BUF_SIZE); devices[index].convert_mmap_buf = MAP_FAILED; @@ -659,7 +677,7 @@ int v4l2_ioctl (int fd, unsigned long int request, ...) { void *arg; va_list ap; - int result, converting, index, saved_err; + int result, index, saved_err; int is_capture_request = 0, stream_needs_locking = 0; va_start (ap, request); @@ -667,7 +685,7 @@ int v4l2_ioctl (int fd, unsigned long int request, ...) va_end (ap); if ((index = v4l2_get_index(fd)) == -1) - return syscall(SYS_ioctl, fd, request, arg); + return SYS_IOCTL(fd, request, arg); /* Appearantly the kernel and / or glibc ignore the 32 most significant bits when long = 64 bits, and some applications pass an int holding the req to @@ -676,21 +694,28 @@ int v4l2_ioctl (int fd, unsigned long int request, ...) /* Is this a capture request and do we need to take the stream lock? */ switch (request) { + case VIDIOC_QUERYCTRL: + case VIDIOC_G_CTRL: + case VIDIOC_S_CTRL: + if (!(devices[index].flags & V4L2_DISABLE_CONVERSION)) + is_capture_request = 1; + break; case VIDIOC_QUERYCAP: is_capture_request = 1; break; case VIDIOC_ENUM_FMT: if (((struct v4l2_fmtdesc *)arg)->type == V4L2_BUF_TYPE_VIDEO_CAPTURE && - (devices[index].flags & V4L2_ENABLE_ENUM_FMT_EMULATION)) + !(devices[index].flags & V4L2_DISABLE_CONVERSION)) is_capture_request = 1; break; case VIDIOC_ENUM_FRAMESIZES: case VIDIOC_ENUM_FRAMEINTERVALS: - if (devices[index].flags & V4L2_ENABLE_ENUM_FMT_EMULATION) + if (!(devices[index].flags & V4L2_DISABLE_CONVERSION)) is_capture_request = 1; break; case VIDIOC_TRY_FMT: - if (((struct v4l2_format *)arg)->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) + if (((struct v4l2_format *)arg)->type == V4L2_BUF_TYPE_VIDEO_CAPTURE && + !(devices[index].flags & V4L2_DISABLE_CONVERSION)) is_capture_request = 1; break; case VIDIOC_S_FMT: @@ -724,7 +749,7 @@ int v4l2_ioctl (int fd, unsigned long int request, ...) } if (!is_capture_request) { - result = syscall(SYS_ioctl, fd, request, arg); + result = SYS_IOCTL(fd, request, arg); saved_err = errno; v4l2_log_ioctl(request, arg, result); errno = saved_err; @@ -732,18 +757,48 @@ int v4l2_ioctl (int fd, unsigned long int request, ...) } - if (stream_needs_locking) + if (stream_needs_locking) { pthread_mutex_lock(&devices[index].stream_lock); - - converting = v4lconvert_needs_conversion(devices[index].convert, - &devices[index].src_fmt, &devices[index].dest_fmt); + /* If this is the first stream related ioctl, and we should only allow + libv4lconvert supported destination formats (so that it can do flipping, + processing, etc.) and the current destination format is not supported, + try setting the format to RGB24 (which is a supported dest. format). */ + if (!(devices[index].flags & V4L2_STREAM_TOUCHED) && + !(devices[index].flags & V4L2_DISABLE_CONVERSION) && + v4lconvert_supported_dst_fmt_only(devices[index].convert) && + !v4lconvert_supported_dst_format( + devices[index].dest_fmt.fmt.pix.pixelformat)) { + struct v4l2_format fmt = devices[index].dest_fmt; + + V4L2_LOG("Setting pixelformat to RGB24 (supported_dst_fmt_only)"); + devices[index].flags |= V4L2_STREAM_TOUCHED; + fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_RGB24; + pthread_mutex_unlock(&devices[index].stream_lock); + v4l2_ioctl(fd, VIDIOC_S_FMT, &fmt); + pthread_mutex_lock(&devices[index].stream_lock); + V4L2_LOG("Done setting pixelformat (supported_dst_fmt_only)"); + } + devices[index].flags |= V4L2_STREAM_TOUCHED; + } switch (request) { + case VIDIOC_QUERYCTRL: + result = v4lconvert_vidioc_queryctrl(devices[index].convert, arg); + break; + + case VIDIOC_G_CTRL: + result = v4lconvert_vidioc_g_ctrl(devices[index].convert, arg); + break; + + case VIDIOC_S_CTRL: + result = v4lconvert_vidioc_s_ctrl(devices[index].convert, arg); + break; + case VIDIOC_QUERYCAP: { struct v4l2_capability *cap = arg; - result = syscall(SYS_ioctl, devices[index].fd, VIDIOC_QUERYCAP, cap); + result = SYS_IOCTL(devices[index].fd, VIDIOC_QUERYCAP, cap); if (result == 0) /* We always support read() as we fake it using mmap mode */ cap->capabilities |= V4L2_CAP_READWRITE; @@ -794,7 +849,7 @@ int v4l2_ioctl (int fd, unsigned long int request, ...) } if (devices[index].flags & V4L2_DISABLE_CONVERSION) { - result = syscall(SYS_ioctl, devices[index].fd, VIDIOC_TRY_FMT, + result = SYS_IOCTL(devices[index].fd, VIDIOC_TRY_FMT, dest_fmt); src_fmt = *dest_fmt; } else { @@ -834,7 +889,7 @@ int v4l2_ioctl (int fd, unsigned long int request, ...) break; req_pix_fmt = src_fmt.fmt.pix; - result = syscall(SYS_ioctl, devices[index].fd, VIDIOC_S_FMT, &src_fmt); + result = SYS_IOCTL(devices[index].fd, VIDIOC_S_FMT, &src_fmt); if (result) { saved_err = errno; V4L2_LOG_ERR("setting pixformat: %s\n", strerror(errno)); @@ -884,7 +939,7 @@ int v4l2_ioctl (int fd, unsigned long int request, ...) if (req->count > V4L2_MAX_NO_FRAMES) req->count = V4L2_MAX_NO_FRAMES; - result = syscall(SYS_ioctl, devices[index].fd, VIDIOC_REQBUFS, req); + result = SYS_IOCTL(devices[index].fd, VIDIOC_REQBUFS, req); if (result < 0) break; result = 0; /* some drivers return the number of buffers on success */ @@ -904,8 +959,8 @@ int v4l2_ioctl (int fd, unsigned long int request, ...) /* Do a real query even when converting to let the driver fill in things like buf->field */ - result = syscall(SYS_ioctl, devices[index].fd, VIDIOC_QUERYBUF, buf); - if (result || !converting) + result = SYS_IOCTL(devices[index].fd, VIDIOC_QUERYBUF, buf); + if (result || !v4l2_needs_conversion(index)) break; buf->m.offset = V4L2_MMAP_OFFSET_MAGIC | buf->index; @@ -923,11 +978,11 @@ int v4l2_ioctl (int fd, unsigned long int request, ...) break; /* With some drivers the buffers must be mapped before queuing */ - if (converting) + if (v4l2_needs_conversion(index)) if ((result = v4l2_map_buffers(index))) break; - result = syscall(SYS_ioctl, devices[index].fd, VIDIOC_QBUF, arg); + result = SYS_IOCTL(devices[index].fd, VIDIOC_QBUF, arg); break; case VIDIOC_DQBUF: @@ -938,8 +993,8 @@ int v4l2_ioctl (int fd, unsigned long int request, ...) if ((result = v4l2_deactivate_read_stream(index))) break; - if (!converting) { - result = syscall(SYS_ioctl, devices[index].fd, VIDIOC_DQBUF, buf); + if (!v4l2_needs_conversion(index)) { + result = SYS_IOCTL(devices[index].fd, VIDIOC_DQBUF, buf); if (result) { int saved_err = errno; V4L2_LOG_ERR("dequeuing buf: %s\n", strerror(errno)); @@ -952,7 +1007,7 @@ int v4l2_ioctl (int fd, unsigned long int request, ...) but we need the buffer _now_ to write our converted data to it! */ if (devices[index].convert_mmap_buf == MAP_FAILED) { - devices[index].convert_mmap_buf = (void *)syscall(SYS_mmap2, NULL, + devices[index].convert_mmap_buf = (void *)SYS_MMAP(NULL, (size_t)( devices[index].no_frames * V4L2_FRAME_BUF_SIZE), @@ -997,7 +1052,7 @@ int v4l2_ioctl (int fd, unsigned long int request, ...) break; default: - result = syscall(SYS_ioctl, fd, request, arg); + result = SYS_IOCTL(fd, request, arg); } if (stream_needs_locking) @@ -1018,16 +1073,15 @@ ssize_t v4l2_read (int fd, void* dest, size_t n) struct v4l2_buffer buf; if ((index = v4l2_get_index(fd)) == -1) - return syscall(SYS_read, fd, dest, n); + return SYS_READ(fd, dest, n); pthread_mutex_lock(&devices[index].stream_lock); /* When not converting and the device supports read let the kernel handle it */ if ((devices[index].flags & V4L2_SUPPORTS_READ) && - !v4lconvert_needs_conversion(devices[index].convert, - &devices[index].src_fmt, &devices[index].dest_fmt)) { - result = syscall(SYS_read, fd, dest, n); + !v4l2_needs_conversion(index)) { + result = SYS_READ(fd, dest, n); goto leave; } @@ -1056,7 +1110,7 @@ leave: } void *v4l2_mmap(void *start, size_t length, int prot, int flags, int fd, - __off64_t offset) + int64_t offset) { int index; unsigned int buffer_index; @@ -1076,8 +1130,7 @@ void *v4l2_mmap(void *start, size_t length, int prot, int flags, int fd, return MAP_FAILED; } - return (void *)syscall(SYS_mmap2, start, length, prot, flags, fd, - (__off_t)(offset >> MMAP2_PAGE_SHIFT)); + return (void *)SYS_MMAP(start, length, prot, flags, fd, offset); } pthread_mutex_lock(&devices[index].stream_lock); @@ -1085,15 +1138,14 @@ void *v4l2_mmap(void *start, size_t length, int prot, int flags, int fd, buffer_index = offset & 0xff; if (buffer_index >= devices[index].no_frames || /* Got magic offset and not converting ?? */ - !v4lconvert_needs_conversion(devices[index].convert, - &devices[index].src_fmt, &devices[index].dest_fmt)) { + !v4l2_needs_conversion(index)) { errno = EINVAL; result = MAP_FAILED; goto leave; } if (devices[index].convert_mmap_buf == MAP_FAILED) { - devices[index].convert_mmap_buf = (void *)syscall(SYS_mmap2, NULL, + devices[index].convert_mmap_buf = (void *)SYS_MMAP(NULL, (size_t)( devices[index].no_frames * V4L2_FRAME_BUF_SIZE), @@ -1166,7 +1218,7 @@ int v4l2_munmap(void *_start, size_t length) V4L2_LOG("v4l2 unknown munmap %p, %d\n", start, (int)length); - return syscall(SYS_munmap, _start, length); + return SYS_MUNMAP(_start, length); } /* Misc utility functions */ @@ -1176,7 +1228,7 @@ int v4l2_set_control(int fd, int cid, int value) struct v4l2_control ctrl = { .id = cid }; int result; - if ((result = syscall(SYS_ioctl, fd, VIDIOC_QUERYCTRL, &qctrl))) + if ((result = SYS_IOCTL(fd, VIDIOC_QUERYCTRL, &qctrl))) return result; if (!(qctrl.flags & V4L2_CTRL_FLAG_DISABLED) && @@ -1187,7 +1239,7 @@ int v4l2_set_control(int fd, int cid, int value) ctrl.value = (value * (qctrl.maximum - qctrl.minimum) + 32767) / 65535 + qctrl.minimum; - result = syscall(SYS_ioctl, fd, VIDIOC_S_CTRL, &ctrl); + result = SYS_IOCTL(fd, VIDIOC_S_CTRL, &ctrl); } return result; @@ -1198,13 +1250,13 @@ int v4l2_get_control(int fd, int cid) struct v4l2_queryctrl qctrl = { .id = cid }; struct v4l2_control ctrl = { .id = cid }; - if (syscall(SYS_ioctl, fd, VIDIOC_QUERYCTRL, &qctrl)) + if (SYS_IOCTL(fd, VIDIOC_QUERYCTRL, &qctrl)) return 0; if (qctrl.flags & V4L2_CTRL_FLAG_DISABLED) return 0; - if (syscall(SYS_ioctl, fd, VIDIOC_G_CTRL, &ctrl)) + if (SYS_IOCTL(fd, VIDIOC_G_CTRL, &ctrl)) return 0; return ((ctrl.value - qctrl.minimum) * 65535 + diff --git a/v4l2-apps/libv4l/libv4l2/log.c b/v4l2-apps/libv4l/libv4l2/log.c index c29086ff4..86d71b64e 100644 --- a/v4l2-apps/libv4l/libv4l2/log.c +++ b/v4l2-apps/libv4l/libv4l2/log.c @@ -1,5 +1,7 @@ /* -# (C) 2008 Hans de Goede <j.w.r.degoede@hhs.nl> +# (C) 2008 Elmar Kleijn <elmar_kleijn@hotmail.com> +# (C) 2008 Sjoerd Piepenbrink <need4weed@gmail.com> +# (C) 2008 Hans de Goede <hdegoede@redhat.com> # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by @@ -20,12 +22,7 @@ #include <stdlib.h> #include <string.h> #include <errno.h> -#include <linux/ioctl.h> -/* These headers are not needed by us, but by linux/videodev2.h, - which is broken on some systems and doesn't include them itself :( */ -#include <sys/time.h> -#include <asm/types.h> -/* end broken header workaround includes */ +#include "../libv4lconvert/libv4lsyscall-priv.h" #include <linux/videodev2.h> #include "libv4l2.h" #include "libv4l2-priv.h" @@ -161,7 +158,7 @@ void v4l2_log_ioctl(unsigned long int request, void *arg, int result) struct v4l2_frmsizeenum *frmsize = arg; int pixfmt = frmsize->pixel_format; - fprintf(v4l2_log_file, " index: %u pixelformat: %c%c%c%c", + fprintf(v4l2_log_file, " index: %u pixelformat: %c%c%c%c\n", frmsize->index, pixfmt & 0xff, (pixfmt >> 8) & 0xff, @@ -169,12 +166,12 @@ void v4l2_log_ioctl(unsigned long int request, void *arg, int result) pixfmt >> 24); switch (frmsize->type) { case V4L2_FRMSIZE_TYPE_DISCRETE: - fprintf(v4l2_log_file, " %ux%u\n", frmsize->discrete.width, + fprintf(v4l2_log_file, " %ux%u\n", frmsize->discrete.width, frmsize->discrete.height); break; case V4L2_FRMSIZE_TYPE_CONTINUOUS: case V4L2_FRMSIZE_TYPE_STEPWISE: - fprintf(v4l2_log_file, " %ux%u -> %ux%u\n", + fprintf(v4l2_log_file, " %ux%u -> %ux%u\n", frmsize->stepwise.min_width, frmsize->stepwise.min_height, frmsize->stepwise.max_width, frmsize->stepwise.max_height); break; @@ -186,7 +183,7 @@ void v4l2_log_ioctl(unsigned long int request, void *arg, int result) struct v4l2_frmivalenum *frmival = arg; int pixfmt = frmival->pixel_format; - fprintf(v4l2_log_file, " index: %u pixelformat: %c%c%c%c %ux%u: ", + fprintf(v4l2_log_file, " index: %u pixelformat: %c%c%c%c %ux%u:\n", frmival->index, pixfmt & 0xff, (pixfmt >> 8) & 0xff, @@ -196,12 +193,12 @@ void v4l2_log_ioctl(unsigned long int request, void *arg, int result) frmival->height); switch (frmival->type) { case V4L2_FRMIVAL_TYPE_DISCRETE: - fprintf(v4l2_log_file, "%u/%u\n", frmival->discrete.numerator, + fprintf(v4l2_log_file, " %u/%u\n", frmival->discrete.numerator, frmival->discrete.denominator); break; case V4L2_FRMIVAL_TYPE_CONTINUOUS: case V4L2_FRMIVAL_TYPE_STEPWISE: - fprintf(v4l2_log_file, "%u/%u -> %u/%u\n", + fprintf(v4l2_log_file, " %u/%u -> %u/%u\n", frmival->stepwise.min.numerator, frmival->stepwise.min.denominator, frmival->stepwise.max.numerator, diff --git a/v4l2-apps/libv4l/libv4l2/v4l2convert.c b/v4l2-apps/libv4l/libv4l2/v4l2convert.c index 307a03ce5..4b75a5ebf 100644 --- a/v4l2-apps/libv4l/libv4l2/v4l2convert.c +++ b/v4l2-apps/libv4l/libv4l2/v4l2convert.c @@ -24,17 +24,11 @@ #include <stdarg.h> #include <stdlib.h> -#include <syscall.h> #include <fcntl.h> #include <string.h> #include <sys/ioctl.h> #include <sys/mman.h> -/* These headers are not needed by us, but by linux/videodev2.h, - which is broken on some systems and doesn't include them itself :( */ -#include <sys/time.h> -#include <asm/types.h> -#include <linux/ioctl.h> -/* end broken header workaround includes */ +#include "../libv4lconvert/libv4lsyscall-priv.h" #include <linux/videodev2.h> #include <libv4l2.h> @@ -72,18 +66,18 @@ LIBV4L_PUBLIC int open (const char *file, int oflag, ...) va_start (ap, oflag); mode = va_arg (ap, mode_t); - fd = syscall(SYS_open, file, oflag, mode); + fd = SYS_OPEN(file, oflag, mode); va_end(ap); } else - fd = syscall(SYS_open, file, oflag); + fd = SYS_OPEN(file, oflag, 0); /* end of original open code */ if (fd == -1 || !v4l_device) return fd; /* check that this is an v4l2 device, libv4l2 only supports v4l2 devices */ - if (syscall(SYS_ioctl, fd, VIDIOC_QUERYCAP, &cap)) + if (SYS_IOCTL(fd, VIDIOC_QUERYCAP, &cap)) return fd; /* libv4l2 only adds functionality to capture capable devices */ @@ -92,11 +86,12 @@ LIBV4L_PUBLIC int open (const char *file, int oflag, ...) /* Try to Register with libv4l2 (in case of failure pass the fd to the application as is) */ - v4l2_fd_open(fd, V4L2_ENABLE_ENUM_FMT_EMULATION); + v4l2_fd_open(fd, 0); return fd; } +#ifdef linux LIBV4L_PUBLIC int open64(const char *file, int oflag, ...) { int fd; @@ -119,6 +114,7 @@ LIBV4L_PUBLIC int open64(const char *file, int oflag, ...) return fd; } +#endif LIBV4L_PUBLIC int close(int fd) { @@ -153,11 +149,13 @@ LIBV4L_PUBLIC void *mmap(void *start, size_t length, int prot, int flags, int fd return v4l2_mmap(start, length, prot, flags, fd, offset); } +#ifdef linux LIBV4L_PUBLIC void *mmap64(void *start, size_t length, int prot, int flags, int fd, __off64_t offset) { return v4l2_mmap(start, length, prot, flags, fd, offset); } +#endif LIBV4L_PUBLIC int munmap(void *start, size_t length) { diff --git a/v4l2-apps/libv4l/libv4lconvert/Makefile b/v4l2-apps/libv4l/libv4lconvert/Makefile index f779011b4..036649e86 100644 --- a/v4l2-apps/libv4l/libv4lconvert/Makefile +++ b/v4l2-apps/libv4l/libv4lconvert/Makefile @@ -3,6 +3,8 @@ override CPPFLAGS += -I../include -I../../../include -fvisibility=hidden CFLAGS := -g -O1 CFLAGS += -Wall -Wno-unused -Wpointer-arith -Wstrict-prototypes -Wmissing-prototypes +LIBS_libv4lconvert = -lrt -lm + ifeq ($(LINKTYPE),static) CONVERT_LIB = libv4lconvert.a else @@ -12,8 +14,11 @@ endif CONVERT_OBJS = libv4lconvert.o tinyjpeg.o sn9c10x.o sn9c20x.o pac207.o \ mr97310a.o flip.o crop.o jidctflt.o spca561-decompress.o \ - rgbyuv.o spca501.o sq905c.o bayer.o hm12.o -TARGETS = $(CONVERT_LIB) libv4lconvert.pc + rgbyuv.o spca501.o sq905c.o bayer.o hm12.o helper.o \ + control/libv4lcontrol.o processing/libv4lprocessing.o \ + processing/whitebalance.o processing/autogain.o \ + processing/gamma.o +TARGETS = $(CONVERT_LIB) libv4lconvert.pc ov511-decomp ov518-decomp INCLUDES = ../include/libv4lconvert.h ifeq ($(LIB_RELEASE),) @@ -28,8 +33,12 @@ ifeq ($(LIBDIR),) LIBDIR = $(PREFIX)/lib endif +override CPPFLAGS += -DLIBDIR=\"$(LIBDIR)\" + all: $(TARGETS) +-include $(CONVERT_OBJS:.o=.d) + $(CONVERT_LIB): $(CONVERT_OBJS) libv4lconvert.pc: @@ -40,32 +49,37 @@ libv4lconvert.pc: @echo 'Description: v4l format conversion library' >> libv4lconvert.pc @echo 'Version: '$(V4L2_LIB_VERSION) >> libv4lconvert.pc @echo 'Libs: -L$${libdir} -lv4lconvert' >> libv4lconvert.pc + @echo 'Libs.private: -lrt -lm' >> libv4lconvert.pc @echo 'Cflags: -I$${prefix}/include' >> libv4lconvert.pc install: all mkdir -p $(DESTDIR)$(PREFIX)/include install -p -m 644 $(INCLUDES) $(DESTDIR)$(PREFIX)/include - mkdir -p $(DESTDIR)$(LIBDIR) + mkdir -p $(DESTDIR)$(LIBDIR)/libv4l ifeq ($(LINKTYPE),static) - mkdir -p $(DESTDIR)$(LIBDIR) install -m 644 $(CONVERT_LIB) $(DESTDIR)$(LIBDIR) else install -m 755 $(CONVERT_LIB).$(LIB_RELEASE) $(DESTDIR)$(LIBDIR) cd $(DESTDIR)$(LIBDIR) && \ ln -f -s $(CONVERT_LIB).$(LIB_RELEASE) $(CONVERT_LIB) endif + install -m 755 *-decomp $(DESTDIR)$(LIBDIR)/libv4l mkdir -p $(DESTDIR)$(LIBDIR)/pkgconfig install -m 644 libv4lconvert.pc $(DESTDIR)$(LIBDIR)/pkgconfig clean:: - rm -f *.a *.so* *.o *.d libv4lconvert.pc log *~ *.orig *.rej + rm -f *.a *.so* *.o *.d */*.o */*.d libv4lconvert.pc log *~ */*~ + rm -f *.orig *.rej */*.orig */*.rej DEADJOE */DEADJOE *-decomp %.o: %.c - $(CC) -c -MMD $(CPPFLAGS) $(CFLAGS) -o $@ $< + $(CC) -Wp,-MMD,"$*.d",-MQ,"$@",-MP -c $(CPPFLAGS) $(CFLAGS) -o $@ $< %.so: - $(CC) -shared $(LDFLAGS) -Wl,-soname,$@.$(LIB_RELEASE) -o $@.$(LIB_RELEASE) $^ + $(CC) -shared $(LDFLAGS) -Wl,-soname,$@.$(LIB_RELEASE) -o $@.$(LIB_RELEASE) $^ $(LIBS_$*) ln -f -s $@.$(LIB_RELEASE) $@ %.a: $(AR) cqs $@ $^ + +ov511-decomp: ov511-decomp.o +ov518-decomp: ov518-decomp.o diff --git a/v4l2-apps/libv4l/libv4lconvert/control/libv4lcontrol-priv.h b/v4l2-apps/libv4l/libv4lconvert/control/libv4lcontrol-priv.h new file mode 100644 index 000000000..6211ee22a --- /dev/null +++ b/v4l2-apps/libv4l/libv4lconvert/control/libv4lcontrol-priv.h @@ -0,0 +1,54 @@ +/* +# (C) 2008-2009 Elmar Kleijn <elmar_kleijn@hotmail.com> +# (C) 2008-2009 Sjoerd Piepenbrink <need4weed@gmail.com> +# (C) 2008-2009 Radjnies Bhansingh <radjnies@gmail.com> +# (C) 2008-2009 Hans de Goede <hdegoede@redhat.com> + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef __LIBV4LCONTROL_PRIV_H +#define __LIBV4LCONTROL_PRIV_H + +#define V4LCONTROL_SHM_SIZE 4096 + +#define V4LCONTROL_SUPPORTS_NEXT_CTRL 0x01 + +struct v4lcontrol_flags_info; + +struct v4lcontrol_data { + int fd; /* Device fd */ + int flags; /* Flags for this device */ + int priv_flags; /* Internal use only flags */ + int controls; /* Which controls to use for this device */ + unsigned int *shm_values; /* shared memory control value store */ + unsigned int old_values[V4LCONTROL_COUNT]; /* for controls_changed() */ + const struct v4lcontrol_flags_info *flags_info; +}; + +struct v4lcontrol_flags_info { + unsigned short vendor_id; + unsigned short product_id; + unsigned short product_mask; + const char *dmi_board_vendor; + const char *dmi_board_name; +/* We could also use the USB manufacturer and product strings some devices have + const char *manufacturer; + const char *product; */ + int flags; + int default_gamma; +}; + +#endif diff --git a/v4l2-apps/libv4l/libv4lconvert/control/libv4lcontrol.c b/v4l2-apps/libv4l/libv4lconvert/control/libv4lcontrol.c new file mode 100644 index 000000000..4e600351b --- /dev/null +++ b/v4l2-apps/libv4l/libv4lconvert/control/libv4lcontrol.c @@ -0,0 +1,504 @@ +/* +# (C) 2008-2009 Elmar Kleijn <elmar_kleijn@hotmail.com> +# (C) 2008-2009 Sjoerd Piepenbrink <need4weed@gmail.com> +# (C) 2008-2009 Radjnies Bhansingh <radjnies@gmail.com> +# (C) 2008-2009 Hans de Goede <hdegoede@redhat.com> + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include <sys/types.h> +#include <sys/mman.h> +#include <fcntl.h> +#include <sys/stat.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include "libv4lcontrol.h" +#include "libv4lcontrol-priv.h" +#include "../libv4lsyscall-priv.h" +#include <linux/videodev2.h> + +#define ARRAY_SIZE(x) ((int)sizeof(x)/(int)sizeof((x)[0])) + +/* Workaround these potentially missing from videodev2.h */ +#ifndef V4L2_IN_ST_HFLIP +#define V4L2_IN_ST_HFLIP 0x00000010 /* Frames are flipped horizontally */ +#endif + +#ifndef V4L2_IN_ST_VFLIP +#define V4L2_IN_ST_VFLIP 0x00000020 /* Frames are flipped vertically */ +#endif + + +/* List of cams which need special flags */ +static const struct v4lcontrol_flags_info v4lcontrol_flags[] = { +/* First: Upside down devices */ + /* Philips SPC200NC */ + { 0x0471, 0x0325, 0, NULL, NULL, V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED }, + /* Philips SPC300NC */ + { 0x0471, 0x0326, 0, NULL, NULL, V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED }, + /* Philips SPC210NC */ + { 0x0471, 0x032d, 0, NULL, NULL, V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED }, + /* Genius E-M 112 (also want whitebalance by default) */ + { 0x093a, 0x2476, 0, NULL, NULL, + V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED|V4LCONTROL_WANTS_WB, 1500 }, + /* Asus N50Vn laptop */ + { 0x04f2, 0xb106, 0, "ASUSTeK Computer Inc. ", "N50Vn ", + V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED }, +/* Second: devices which should use some software processing by default */ + /* Pac207 based devices */ + { 0x041e, 0x4028, 0, NULL, NULL, V4LCONTROL_WANTS_WB, 1500 }, + { 0x093a, 0x2460, 0x1f, NULL, NULL, V4LCONTROL_WANTS_WB, 1500 }, + { 0x145f, 0x013a, 0, NULL, NULL, V4LCONTROL_WANTS_WB, 1500 }, + { 0x2001, 0xf115, 0, NULL, NULL, V4LCONTROL_WANTS_WB, 1500 }, + /* Pac7302 based devices */ + { 0x093a, 0x2620, 0x0f, NULL, NULL, + V4LCONTROL_ROTATED_90_JPEG|V4LCONTROL_WANTS_WB }, + /* Pac7311 based devices */ + { 0x093a, 0x2600, 0x0f, NULL, NULL, V4LCONTROL_WANTS_WB }, + /* sq905 devices */ + { 0x2770, 0x9120, 0, NULL, NULL, V4LCONTROL_WANTS_WB }, + /* spca561 revison 12a devices */ + { 0x041e, 0x403b, 0, NULL, NULL, V4LCONTROL_WANTS_WB_AUTOGAIN }, + { 0x046d, 0x0928, 7, NULL, NULL, V4LCONTROL_WANTS_WB_AUTOGAIN }, + /* logitech quickcam express stv06xx + pb0100 */ + { 0x046d, 0x0840, 0, NULL, NULL, V4LCONTROL_WANTS_WB }, + /* logitech quickcam messenger variants, st6422 */ + { 0x046d, 0x08f0, 0, NULL, NULL, V4LCONTROL_WANTS_AUTOGAIN }, + { 0x046d, 0x08f5, 0, NULL, NULL, V4LCONTROL_WANTS_AUTOGAIN }, + { 0x046d, 0x08f6, 0, NULL, NULL, V4LCONTROL_WANTS_AUTOGAIN }, + { 0x046d, 0x08da, 0, NULL, NULL, V4LCONTROL_WANTS_AUTOGAIN }, +}; + +static const struct v4l2_queryctrl fake_controls[]; + +static void v4lcontrol_init_flags(struct v4lcontrol_data *data) +{ + struct stat st; + FILE *f; + char sysfs_name[512]; + unsigned short vendor_id = 0; + unsigned short product_id = 0; + char dmi_board_vendor[512] = ""; + char dmi_board_name[512]= ""; + int i, minor; + char c, *s, buf[32]; + struct v4l2_input input; + + if ((SYS_IOCTL(data->fd, VIDIOC_G_INPUT, &input.index) == 0) && + (SYS_IOCTL(data->fd, VIDIOC_ENUMINPUT, &input) == 0)) { + if (input.status & V4L2_IN_ST_HFLIP) + data->flags |= V4LCONTROL_HFLIPPED; + if (input.status & V4L2_IN_ST_VFLIP) + data->flags |= V4LCONTROL_VFLIPPED; + } + + if (fstat(data->fd, &st) || !S_ISCHR(st.st_mode)) { + return; /* Should never happen */ + } + + /* <Sigh> find ourselve in sysfs */ + for (i = 0; i < 256; i++) { + snprintf(sysfs_name, sizeof(sysfs_name), + "/sys/class/video4linux/video%d/dev", i); + f = fopen(sysfs_name, "r"); + if (!f) + continue; + + s = fgets(buf, sizeof(buf), f); + fclose(f); + + if (s && sscanf(buf, "%*d:%d%c", &minor, &c) == 2 && c == '\n' && + minor == minor(st.st_rdev)) + break; + } + if (i == 256) + return; /* Not found, sysfs not mounted? */ + + /* Get vendor and product ID */ + snprintf(sysfs_name, sizeof(sysfs_name), + "/sys/class/video4linux/video%d/device/modalias", i); + f = fopen(sysfs_name, "r"); + if (f) { + s = fgets(buf, sizeof(buf), f); + fclose(f); + + if (!s || + sscanf(s, "usb:v%4hxp%4hx%c", &vendor_id, &product_id, &c) != 3 || + c != 'd') + return; /* Not an USB device */ + } else { + /* Try again assuming the device link points to the usb + device instead of the usb interface (bug in older versions + of gspca) */ + + /* Get product ID */ + snprintf(sysfs_name, sizeof(sysfs_name), + "/sys/class/video4linux/video%d/device/idVendor", i); + f = fopen(sysfs_name, "r"); + if (!f) + return; /* Not an USB device (or no sysfs) */ + + s = fgets(buf, sizeof(buf), f); + fclose(f); + + if (!s || sscanf(s, "%04hx%c", &vendor_id, &c) != 2 || c != '\n') + return; /* Should never happen */ + + /* Get product ID */ + snprintf(sysfs_name, sizeof(sysfs_name), + "/sys/class/video4linux/video%d/device/idProduct", i); + f = fopen(sysfs_name, "r"); + if (!f) + return; /* Should never happen */ + + s = fgets(buf, sizeof(buf), f); + fclose(f); + + if (!s || sscanf(s, "%04hx%c", &product_id, &c) != 2 || c != '\n') + return; /* Should never happen */ + } + + /* Get DMI board vendor and name */ + f = fopen("/sys/devices/virtual/dmi/id/board_vendor", "r"); + if (f) { + s = fgets(dmi_board_vendor, sizeof(dmi_board_vendor), f); + if (s) + s[strlen(s) - 1] = 0; + fclose(f); + } + + f = fopen("/sys/devices/virtual/dmi/id/board_name", "r"); + if (f) { + s = fgets(dmi_board_name, sizeof(dmi_board_name), f); + if (s) + s[strlen(s) - 1] = 0; + fclose(f); + } + + for (i = 0; i < ARRAY_SIZE(v4lcontrol_flags); i++) + if (v4lcontrol_flags[i].vendor_id == vendor_id && + v4lcontrol_flags[i].product_id == + (product_id & ~v4lcontrol_flags[i].product_mask) && + (v4lcontrol_flags[i].dmi_board_vendor == NULL || + !strcmp(v4lcontrol_flags[i].dmi_board_vendor, dmi_board_vendor)) && + (v4lcontrol_flags[i].dmi_board_name == NULL || + !strcmp(v4lcontrol_flags[i].dmi_board_name, dmi_board_name))) { + data->flags |= v4lcontrol_flags[i].flags; + data->flags_info = &v4lcontrol_flags[i]; + break; + } +} + +struct v4lcontrol_data *v4lcontrol_create(int fd, int always_needs_conversion) +{ + int shm_fd; + int i, rc, init = 0; + char *s, shm_name[256]; + struct v4l2_capability cap; + struct v4l2_queryctrl ctrl; + + struct v4lcontrol_data *data = calloc(1, sizeof(struct v4lcontrol_data)); + + if (!data) + return NULL; + + data->fd = fd; + + v4lcontrol_init_flags(data); + + /* Allow overriding through environment */ + if ((s = getenv("LIBV4LCONTROL_FLAGS"))) + data->flags = strtol(s, NULL, 0); + + ctrl.id = V4L2_CTRL_FLAG_NEXT_CTRL; + if (SYS_IOCTL(data->fd, VIDIOC_QUERYCTRL, &ctrl) == 0) + data->priv_flags |= V4LCONTROL_SUPPORTS_NEXT_CTRL; + + /* If the device always needs conversion, we can add fake controls at no cost + (no cost when not activated by the user that is) */ + if (always_needs_conversion || v4lcontrol_needs_conversion(data)) { + for (i = 0; i < V4LCONTROL_AUTO_ENABLE_COUNT; i++) { + ctrl.id = fake_controls[i].id; + rc = SYS_IOCTL(data->fd, VIDIOC_QUERYCTRL, &ctrl); + if (rc == -1 || (rc == 0 && (ctrl.flags & V4L2_CTRL_FLAG_DISABLED))) + data->controls |= 1 << i; + } + } + + if (data->flags & V4LCONTROL_WANTS_AUTOGAIN) + data->controls |= 1 << V4LCONTROL_AUTOGAIN | + 1 << V4LCONTROL_AUTOGAIN_TARGET; + + /* Allow overriding through environment */ + if ((s = getenv("LIBV4LCONTROL_CONTROLS"))) + data->controls = strtol(s, NULL, 0); + + if (data->controls == 0) + return data; /* No need to create a shared memory segment */ + + SYS_IOCTL(fd, VIDIOC_QUERYCAP, &cap); + snprintf(shm_name, 256, "/%s:%s", cap.bus_info, cap.card); + + /* / is not allowed inside shm names */ + for (i = 1; shm_name[i]; i++) + if (shm_name[i] == '/') + shm_name[i] = '-'; + + /* Open the shared memory object identified by shm_name */ + if ((shm_fd = shm_open(shm_name, (O_CREAT | O_EXCL | O_RDWR), + (S_IREAD | S_IWRITE))) >= 0) + init = 1; + else if ((shm_fd = shm_open(shm_name, O_RDWR, (S_IREAD | S_IWRITE))) < 0) + goto error; + + /* Set the shared memory size */ + ftruncate(shm_fd, V4LCONTROL_SHM_SIZE); + + /* Retreive a pointer to the shm object */ + data->shm_values = mmap(NULL, V4LCONTROL_SHM_SIZE, (PROT_READ | PROT_WRITE), + MAP_SHARED, shm_fd, 0); + close(shm_fd); + + if (data->shm_values == MAP_FAILED) + goto error; + + if (init) { + /* Initialize the new shm object we created */ + memset(data->shm_values, 0, sizeof(V4LCONTROL_SHM_SIZE)); + + for (i = 0; i < V4LCONTROL_COUNT; i++) + data->shm_values[i] = fake_controls[i].default_value; + + if (data->flags & V4LCONTROL_WANTS_WB) + data->shm_values[V4LCONTROL_WHITEBALANCE] = 1; + + if (data->flags_info && data->flags_info->default_gamma) + data->shm_values[V4LCONTROL_GAMMA] = data->flags_info->default_gamma; + } + + return data; + +error: + free(data); + return NULL; +} + +void v4lcontrol_destroy(struct v4lcontrol_data *data) +{ + if (data->controls) + munmap(data->shm_values, V4LCONTROL_SHM_SIZE); + free(data); +} + +/* FIXME get better CID's for normalize */ +static const struct v4l2_queryctrl fake_controls[V4LCONTROL_COUNT] = { +{ + .id = V4L2_CID_AUTO_WHITE_BALANCE, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Whitebalance (software)", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0, + .flags = 0 +}, +{ + .id = V4L2_CID_HFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Horizontal flip (sw)", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0, + .flags = 0 +}, +{ + .id = V4L2_CID_VFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Vertical flip (sw)", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0, + .flags = 0 +}, +{ + .id = V4L2_CID_GAMMA, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Gamma (software)", + .minimum = 500, /* == 0.5 */ + .maximum = 3000, /* == 3.0 */ + .step = 1, + .default_value = 1000, /* == 1.0 */ + .flags = 0 +}, +{}, /* Dummy place holder for V4LCONTROL_AUTO_ENABLE_COUNT */ +{ + .id = V4L2_CID_AUTOGAIN, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Auto Gain (software)", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 1, + .flags = 0 +}, +{ + .id = V4L2_CTRL_CLASS_USER + 0x2000, /* FIXME */ + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Auto Gain target", + .minimum = 0, + .maximum = 255, + .step = 1, + .default_value = 100, + .flags = 0 +}, +}; + +static void v4lcontrol_copy_queryctrl(struct v4lcontrol_data *data, + struct v4l2_queryctrl *ctrl, int i) +{ + memcpy(ctrl, &fake_controls[i], sizeof(struct v4l2_queryctrl)); + + /* Hmm, not pretty */ + if (ctrl->id == V4L2_CID_AUTO_WHITE_BALANCE && + (data->flags & V4LCONTROL_WANTS_WB)) + ctrl->default_value = 1; + + if (ctrl->id == V4L2_CID_GAMMA && data->flags_info && + data->flags_info->default_gamma) + ctrl->default_value = data->flags_info->default_gamma; +} + +int v4lcontrol_vidioc_queryctrl(struct v4lcontrol_data *data, void *arg) +{ + int i; + struct v4l2_queryctrl *ctrl = arg; + int retval; + __u32 orig_id=ctrl->id; + + /* if we have an exact match return it */ + for (i = 0; i < V4LCONTROL_COUNT; i++) + if ((data->controls & (1 << i)) && + ctrl->id == fake_controls[i].id) { + v4lcontrol_copy_queryctrl(data, ctrl, i); + return 0; + } + + /* find out what the kernel driver would respond. */ + retval = SYS_IOCTL(data->fd, VIDIOC_QUERYCTRL, arg); + + if ((data->priv_flags & V4LCONTROL_SUPPORTS_NEXT_CTRL) && + (orig_id & V4L2_CTRL_FLAG_NEXT_CTRL)) { + /* If the hardware has no more controls check if we still have any + fake controls with a higher id then the hardware's highest */ + if (retval) + ctrl->id = V4L2_CTRL_FLAG_NEXT_CTRL; + + /* If any of our controls have an id > orig_id but less than + ctrl->id then return that control instead. Note we do not + break when we have a match, but keep iterating, so that + we end up with the fake ctrl with the lowest CID > orig_id. */ + for (i = 0; i < V4LCONTROL_COUNT; i++) + if ((data->controls & (1 << i)) && + (fake_controls[i].id > (orig_id & ~V4L2_CTRL_FLAG_NEXT_CTRL)) && + (fake_controls[i].id <= ctrl->id)) { + v4lcontrol_copy_queryctrl(data, ctrl, i); + retval = 0; + } + } + + return retval; +} + +int v4lcontrol_vidioc_g_ctrl(struct v4lcontrol_data *data, void *arg) +{ + int i; + struct v4l2_control *ctrl = arg; + + for (i = 0; i < V4LCONTROL_COUNT; i++) + if ((data->controls & (1 << i)) && + ctrl->id == fake_controls[i].id) { + ctrl->value = data->shm_values[i]; + return 0; + } + + return SYS_IOCTL(data->fd, VIDIOC_G_CTRL, arg); +} + +int v4lcontrol_vidioc_s_ctrl(struct v4lcontrol_data *data, void *arg) +{ + int i; + struct v4l2_control *ctrl = arg; + + for (i = 0; i < V4LCONTROL_COUNT; i++) + if ((data->controls & (1 << i)) && + ctrl->id == fake_controls[i].id) { + if (ctrl->value > fake_controls[i].maximum || + ctrl->value < fake_controls[i].minimum) { + errno = EINVAL; + return -1; + } + + data->shm_values[i] = ctrl->value; + return 0; + } + + return SYS_IOCTL(data->fd, VIDIOC_S_CTRL, arg); +} + +int v4lcontrol_get_flags(struct v4lcontrol_data *data) +{ + return data->flags; +} + +int v4lcontrol_get_ctrl(struct v4lcontrol_data *data, int ctrl) +{ + if (data->controls & (1 << ctrl)) { + /* Special case for devices with flipped input */ + if ((ctrl == V4LCONTROL_HFLIP && (data->flags & V4LCONTROL_HFLIPPED)) || + (ctrl == V4LCONTROL_VFLIP && (data->flags & V4LCONTROL_VFLIPPED))) + return !data->shm_values[ctrl]; + + return data->shm_values[ctrl]; + } + + return 0; +} + +int v4lcontrol_controls_changed(struct v4lcontrol_data *data) +{ + int res; + + if (!data->controls) + return 0; + + res = memcmp(data->shm_values, data->old_values, + V4LCONTROL_COUNT * sizeof(unsigned int)); + + memcpy(data->old_values, data->shm_values, + V4LCONTROL_COUNT * sizeof(unsigned int)); + + return res; +} + +/* See the comment about this in libv4lconvert.h */ +int v4lcontrol_needs_conversion(struct v4lcontrol_data *data) { + return data->flags || data->controls; +} diff --git a/v4l2-apps/libv4l/libv4lconvert/control/libv4lcontrol.h b/v4l2-apps/libv4l/libv4lconvert/control/libv4lcontrol.h new file mode 100644 index 000000000..e29fde3d4 --- /dev/null +++ b/v4l2-apps/libv4l/libv4lconvert/control/libv4lcontrol.h @@ -0,0 +1,70 @@ +/* +# (C) 2008-2009 Elmar Kleijn <elmar_kleijn@hotmail.com> +# (C) 2008-2009 Sjoerd Piepenbrink <need4weed@gmail.com> +# (C) 2008-2009 Radjnies Bhansingh <radjnies@gmail.com> +# (C) 2008-2009 Hans de Goede <hdegoede@redhat.com> + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef __LIBV4LCONTROL_H +#define __LIBV4LCONTROL_H + +/* Flags */ +#define V4LCONTROL_HFLIPPED 0x01 +#define V4LCONTROL_VFLIPPED 0x02 +#define V4LCONTROL_ROTATED_90_JPEG 0x04 +#define V4LCONTROL_WANTS_WB 0x08 +#define V4LCONTROL_WANTS_AUTOGAIN 0x10 + +/* Masks */ +#define V4LCONTROL_WANTS_WB_AUTOGAIN (V4LCONTROL_WANTS_WB | V4LCONTROL_WANTS_AUTOGAIN) + +/* Controls */ +enum { + V4LCONTROL_WHITEBALANCE, + V4LCONTROL_HFLIP, + V4LCONTROL_VFLIP, + V4LCONTROL_GAMMA, + /* All fake controls above here are auto enabled when not present in hw */ + V4LCONTROL_AUTO_ENABLE_COUNT, + V4LCONTROL_AUTOGAIN, + V4LCONTROL_AUTOGAIN_TARGET, + V4LCONTROL_COUNT + }; + +struct v4lcontrol_data; + +struct v4lcontrol_data* v4lcontrol_create(int fd, int always_needs_conversion); +void v4lcontrol_destroy(struct v4lcontrol_data *data); + +/* Functions used by v4lprocessing to get the control state */ +int v4lcontrol_get_flags(struct v4lcontrol_data *data); +int v4lcontrol_get_ctrl(struct v4lcontrol_data *data, int ctrl); +/* Check if the controls have changed since the last time this function + was called */ +int v4lcontrol_controls_changed(struct v4lcontrol_data *data); +/* Check if we must go through the conversion path (and thus alloc conversion + buffers, etc. in libv4l2). Note this always return 1 if we *may* need + rotate90 / flipping / processing, as if we actually need this may change + on the fly while the stream is active. */ +int v4lcontrol_needs_conversion(struct v4lcontrol_data *data); + +/* Functions used by v4lconvert to pass vidioc calls from libv4l2 */ +int v4lcontrol_vidioc_queryctrl(struct v4lcontrol_data *data, void *arg); +int v4lcontrol_vidioc_g_ctrl(struct v4lcontrol_data *data, void *arg); +int v4lcontrol_vidioc_s_ctrl(struct v4lcontrol_data *data, void *arg); + +#endif diff --git a/v4l2-apps/libv4l/libv4lconvert/crop.c b/v4l2-apps/libv4l/libv4lconvert/crop.c index 4294fbeaf..46c87f0ce 100644 --- a/v4l2-apps/libv4l/libv4lconvert/crop.c +++ b/v4l2-apps/libv4l/libv4lconvert/crop.c @@ -69,8 +69,8 @@ static void v4lconvert_reduceandcrop_yuv420( int x,y; int dest_height_half = dest_fmt->fmt.pix.height / 2; int dest_width_half = dest_fmt->fmt.pix.width / 2; - int startx = src_fmt->fmt.pix.width / 2 - dest_fmt->fmt.pix.width; - int starty = src_fmt->fmt.pix.height / 2 - dest_fmt->fmt.pix.height; + int startx = (src_fmt->fmt.pix.width / 2 - dest_fmt->fmt.pix.width) & ~1; + int starty = (src_fmt->fmt.pix.height / 2 - dest_fmt->fmt.pix.height) & ~1; unsigned char *mysrc, *mysrc2; /* Y */ @@ -113,8 +113,8 @@ static void v4lconvert_crop_yuv420(unsigned char *src, unsigned char *dest, const struct v4l2_format *src_fmt, const struct v4l2_format *dest_fmt) { int x; - int startx = (src_fmt->fmt.pix.width - dest_fmt->fmt.pix.width) / 2; - int starty = (src_fmt->fmt.pix.height - dest_fmt->fmt.pix.height) / 2; + int startx = ((src_fmt->fmt.pix.width - dest_fmt->fmt.pix.width) / 2) & ~1; + int starty = ((src_fmt->fmt.pix.height - dest_fmt->fmt.pix.height) / 2) & ~1; unsigned char *mysrc = src + starty * src_fmt->fmt.pix.bytesperline + startx; /* Y */ @@ -143,26 +143,146 @@ static void v4lconvert_crop_yuv420(unsigned char *src, unsigned char *dest, } } +/* Ok, so this is not really cropping, but more the reverse, whatever */ +static void v4lconvert_add_border_rgbbgr24( + unsigned char *src, unsigned char *dest, + const struct v4l2_format *src_fmt, const struct v4l2_format *dest_fmt) +{ + int y; + int borderx = (dest_fmt->fmt.pix.width - src_fmt->fmt.pix.width) / 2; + int bordery = (dest_fmt->fmt.pix.height - src_fmt->fmt.pix.height) / 2; + + for (y = 0; y < bordery; y++) { + memset(dest, 0, dest_fmt->fmt.pix.width * 3); + dest += dest_fmt->fmt.pix.bytesperline; + } + + for (y = 0; y < src_fmt->fmt.pix.height; y++) { + memset(dest, 0, borderx * 3); + dest += borderx * 3; + + memcpy(dest, src, src_fmt->fmt.pix.width * 3); + src += src_fmt->fmt.pix.bytesperline; + dest += src_fmt->fmt.pix.width * 3; + + memset(dest, 0, borderx * 3); + dest += dest_fmt->fmt.pix.bytesperline - + (borderx + src_fmt->fmt.pix.width) * 3; + } + + for (y = 0; y < bordery; y++) { + memset(dest, 0, dest_fmt->fmt.pix.width * 3); + dest += dest_fmt->fmt.pix.bytesperline; + } +} + +static void v4lconvert_add_border_yuv420( + unsigned char *src, unsigned char *dest, + const struct v4l2_format *src_fmt, const struct v4l2_format *dest_fmt) +{ + int y; + int borderx = ((dest_fmt->fmt.pix.width - src_fmt->fmt.pix.width) / 2) & ~1; + int bordery = ((dest_fmt->fmt.pix.height - src_fmt->fmt.pix.height) / 2) & ~1; + + /* Y */ + for (y = 0; y < bordery; y++) { + memset(dest, 16, dest_fmt->fmt.pix.width); + dest += dest_fmt->fmt.pix.bytesperline; + } + + for (y = 0; y < src_fmt->fmt.pix.height; y++) { + memset(dest, 16, borderx); + dest += borderx; + + memcpy(dest, src, src_fmt->fmt.pix.width); + src += src_fmt->fmt.pix.bytesperline; + dest += src_fmt->fmt.pix.width; + + memset(dest, 16, borderx); + dest += dest_fmt->fmt.pix.bytesperline - + (borderx + src_fmt->fmt.pix.width); + } + + for (y = 0; y < bordery; y++) { + memset(dest, 16, dest_fmt->fmt.pix.width); + dest += dest_fmt->fmt.pix.bytesperline; + } + + /* U */ + for (y = 0; y < bordery / 2; y++) { + memset(dest, 128, dest_fmt->fmt.pix.width / 2); + dest += dest_fmt->fmt.pix.bytesperline / 2; + } + + for (y = 0; y < src_fmt->fmt.pix.height / 2; y++) { + memset(dest, 128, borderx / 2); + dest += borderx / 2; + + memcpy(dest, src, src_fmt->fmt.pix.width / 2); + src += src_fmt->fmt.pix.bytesperline / 2; + dest += src_fmt->fmt.pix.width / 2; + + memset(dest, 128, borderx / 2); + dest += (dest_fmt->fmt.pix.bytesperline - + (borderx + src_fmt->fmt.pix.width)) / 2; + } + + for (y = 0; y < bordery / 2; y++) { + memset(dest, 128, dest_fmt->fmt.pix.width / 2); + dest += dest_fmt->fmt.pix.bytesperline / 2; + } + + /* V */ + for (y = 0; y < bordery / 2; y++) { + memset(dest, 128, dest_fmt->fmt.pix.width / 2); + dest += dest_fmt->fmt.pix.bytesperline / 2; + } + + for (y = 0; y < src_fmt->fmt.pix.height / 2; y++) { + memset(dest, 128, borderx / 2); + dest += borderx / 2; + + memcpy(dest, src, src_fmt->fmt.pix.width / 2); + src += src_fmt->fmt.pix.bytesperline / 2; + dest += src_fmt->fmt.pix.width / 2; + + memset(dest, 128, borderx / 2); + dest += (dest_fmt->fmt.pix.bytesperline - + (borderx + src_fmt->fmt.pix.width)) / 2; + } + + for (y = 0; y < bordery / 2; y++) { + memset(dest, 128, dest_fmt->fmt.pix.width / 2); + dest += dest_fmt->fmt.pix.bytesperline / 2; + } +} + void v4lconvert_crop(unsigned char *src, unsigned char *dest, const struct v4l2_format *src_fmt, const struct v4l2_format *dest_fmt) { switch (dest_fmt->fmt.pix.pixelformat) { case V4L2_PIX_FMT_RGB24: case V4L2_PIX_FMT_BGR24: - if (src_fmt->fmt.pix.width >= 2 * dest_fmt->fmt.pix.width && - src_fmt->fmt.pix.height >= 2 * dest_fmt->fmt.pix.height) + if (src_fmt->fmt.pix.width <= dest_fmt->fmt.pix.width && + src_fmt->fmt.pix.height <= dest_fmt->fmt.pix.height) + v4lconvert_add_border_rgbbgr24(src, dest, src_fmt, dest_fmt); + else if (src_fmt->fmt.pix.width >= 2 * dest_fmt->fmt.pix.width && + src_fmt->fmt.pix.height >= 2 * dest_fmt->fmt.pix.height) v4lconvert_reduceandcrop_rgbbgr24(src, dest, src_fmt, dest_fmt); else v4lconvert_crop_rgbbgr24(src, dest, src_fmt, dest_fmt); break; + case V4L2_PIX_FMT_YUV420: case V4L2_PIX_FMT_YVU420: - if (src_fmt->fmt.pix.width >= 2 * dest_fmt->fmt.pix.width && - src_fmt->fmt.pix.height >= 2 * dest_fmt->fmt.pix.height) + if (src_fmt->fmt.pix.width <= dest_fmt->fmt.pix.width && + src_fmt->fmt.pix.height <= dest_fmt->fmt.pix.height) + v4lconvert_add_border_yuv420(src, dest, src_fmt, dest_fmt); + else if (src_fmt->fmt.pix.width >= 2 * dest_fmt->fmt.pix.width && + src_fmt->fmt.pix.height >= 2 * dest_fmt->fmt.pix.height) v4lconvert_reduceandcrop_yuv420(src, dest, src_fmt, dest_fmt); else v4lconvert_crop_yuv420(src, dest, src_fmt, dest_fmt); - break; } } diff --git a/v4l2-apps/libv4l/libv4lconvert/flip.c b/v4l2-apps/libv4l/libv4lconvert/flip.c index f47afde72..8c4d8233c 100644 --- a/v4l2-apps/libv4l/libv4lconvert/flip.c +++ b/v4l2-apps/libv4l/libv4lconvert/flip.c @@ -20,8 +20,100 @@ */ +#include <string.h> #include "libv4lconvert-priv.h" +static void v4lconvert_vflip_rgbbgr24(unsigned char *src, unsigned char *dest, + struct v4l2_format *fmt) +{ + int y; + + src += fmt->fmt.pix.height * fmt->fmt.pix.bytesperline; + for (y = 0; y < fmt->fmt.pix.height; y++) { + src -= fmt->fmt.pix.bytesperline; + memcpy(dest, src, fmt->fmt.pix.width * 3); + dest += fmt->fmt.pix.width * 3; + } +} + +static void v4lconvert_vflip_yuv420(unsigned char *src, unsigned char *dest, + struct v4l2_format *fmt) +{ + int y; + + /* First flip the Y plane */ + src += fmt->fmt.pix.height * fmt->fmt.pix.bytesperline; + for (y = 0; y < fmt->fmt.pix.height; y++) { + src -= fmt->fmt.pix.bytesperline; + memcpy(dest, src, fmt->fmt.pix.width); + dest += fmt->fmt.pix.width; + } + + /* Now flip the U plane */ + src += fmt->fmt.pix.height * fmt->fmt.pix.bytesperline * 5 / 4; + for (y = 0; y < fmt->fmt.pix.height / 2; y++) { + src -= fmt->fmt.pix.bytesperline / 2; + memcpy(dest, src, fmt->fmt.pix.width / 2); + dest += fmt->fmt.pix.width / 2; + } + + /* Last flip the V plane */ + src += fmt->fmt.pix.height * fmt->fmt.pix.bytesperline / 2; + for (y = 0; y < fmt->fmt.pix.height / 2; y++) { + src -= fmt->fmt.pix.bytesperline / 2; + memcpy(dest, src, fmt->fmt.pix.width / 2); + dest += fmt->fmt.pix.width / 2; + } +} + +static void v4lconvert_hflip_rgbbgr24(unsigned char *src, unsigned char *dest, + struct v4l2_format *fmt) +{ + int x, y; + + for (y = 0; y < fmt->fmt.pix.height; y++) { + src += fmt->fmt.pix.width * 3; + for (x = 0; x < fmt->fmt.pix.width; x++) { + src -= 3; + dest[0] = src[0]; + dest[1] = src[1]; + dest[2] = src[2]; + dest += 3; + } + src += fmt->fmt.pix.bytesperline; + } +} + +static void v4lconvert_hflip_yuv420(unsigned char *src, unsigned char *dest, + struct v4l2_format *fmt) +{ + int x, y; + + /* First flip the Y plane */ + for (y = 0; y < fmt->fmt.pix.height; y++) { + src += fmt->fmt.pix.width; + for (x = 0; x < fmt->fmt.pix.width; x++) + *dest++ = *--src; + src += fmt->fmt.pix.bytesperline; + } + + /* Now flip the U plane */ + for (y = 0; y < fmt->fmt.pix.height / 2; y++) { + src += fmt->fmt.pix.width / 2; + for (x = 0; x < fmt->fmt.pix.width / 2; x++) + *dest++ = *--src; + src += fmt->fmt.pix.bytesperline / 2; + } + + /* Last flip the V plane */ + for (y = 0; y < fmt->fmt.pix.height / 2; y++) { + src += fmt->fmt.pix.width / 2; + for (x = 0; x < fmt->fmt.pix.width / 2; x++) + *dest++ = *--src; + src += fmt->fmt.pix.bytesperline / 2; + } +} + static void v4lconvert_rotate180_rgbbgr24(const unsigned char *src, unsigned char *dst, int width, int height) { @@ -106,37 +198,69 @@ static void v4lconvert_rotate90_yuv420(const unsigned char *src, } } -void v4lconvert_rotate(unsigned char *src, unsigned char *dest, - int width, int height, unsigned int pix_fmt, int rotate) +void v4lconvert_rotate90(unsigned char *src, unsigned char *dest, + struct v4l2_format *fmt) +{ + int tmp; + + tmp = fmt->fmt.pix.width; + fmt->fmt.pix.width = fmt->fmt.pix.height; + fmt->fmt.pix.height = tmp; + + switch (fmt->fmt.pix.pixelformat) { + case V4L2_PIX_FMT_RGB24: + case V4L2_PIX_FMT_BGR24: + v4lconvert_rotate90_rgbbgr24(src, dest, fmt->fmt.pix.width, + fmt->fmt.pix.height); + break; + case V4L2_PIX_FMT_YUV420: + case V4L2_PIX_FMT_YVU420: + v4lconvert_rotate90_yuv420(src, dest, fmt->fmt.pix.width, + fmt->fmt.pix.height); + break; + } +} + +void v4lconvert_flip(unsigned char *src, unsigned char *dest, + struct v4l2_format *fmt, int hflip, int vflip) { - switch (rotate) { - case 0: - break; - case 90: - switch (pix_fmt) { + if (vflip && hflip) { + switch (fmt->fmt.pix.pixelformat) { + case V4L2_PIX_FMT_RGB24: + case V4L2_PIX_FMT_BGR24: + v4lconvert_rotate180_rgbbgr24(src, dest, fmt->fmt.pix.width, + fmt->fmt.pix.height); + break; + case V4L2_PIX_FMT_YUV420: + case V4L2_PIX_FMT_YVU420: + v4lconvert_rotate180_yuv420(src, dest, fmt->fmt.pix.width, + fmt->fmt.pix.height); + break; + } + } else if (hflip) { + switch (fmt->fmt.pix.pixelformat) { case V4L2_PIX_FMT_RGB24: case V4L2_PIX_FMT_BGR24: - v4lconvert_rotate90_rgbbgr24(src, dest, width, height); + v4lconvert_hflip_rgbbgr24(src, dest, fmt); break; case V4L2_PIX_FMT_YUV420: case V4L2_PIX_FMT_YVU420: - v4lconvert_rotate90_yuv420(src, dest, width, height); + v4lconvert_hflip_yuv420(src, dest, fmt); break; } - break; - case 180: - switch (pix_fmt) { + } else if (vflip) { + switch (fmt->fmt.pix.pixelformat) { case V4L2_PIX_FMT_RGB24: case V4L2_PIX_FMT_BGR24: - v4lconvert_rotate180_rgbbgr24(src, dest, width, height); + v4lconvert_vflip_rgbbgr24(src, dest, fmt); break; case V4L2_PIX_FMT_YUV420: case V4L2_PIX_FMT_YVU420: - v4lconvert_rotate180_yuv420(src, dest, width, height); + v4lconvert_vflip_yuv420(src, dest, fmt); break; } - break; - default: - printf("FIXME add %d degrees rotation\n", rotate); } + + /* Our newly written data has no padding */ + v4lconvert_fixup_fmt(fmt); } diff --git a/v4l2-apps/libv4l/libv4lconvert/helper-funcs.h b/v4l2-apps/libv4l/libv4lconvert/helper-funcs.h new file mode 100644 index 000000000..8ce12343c --- /dev/null +++ b/v4l2-apps/libv4l/libv4lconvert/helper-funcs.h @@ -0,0 +1,79 @@ +/* Utility functions for decompression helpers + * + * Copyright (c) 2009 Hans de Goede <hdegoede@redhat.com> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <errno.h> +#include <string.h> + +static int v4lconvert_helper_write(int fd, const void *b, size_t count, + char *progname) +{ + const unsigned char *buf = b; + size_t ret, written = 0; + + while (written < count) { + ret = write(fd, buf + written, count - written); + if (ret == -1) { + if (errno == EINTR) + continue; + + if (errno == EPIPE) /* Main program has quited */ + exit(0); + + fprintf(stderr, "%s: error writing: %s\n", progname, strerror(errno)); + return -1; + } + written += ret; + } + + return 0; +} + +static int v4lconvert_helper_read(int fd, void *b, size_t count, + char *progname) +{ + unsigned char *buf = b; + size_t ret, r = 0; + + while (r < count) { + ret = read(fd, buf + r, count - r); + if (ret == -1) { + if (errno == EINTR) + continue; + + fprintf(stderr, "%s: error reading: %s\n", progname, strerror(errno)); + return -1; + } + if (ret == 0) /* EOF */ + exit(0); + + r += ret; + } + + return 0; +} diff --git a/v4l2-apps/libv4l/libv4lconvert/helper.c b/v4l2-apps/libv4l/libv4lconvert/helper.c new file mode 100644 index 000000000..c1d55c2e5 --- /dev/null +++ b/v4l2-apps/libv4l/libv4lconvert/helper.c @@ -0,0 +1,224 @@ +/* +# (C) 2009 Hans de Goede <hdegoede@redhat.com> + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <errno.h> +#include <string.h> +#include <signal.h> +#include <sys/wait.h> +#include "libv4lconvert-priv.h" + +#define READ_END 0 +#define WRITE_END 1 + +/* <sigh> Unfortunately I've failed in contact some Authors of decompression + code of out of tree drivers. So I've no permission to relicense their code + their code from GPL to LGPL. To work around this, these decompression + algorithms are put in separate executables and we pipe data through these + to decompress. + + The "protocol" is very simple: + + From libv4l to the helper the following is send: + int width + int height + int flags + int data length + unsigned char[] data (data length long) + + From the helper to libv4l the following is send: + int data length (-1 in case of a decompression error) + unsigned char[] data (not present when a decompression error happened) +*/ + +static int v4lconvert_helper_start(struct v4lconvert_data *data, + const char *helper) +{ + if (pipe(data->decompress_in_pipe)) { + V4LCONVERT_ERR("with helper pipe: %s\n", strerror(errno)); + goto error; + } + + if (pipe(data->decompress_out_pipe)) { + V4LCONVERT_ERR("with helper pipe: %s\n", strerror(errno)); + goto error_close_in_pipe; + } + + data->decompress_pid = fork(); + if (data->decompress_pid == -1) { + V4LCONVERT_ERR("with helper fork: %s\n", strerror(errno)); + goto error_close_out_pipe; + } + + if (data->decompress_pid == 0) { + /* We are the child */ + + /* Closed unused read / write end of the pipes */ + close(data->decompress_out_pipe[WRITE_END]); + close(data->decompress_in_pipe[READ_END]); + + /* Connect stdin / out to the pipes */ + if (dup2(data->decompress_out_pipe[READ_END], STDIN_FILENO) == -1) { + perror("libv4lconvert: error with helper dup2"); + exit(1); + } + if (dup2(data->decompress_in_pipe[WRITE_END], STDOUT_FILENO) == -1) { + perror("libv4lconvert: error with helper dup2"); + exit(1); + } + + /* And execute the helper */ + execl(helper, helper, NULL); + + /* We should never get here */ + perror("libv4lconvert: error starting helper"); + exit(1); + } else { + /* Closed unused read / write end of the pipes */ + close(data->decompress_out_pipe[READ_END]); + close(data->decompress_in_pipe[WRITE_END]); + } + + return 0; + +error_close_out_pipe: + close(data->decompress_out_pipe[READ_END]); + close(data->decompress_out_pipe[WRITE_END]); +error_close_in_pipe: + close(data->decompress_in_pipe[READ_END]); + close(data->decompress_in_pipe[WRITE_END]); +error: + return -1; +} + +/* IMPROVE ME: we could block SIGPIPE here using pthread_sigmask() + and then in case of EPIPE consume the signal using + sigtimedwait (we need to check if a blocked signal wasn't present + before the write, otherwise we will consume that) and + after consuming the signal try to restart the helper. + + Note we currently do not do this, as SIGPIPE only happens if the + decompressor crashes, which in case of an embedded decompressor + would mean end of program, so by not handling SIGPIPE we treat + external decompressors identical. */ +static int v4lconvert_helper_write(struct v4lconvert_data *data, + const void *b, size_t count) +{ + const unsigned char *buf = b; + const int *i = b; + size_t ret, written = 0; + + while (written < count) { + ret = write(data->decompress_out_pipe[WRITE_END], buf + written, + count - written); + if (ret == -1) { + if (errno == EINTR) + continue; + + V4LCONVERT_ERR("writing to helper: %s\n", strerror(errno)); + return -1; + } + written += ret; + } + + return 0; +} + +static int v4lconvert_helper_read(struct v4lconvert_data *data, void *b, + size_t count) +{ + unsigned char *buf = b; + size_t ret, r = 0; + + while (r < count) { + ret = read(data->decompress_in_pipe[READ_END], buf + r, count - r); + if (ret == -1) { + if (errno == EINTR) + continue; + + V4LCONVERT_ERR("reading from helper: %s\n", strerror(errno)); + return -1; + } + if (ret == 0) { + V4LCONVERT_ERR("reading from helper: unexpected EOF\n"); + return -1; + } + r += ret; + } + + return 0; +} + +int v4lconvert_helper_decompress(struct v4lconvert_data *data, + const char *helper, const unsigned char *src, int src_size, + unsigned char *dest, int dest_size, int width, int height, int flags) +{ + int r; + + if (data->decompress_pid == -1) { + if (v4lconvert_helper_start(data, helper)) + return -1; + } + + if (v4lconvert_helper_write(data, &width, sizeof(int))) + return -1; + + if (v4lconvert_helper_write(data, &height, sizeof(int))) + return -1; + + if (v4lconvert_helper_write(data, &flags, sizeof(int))) + return -1; + + if (v4lconvert_helper_write(data, &src_size, sizeof(int))) + return -1; + + if (v4lconvert_helper_write(data, src, src_size)) + return -1; + + if (v4lconvert_helper_read(data, &r, sizeof(int))) + return -1; + + if (r < 0) { + V4LCONVERT_ERR("decompressing frame data\n"); + return -1; + } + + if (dest_size < r) { + V4LCONVERT_ERR("destination buffer to small\n"); + return -1; + } + + return v4lconvert_helper_read(data, dest, r); +} + +void v4lconvert_helper_cleanup(struct v4lconvert_data *data) +{ + int status; + + if (data->decompress_pid != -1) { + kill(data->decompress_pid, SIGTERM); + waitpid(data->decompress_pid, &status, 0); + + close(data->decompress_out_pipe[WRITE_END]); + close(data->decompress_in_pipe[READ_END]); + + data->decompress_pid = -1; + } +} diff --git a/v4l2-apps/libv4l/libv4lconvert/libv4lconvert-priv.h b/v4l2-apps/libv4l/libv4lconvert/libv4lconvert-priv.h index 5ce7bde3b..2cf8f6ba4 100644 --- a/v4l2-apps/libv4l/libv4lconvert/libv4lconvert-priv.h +++ b/v4l2-apps/libv4l/libv4lconvert/libv4lconvert-priv.h @@ -20,9 +20,14 @@ #define __LIBV4LCONVERT_PRIV_H #include <stdio.h> +#include <sys/types.h> #include "libv4lconvert.h" +#include "control/libv4lcontrol.h" +#include "processing/libv4lprocessing.h" #include "tinyjpeg.h" +/* Workaround these potentially missing from videodev2.h */ + #ifndef V4L2_PIX_FMT_SPCA501 #define V4L2_PIX_FMT_SPCA501 v4l2_fourcc('S','5','0','1') /* YUYV per line */ #endif @@ -79,6 +84,16 @@ #define V4L2_PIX_FMT_SN9C20X_I420 v4l2_fourcc('S', '9', '2', '0') #endif +#ifndef V4L2_PIX_FMT_OV511 +#define V4L2_PIX_FMT_OV511 v4l2_fourcc('O', '5', '1', '1') +#endif + +#ifndef V4L2_PIX_FMT_OV518 +#define V4L2_PIX_FMT_OV518 v4l2_fourcc('O', '5', '1', '8') /* ov518 JPEG */ +#endif + +#define ARRAY_SIZE(x) ((int)sizeof(x)/(int)sizeof((x)[0])) + #define V4LCONVERT_ERROR_MSG_SIZE 256 #define V4LCONVERT_MAX_FRAMESIZES 16 @@ -87,37 +102,40 @@ "v4l-convert: error " __VA_ARGS__) /* Card flags */ -#define V4LCONVERT_ROTATE_90 0x01 -#define V4LCONVERT_ROTATE_180 0x02 -#define V4LCONVERT_IS_UVC 0x04 +#define V4LCONVERT_IS_UVC 0x01 +#define V4LCONVERT_IS_SN9C20X 0x02 /* Pixformat flags */ -#define V4LCONVERT_COMPRESSED 0x01 +#define V4LCONVERT_COMPRESSED 0x01 /* Compressed format */ +#define V4LCONVERT_NEEDS_CONVERSION 0x02 /* Apps likely wont know this */ struct v4lconvert_data { int fd; int flags; /* bitfield */ + int control_flags; /* bitfield */ int supported_src_formats; /* bitfield */ unsigned int no_formats; char error_msg[V4LCONVERT_ERROR_MSG_SIZE]; struct jdec_private *jdec; struct v4l2_frmsizeenum framesizes[V4LCONVERT_MAX_FRAMESIZES]; unsigned int no_framesizes; - int convert_buf_size; - int rotate_buf_size; + int convert1_buf_size; + int convert2_buf_size; + int rotate90_buf_size; + int flip_buf_size; int convert_pixfmt_buf_size; - unsigned char *convert_buf; - unsigned char *rotate_buf; + unsigned char *convert1_buf; + unsigned char *convert2_buf; + unsigned char *rotate90_buf; + unsigned char *flip_buf; unsigned char *convert_pixfmt_buf; -}; + struct v4lcontrol_data *control; + struct v4lprocessing_data *processing; -struct v4lconvert_flags_info { - unsigned short vendor_id; - unsigned short product_id; -/* We could also use the USB manufacturer and product strings some devices have - const char *manufacturer; - const char *product; */ - int flags; + /* Data for external decompression helpers code */ + pid_t decompress_pid; + int decompress_in_pipe[2]; /* Data from helper to us */ + int decompress_out_pipe[2]; /* Data from us to helper */ }; struct v4lconvert_pixfmt { @@ -125,6 +143,8 @@ struct v4lconvert_pixfmt { int flags; }; +void v4lconvert_fixup_fmt(struct v4l2_format *fmt); + void v4lconvert_rgb24_to_yuv420(const unsigned char *src, unsigned char *dest, const struct v4l2_format *src_fmt, int bgr, int yvu); @@ -209,10 +229,19 @@ void v4lconvert_hm12_to_bgr24(const unsigned char *src, void v4lconvert_hm12_to_yuv420(const unsigned char *src, unsigned char *dst, int width, int height, int yvu); -void v4lconvert_rotate(unsigned char *src, unsigned char *dest, - int width, int height, unsigned int pix_fmt, int rotate); +void v4lconvert_rotate90(unsigned char *src, unsigned char *dest, + struct v4l2_format *fmt); + +void v4lconvert_flip(unsigned char *src, unsigned char *dest, + struct v4l2_format *fmt, int hflip, int vflip); void v4lconvert_crop(unsigned char *src, unsigned char *dest, const struct v4l2_format *src_fmt, const struct v4l2_format *dest_fmt); +int v4lconvert_helper_decompress(struct v4lconvert_data *data, + const char *helper, const unsigned char *src, int src_size, + unsigned char *dest, int dest_size, int width, int height, int command); + +void v4lconvert_helper_cleanup(struct v4lconvert_data *data); + #endif diff --git a/v4l2-apps/libv4l/libv4lconvert/libv4lconvert.c b/v4l2-apps/libv4l/libv4lconvert/libv4lconvert.c index 1204e8ef2..1389c58bc 100644 --- a/v4l2-apps/libv4l/libv4lconvert/libv4lconvert.c +++ b/v4l2-apps/libv4l/libv4lconvert/libv4lconvert.c @@ -19,15 +19,14 @@ #include <errno.h> #include <string.h> #include <stdlib.h> -#include <syscall.h> #include <unistd.h> #include <sys/stat.h> #include <sys/types.h> #include "libv4lconvert.h" #include "libv4lconvert-priv.h" +#include "libv4lsyscall-priv.h" #define MIN(a,b) (((a)<(b))?(a):(b)) -#define ARRAY_SIZE(x) ((int)sizeof(x)/(int)sizeof((x)[0])) /* Note for proper functioning of v4lconvert_enum_fmt the first entries in supported_src_pixfmts must match with the entries in supported_dst_pixfmts */ @@ -47,15 +46,15 @@ static const struct v4lconvert_pixfmt supported_src_pixfmts[] = { { V4L2_PIX_FMT_YUYV, 0 }, { V4L2_PIX_FMT_YVYU, 0 }, { V4L2_PIX_FMT_UYVY, 0 }, - { V4L2_PIX_FMT_SN9C20X_I420, 0 }, - { V4L2_PIX_FMT_SBGGR8, 0 }, - { V4L2_PIX_FMT_SGBRG8, 0 }, - { V4L2_PIX_FMT_SGRBG8, 0 }, - { V4L2_PIX_FMT_SRGGB8, 0 }, - { V4L2_PIX_FMT_SPCA501, 0 }, - { V4L2_PIX_FMT_SPCA505, 0 }, - { V4L2_PIX_FMT_SPCA508, 0 }, - { V4L2_PIX_FMT_HM12, 0 }, + { V4L2_PIX_FMT_SN9C20X_I420, V4LCONVERT_NEEDS_CONVERSION }, + { V4L2_PIX_FMT_SBGGR8, V4LCONVERT_NEEDS_CONVERSION }, + { V4L2_PIX_FMT_SGBRG8, V4LCONVERT_NEEDS_CONVERSION }, + { V4L2_PIX_FMT_SGRBG8, V4LCONVERT_NEEDS_CONVERSION }, + { V4L2_PIX_FMT_SRGGB8, V4LCONVERT_NEEDS_CONVERSION }, + { V4L2_PIX_FMT_SPCA501, V4LCONVERT_NEEDS_CONVERSION }, + { V4L2_PIX_FMT_SPCA505, V4LCONVERT_NEEDS_CONVERSION }, + { V4L2_PIX_FMT_SPCA508, V4LCONVERT_NEEDS_CONVERSION }, + { V4L2_PIX_FMT_HM12, V4LCONVERT_NEEDS_CONVERSION }, { V4L2_PIX_FMT_MJPEG, V4LCONVERT_COMPRESSED }, { V4L2_PIX_FMT_JPEG, V4LCONVERT_COMPRESSED }, { V4L2_PIX_FMT_SPCA561, V4LCONVERT_COMPRESSED }, @@ -64,20 +63,14 @@ static const struct v4lconvert_pixfmt supported_src_pixfmts[] = { { V4L2_PIX_FMT_MR97310A, V4LCONVERT_COMPRESSED }, { V4L2_PIX_FMT_SQ905C, V4LCONVERT_COMPRESSED }, { V4L2_PIX_FMT_PJPG, V4LCONVERT_COMPRESSED }, + { V4L2_PIX_FMT_OV511, V4LCONVERT_COMPRESSED }, + { V4L2_PIX_FMT_OV518, V4LCONVERT_COMPRESSED }, }; static const struct v4lconvert_pixfmt supported_dst_pixfmts[] = { SUPPORTED_DST_PIXFMTS }; -/* List of cams which need special flags */ -static const struct v4lconvert_flags_info v4lconvert_flags[] = { - { 0x0471, 0x0325, V4LCONVERT_ROTATE_180 }, /* Philips SPC200NC */ - { 0x0471, 0x0326, V4LCONVERT_ROTATE_180 }, /* Philips SPC300NC */ - { 0x0471, 0x032d, V4LCONVERT_ROTATE_180 }, /* Philips SPC210NC */ - { 0x093a, 0x2476, V4LCONVERT_ROTATE_180 }, /* Genius E-M 112 */ -}; - /* List of well known resolutions which we can get by cropping somewhat larger resolutions */ static const int v4lconvert_crop_res[][2] = { @@ -91,56 +84,21 @@ static const int v4lconvert_crop_res[][2] = { { 176, 144 }, }; -static int v4lconvert_get_flags(int fd) -{ - struct stat st; - FILE *f; - char sysfs_name[512]; - unsigned short vendor_id = 0; - unsigned short product_id = 0; - int i; - - if (fstat(fd, &st) || !S_ISCHR(st.st_mode)) - return 0; /* Should never happen */ - - /* Get product ID */ - snprintf(sysfs_name, sizeof(sysfs_name), - "/sys/class/video4linux/video%d/device/idVendor", - minor(st.st_rdev)); - f = fopen(sysfs_name, "r"); - if (!f) - return 0; /* Not an USB device (or no sysfs) */ - i = fscanf(f, "%hx", &vendor_id); - fclose(f); - - /* Get product ID */ - snprintf(sysfs_name, sizeof(sysfs_name), - "/sys/class/video4linux/video%d/device/idProduct", - minor(st.st_rdev)); - f = fopen(sysfs_name, "r"); - if (!f) - return 0; /* Should never happen */ - i = fscanf(f, "%hx", &product_id); - fclose(f); - - for (i = 0; i < ARRAY_SIZE(v4lconvert_flags); i++) - if (v4lconvert_flags[i].vendor_id == vendor_id && - v4lconvert_flags[i].product_id == product_id) - return v4lconvert_flags[i].flags; - - return 0; -} - struct v4lconvert_data *v4lconvert_create(int fd) { int i, j; struct v4lconvert_data *data = calloc(1, sizeof(struct v4lconvert_data)); struct v4l2_capability cap; + /* This keeps tracks of devices which have only formats for which apps + most likely will need conversion and we can thus safely add software + processing controls without a performance impact. */ + int always_needs_conversion = 1; if (!data) return NULL; data->fd = fd; + data->decompress_pid = -1; /* Check supported formats */ for (i = 0; ; i++) { @@ -148,24 +106,47 @@ struct v4lconvert_data *v4lconvert_create(int fd) fmt.index = i; - if (syscall(SYS_ioctl, fd, VIDIOC_ENUM_FMT, &fmt)) + if (SYS_IOCTL(data->fd, VIDIOC_ENUM_FMT, &fmt)) break; for (j = 0; j < ARRAY_SIZE(supported_src_pixfmts); j++) if (fmt.pixelformat == supported_src_pixfmts[j].fmt) { data->supported_src_formats |= 1 << j; v4lconvert_get_framesizes(data, fmt.pixelformat, j); + if (!supported_src_pixfmts[j].flags) + always_needs_conversion = 0; break; } + + if (j == ARRAY_SIZE(supported_src_pixfmts)) + always_needs_conversion = 0; } data->no_formats = i; /* Check if this cam has any special flags */ - data->flags = v4lconvert_get_flags(data->fd); - if (syscall(SYS_ioctl, fd, VIDIOC_QUERYCAP, &cap) == 0) { + if (SYS_IOCTL(data->fd, VIDIOC_QUERYCAP, &cap) == 0) { if (!strcmp((char *)cap.driver, "uvcvideo")) data->flags |= V4LCONVERT_IS_UVC; + else if (!strcmp((char *)cap.driver, "sn9c20x")) + data->flags |= V4LCONVERT_IS_SN9C20X; + + if ((cap.capabilities & 0xff) & ~V4L2_CAP_VIDEO_CAPTURE) + always_needs_conversion = 0; + } + + data->control = v4lcontrol_create(fd, always_needs_conversion); + if (!data->control) { + free(data); + return NULL; + } + data->control_flags = v4lcontrol_get_flags(data->control); + + data->processing = v4lprocessing_create(fd, data->control); + if (!data->processing) { + v4lcontrol_destroy(data->control); + free(data); + return NULL; } return data; @@ -173,18 +154,23 @@ struct v4lconvert_data *v4lconvert_create(int fd) void v4lconvert_destroy(struct v4lconvert_data *data) { + v4lprocessing_destroy(data->processing); + v4lcontrol_destroy(data->control); if (data->jdec) { unsigned char *comps[3] = { NULL, NULL, NULL }; tinyjpeg_set_components(data->jdec, comps, 3); tinyjpeg_free(data->jdec); } - free(data->convert_buf); - free(data->rotate_buf); + v4lconvert_helper_cleanup(data); + free(data->convert1_buf); + free(data->convert2_buf); + free(data->rotate90_buf); + free(data->flip_buf); free(data->convert_pixfmt_buf); free(data); } -static int v4lconvert_supported_dst_format(unsigned int pixelformat) +int v4lconvert_supported_dst_format(unsigned int pixelformat) { int i; @@ -195,6 +181,12 @@ static int v4lconvert_supported_dst_format(unsigned int pixelformat) return i != ARRAY_SIZE(supported_dst_pixfmts); } +int v4lconvert_supported_dst_fmt_only(struct v4lconvert_data *data) +{ + return v4lcontrol_needs_conversion(data->control) && + data->supported_src_formats; +} + /* See libv4lconvert.h for description of in / out parameters */ int v4lconvert_enum_fmt(struct v4lconvert_data *data, struct v4l2_fmtdesc *fmt) { @@ -202,17 +194,22 @@ int v4lconvert_enum_fmt(struct v4lconvert_data *data, struct v4l2_fmtdesc *fmt) unsigned int faked_fmts[ARRAY_SIZE(supported_dst_pixfmts)]; if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE || - fmt->index < data->no_formats || - !data->supported_src_formats) - return syscall(SYS_ioctl, data->fd, VIDIOC_ENUM_FMT, fmt); + (!v4lconvert_supported_dst_fmt_only(data) && + fmt->index < data->no_formats)) + return SYS_IOCTL(data->fd, VIDIOC_ENUM_FMT, fmt); for (i = 0; i < ARRAY_SIZE(supported_dst_pixfmts); i++) - if (!(data->supported_src_formats & (1 << i))) { + if (v4lconvert_supported_dst_fmt_only(data) || + !(data->supported_src_formats & (1 << i))) { faked_fmts[no_faked_fmts] = supported_dst_pixfmts[i].fmt; no_faked_fmts++; } - i = fmt->index - data->no_formats; + if (!v4lconvert_supported_dst_fmt_only(data)) + i = fmt->index - data->no_formats; + else + i = fmt->index; + if (i >= no_faked_fmts) { errno = EINVAL; return -1; @@ -307,7 +304,7 @@ static int v4lconvert_do_try_format(struct v4lconvert_data *data, try_fmt = *dest_fmt; try_fmt.fmt.pix.pixelformat = supported_src_pixfmts[i].fmt; - if (!syscall(SYS_ioctl, data->fd, VIDIOC_TRY_FMT, &try_fmt)) + if (!SYS_IOCTL(data->fd, VIDIOC_TRY_FMT, &try_fmt)) { if (try_fmt.fmt.pix.pixelformat == supported_src_pixfmts[i].fmt) { int size_x_diff = abs((int)try_fmt.fmt.pix.width - @@ -339,7 +336,7 @@ static int v4lconvert_do_try_format(struct v4lconvert_data *data, return 0; } -static void v4lconvert_fixup_fmt(struct v4l2_format *fmt) +void v4lconvert_fixup_fmt(struct v4l2_format *fmt) { switch (fmt->fmt.pix.pixelformat) { case V4L2_PIX_FMT_RGB24: @@ -362,27 +359,58 @@ int v4lconvert_try_format(struct v4lconvert_data *data, int i, result; unsigned int desired_width = dest_fmt->fmt.pix.width; unsigned int desired_height = dest_fmt->fmt.pix.height; - struct v4l2_format try_src, try_dest = *dest_fmt; + struct v4l2_format try_src, try_dest, try2_src, try2_dest; + + if (dest_fmt->type == V4L2_BUF_TYPE_VIDEO_CAPTURE && + v4lconvert_supported_dst_fmt_only(data) && + !v4lconvert_supported_dst_format(dest_fmt->fmt.pix.pixelformat)) + dest_fmt->fmt.pix.pixelformat = V4L2_PIX_FMT_RGB24; + + try_dest = *dest_fmt; /* Can we do conversion to the requested format & type? */ if (!v4lconvert_supported_dst_format(dest_fmt->fmt.pix.pixelformat) || dest_fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE || v4lconvert_do_try_format(data, &try_dest, &try_src)) { - result = syscall(SYS_ioctl, data->fd, VIDIOC_TRY_FMT, dest_fmt); + result = SYS_IOCTL(data->fd, VIDIOC_TRY_FMT, dest_fmt); if (src_fmt) *src_fmt = *dest_fmt; return result; } + /* In case of a non exact resolution match, try again with a slightly larger + resolution as some weird devices are not able to crop of the number of + extra (border) pixels most sensors have compared to standard resolutions, + which we will then just crop of in software */ + if (try_dest.fmt.pix.width != desired_width || + try_dest.fmt.pix.height != desired_height) { + try2_dest = *dest_fmt; + try2_dest.fmt.pix.width = desired_width + 7; + try2_dest.fmt.pix.height = desired_height + 1; + result = v4lconvert_do_try_format(data, &try2_dest, &try2_src); + if (result == 0 && + try2_dest.fmt.pix.width >= desired_width && + try2_dest.fmt.pix.width <= desired_width + 7 && + try2_dest.fmt.pix.height >= desired_height && + try2_dest.fmt.pix.height <= desired_height + 1) { + /* Success! */ + try2_dest.fmt.pix.width = desired_width; + try2_dest.fmt.pix.height = desired_height; + try_dest = try2_dest; + try_src = try2_src; + } + } + /* In case of a non exact resolution match, see if this is a well known resolution some apps are hardcoded too and try to give the app what it - asked for by cropping a slightly larger resolution */ + asked for by cropping a slightly larger resolution or adding a small + black border to a slightly smaller resolution */ if (try_dest.fmt.pix.width != desired_width || try_dest.fmt.pix.height != desired_height) { for (i = 0; i < ARRAY_SIZE(v4lconvert_crop_res); i++) { if (v4lconvert_crop_res[i][0] == desired_width && v4lconvert_crop_res[i][1] == desired_height) { - struct v4l2_format try2_src, try2_dest = *dest_fmt; + try2_dest = *dest_fmt; /* Note these are chosen so that cropping to vga res just works for vv6410 sensor cams, which have 356x292 and 180x148 */ @@ -390,13 +418,20 @@ int v4lconvert_try_format(struct v4lconvert_data *data, try2_dest.fmt.pix.height = desired_height * 124 / 100; result = v4lconvert_do_try_format(data, &try2_dest, &try2_src); if (result == 0 && - ((try2_dest.fmt.pix.width >= desired_width && + (/* Add a small black border of max 16 pixels */ + (try2_dest.fmt.pix.width >= desired_width - 16 && + try2_dest.fmt.pix.width <= desired_width && + try2_dest.fmt.pix.height >= desired_height - 16 && + try2_dest.fmt.pix.height <= desired_height) || + /* Standard cropping to max 80% of actual width / height */ + (try2_dest.fmt.pix.width >= desired_width && try2_dest.fmt.pix.width <= desired_width * 5 / 4 && try2_dest.fmt.pix.height >= desired_height && try2_dest.fmt.pix.height <= desired_height * 5 / 4) || + /* Downscale 2x + cropping to max 80% of actual width / height */ (try2_dest.fmt.pix.width >= desired_width * 2 && try2_dest.fmt.pix.width <= desired_width * 5 / 2 && - try2_dest.fmt.pix.height >= desired_height && + try2_dest.fmt.pix.height >= desired_height * 2 && try2_dest.fmt.pix.height <= desired_height * 5 / 2))) { /* Success! */ try2_dest.fmt.pix.width = desired_width; @@ -409,7 +444,15 @@ int v4lconvert_try_format(struct v4lconvert_data *data, } } - /* Are we converting? */ + /* Some applications / libs (*cough* gstreamer *cough*) will not work + correctly with planar YUV formats when the width is not a multiple of 8 + or the height is not a multiple of 2. With RGB formats these apps require + the width to be a multiple of 4. We apply the same rounding to all + formats to not end up with 2 close but different resolutions. */ + try_dest.fmt.pix.width &= ~7; + try_dest.fmt.pix.height &= ~1; + + /* Are we converting / cropping ? */ if(try_src.fmt.pix.width != try_dest.fmt.pix.width || try_src.fmt.pix.height != try_dest.fmt.pix.height || try_src.fmt.pix.pixelformat != try_dest.fmt.pix.pixelformat) @@ -427,19 +470,40 @@ int v4lconvert_needs_conversion(struct v4lconvert_data *data, const struct v4l2_format *src_fmt, /* in */ const struct v4l2_format *dest_fmt) /* in */ { - if(src_fmt->fmt.pix.width != dest_fmt->fmt.pix.width || - src_fmt->fmt.pix.height != dest_fmt->fmt.pix.height || - src_fmt->fmt.pix.pixelformat != dest_fmt->fmt.pix.pixelformat) - return 1; /* Formats differ */ + if (src_fmt->fmt.pix.width != dest_fmt->fmt.pix.width || + src_fmt->fmt.pix.height != dest_fmt->fmt.pix.height || + src_fmt->fmt.pix.pixelformat != dest_fmt->fmt.pix.pixelformat || + (v4lcontrol_needs_conversion(data->control) && + v4lconvert_supported_dst_format(dest_fmt->fmt.pix.pixelformat))) + return 1; - if (!(data->flags & (V4LCONVERT_ROTATE_90|V4LCONVERT_ROTATE_180))) - return 0; /* Formats identical and we don't need flip */ + return 0; +} - /* Formats are identical, but we need flip, do we support the dest_fmt? */ - if (!v4lconvert_supported_dst_format(dest_fmt->fmt.pix.pixelformat)) - return 0; /* Needs flip but we cannot do it :( */ - else - return 1; /* Needs flip and thus conversion */ +static int v4lconvert_processing_needs_double_conversion( + unsigned int src_pix_fmt, unsigned int dest_pix_fmt) +{ + switch (src_pix_fmt) { + case V4L2_PIX_FMT_RGB24: + case V4L2_PIX_FMT_BGR24: + case V4L2_PIX_FMT_SPCA561: + case V4L2_PIX_FMT_SN9C10X: + case V4L2_PIX_FMT_PAC207: + case V4L2_PIX_FMT_MR97310A: + case V4L2_PIX_FMT_SQ905C: + case V4L2_PIX_FMT_SBGGR8: + case V4L2_PIX_FMT_SGBRG8: + case V4L2_PIX_FMT_SGRBG8: + case V4L2_PIX_FMT_SRGGB8: + return 0; + } + switch (dest_pix_fmt) { + case V4L2_PIX_FMT_RGB24: + case V4L2_PIX_FMT_BGR24: + return 0; + } + + return 1; } static unsigned char *v4lconvert_alloc_buffer(struct v4lconvert_data *data, @@ -460,15 +524,15 @@ static unsigned char *v4lconvert_alloc_buffer(struct v4lconvert_data *data, } static int v4lconvert_convert_pixfmt(struct v4lconvert_data *data, - unsigned char *src, int src_size, unsigned char *dest, - const struct v4l2_format *src_fmt, unsigned int dest_pix_fmt) + unsigned char *src, int src_size, unsigned char *dest, int dest_size, + struct v4l2_format *fmt, unsigned int dest_pix_fmt) { unsigned int header_width, header_height; int result = 0, jpeg_flags = TINYJPEG_FLAGS_MJPEG_TABLE; unsigned char *components[3]; - unsigned int src_pix_fmt = src_fmt->fmt.pix.pixelformat; - unsigned int width = src_fmt->fmt.pix.width; - unsigned int height = src_fmt->fmt.pix.height; + unsigned int src_pix_fmt = fmt->fmt.pix.pixelformat; + unsigned int width = fmt->fmt.pix.width; + unsigned int height = fmt->fmt.pix.height; switch (src_pix_fmt) { case V4L2_PIX_FMT_PJPG: @@ -496,12 +560,14 @@ static int v4lconvert_convert_pixfmt(struct v4lconvert_data *data, if (header_width != width || header_height != height) { /* Check for (pixart) rotated JPEG */ if (header_width == height && header_height == width) { - if (!(data->flags & V4LCONVERT_ROTATE_90)) { - V4LCONVERT_ERR("JPEG needs 90 degree rotation\n"); - data->flags |= V4LCONVERT_ROTATE_90; - errno = EAGAIN; + if (!(data->control_flags & V4LCONTROL_ROTATED_90_JPEG)) { + V4LCONVERT_ERR("JPEG needs 90° rotation, please report " + "this to <hdegoede@redhat.com>\n"); + errno = EIO; return -1; } + fmt->fmt.pix.width = header_width; + fmt->fmt.pix.height = header_height; } else { V4LCONVERT_ERR("unexpected width / height in JPEG header" "expected: %ux%u, header: %ux%u\n", width, height, @@ -509,6 +575,13 @@ static int v4lconvert_convert_pixfmt(struct v4lconvert_data *data, errno = EIO; return -1; } + } else if ((data->control_flags & V4LCONTROL_ROTATED_90_JPEG)) { + fprintf(stderr, "libv4lconvert: expected 90° rotated JPEG, but got " + "normal JPEG, please report this to <hdegoede@redhat.com>\n"); + V4LCONVERT_ERR("expected 90° rotated JPEG, but got normal JPEG\n"); + errno = EAGAIN; + data->control_flags &= ~V4LCONTROL_ROTATED_90_JPEG; + return -1; } components[0] = dest; @@ -541,7 +614,8 @@ static int v4lconvert_convert_pixfmt(struct v4lconvert_data *data, are best thrown away to avoid flashes in the video stream. Tell the upper layer this is an intermediate fault and it should try again with a new buffer by setting errno to EAGAIN */ - if (src_pix_fmt == V4L2_PIX_FMT_PJPG) { + if (src_pix_fmt == V4L2_PIX_FMT_PJPG || + data->flags & V4LCONVERT_IS_SN9C20X) { V4LCONVERT_ERR("decompressing JPEG: %s", tinyjpeg_get_errorstring(data->jdec)); errno = EAGAIN; @@ -561,8 +635,11 @@ static int v4lconvert_convert_pixfmt(struct v4lconvert_data *data, case V4L2_PIX_FMT_SPCA505: case V4L2_PIX_FMT_SPCA508: case V4L2_PIX_FMT_SN9C20X_I420: + case V4L2_PIX_FMT_OV511: + case V4L2_PIX_FMT_OV518: { unsigned char *d; + int d_size; int yvu = 0; if (dest_pix_fmt != V4L2_PIX_FMT_YUV420 && @@ -571,8 +648,11 @@ static int v4lconvert_convert_pixfmt(struct v4lconvert_data *data, &data->convert_pixfmt_buf, &data->convert_pixfmt_buf_size); if (!d) return -1; - } else + d_size = width * height * 3 / 2; + } else { d = dest; + d_size = dest_size; + } if (dest_pix_fmt == V4L2_PIX_FMT_YVU420) yvu = 1; @@ -590,6 +670,22 @@ static int v4lconvert_convert_pixfmt(struct v4lconvert_data *data, case V4L2_PIX_FMT_SN9C20X_I420: v4lconvert_sn9c20x_to_yuv420(src, d, width, height, yvu); break; + case V4L2_PIX_FMT_OV511: + if (v4lconvert_helper_decompress(data, LIBDIR "/libv4l/ov511-decomp", + src, src_size, d, d_size, width, height, yvu)) { + /* Corrupt frame, better get another one */ + errno = EAGAIN; + return -1; + } + break; + case V4L2_PIX_FMT_OV518: + if (v4lconvert_helper_decompress(data, LIBDIR "/libv4l/ov518-decomp", + src, src_size, d, d_size, width, height, yvu)) { + /* Corrupt frame, better get another one */ + errno = EAGAIN; + return -1; + } + break; } switch (dest_pix_fmt) { @@ -631,6 +727,7 @@ static int v4lconvert_convert_pixfmt(struct v4lconvert_data *data, case V4L2_PIX_FMT_SQ905C: { unsigned char *tmpbuf; + struct v4l2_format tmpfmt = *fmt; tmpbuf = v4lconvert_alloc_buffer(data, width * height, &data->convert_pixfmt_buf, &data->convert_pixfmt_buf_size); @@ -640,27 +737,33 @@ static int v4lconvert_convert_pixfmt(struct v4lconvert_data *data, switch (src_pix_fmt) { case V4L2_PIX_FMT_SPCA561: v4lconvert_decode_spca561(src, tmpbuf, width, height); - src_pix_fmt = V4L2_PIX_FMT_SGBRG8; + tmpfmt.fmt.pix.pixelformat = V4L2_PIX_FMT_SGBRG8; break; case V4L2_PIX_FMT_SN9C10X: v4lconvert_decode_sn9c10x(src, tmpbuf, width, height); - src_pix_fmt = V4L2_PIX_FMT_SBGGR8; + tmpfmt.fmt.pix.pixelformat = V4L2_PIX_FMT_SBGGR8; break; case V4L2_PIX_FMT_PAC207: v4lconvert_decode_pac207(src, tmpbuf, width, height); - src_pix_fmt = V4L2_PIX_FMT_SBGGR8; + tmpfmt.fmt.pix.pixelformat = V4L2_PIX_FMT_SBGGR8; break; case V4L2_PIX_FMT_MR97310A: v4lconvert_decode_mr97310a(src, tmpbuf, width, height); - src_pix_fmt = V4L2_PIX_FMT_SBGGR8; + tmpfmt.fmt.pix.pixelformat = V4L2_PIX_FMT_SBGGR8; break; case V4L2_PIX_FMT_SQ905C: v4lconvert_decode_sq905c(src, tmpbuf, width, height); - src_pix_fmt = V4L2_PIX_FMT_SRGGB8; + tmpfmt.fmt.pix.pixelformat = V4L2_PIX_FMT_SRGGB8; break; } - src = tmpbuf; + /* Do processing on the tmp buffer, because doing it on bayer data is + cheaper, and bayer == rgb and our dest_fmt may be yuv */ + tmpfmt.fmt.pix.bytesperline = width; + tmpfmt.fmt.pix.sizeimage = width * height; + v4lprocessing_processing(data->processing, tmpbuf, &tmpfmt); /* Deliberate fall through to raw bayer fmt code! */ + src_pix_fmt = tmpfmt.fmt.pix.pixelformat; + src = tmpbuf; } /* Raw bayer formats */ @@ -690,10 +793,10 @@ static int v4lconvert_convert_pixfmt(struct v4lconvert_data *data, v4lconvert_swap_rgb(src, dest, width, height); break; case V4L2_PIX_FMT_YUV420: - v4lconvert_rgb24_to_yuv420(src, dest, src_fmt, 0, 0); + v4lconvert_rgb24_to_yuv420(src, dest, fmt, 0, 0); break; case V4L2_PIX_FMT_YVU420: - v4lconvert_rgb24_to_yuv420(src, dest, src_fmt, 0, 1); + v4lconvert_rgb24_to_yuv420(src, dest, fmt, 0, 1); break; } break; @@ -704,10 +807,10 @@ static int v4lconvert_convert_pixfmt(struct v4lconvert_data *data, v4lconvert_swap_rgb(src, dest, width, height); break; case V4L2_PIX_FMT_YUV420: - v4lconvert_rgb24_to_yuv420(src, dest, src_fmt, 1, 0); + v4lconvert_rgb24_to_yuv420(src, dest, fmt, 1, 0); break; case V4L2_PIX_FMT_YVU420: - v4lconvert_rgb24_to_yuv420(src, dest, src_fmt, 1, 1); + v4lconvert_rgb24_to_yuv420(src, dest, fmt, 1, 1); break; } break; @@ -723,7 +826,7 @@ static int v4lconvert_convert_pixfmt(struct v4lconvert_data *data, height, 0); break; case V4L2_PIX_FMT_YVU420: - v4lconvert_swap_uv(src, dest, src_fmt); + v4lconvert_swap_uv(src, dest, fmt); break; } break; @@ -739,7 +842,7 @@ static int v4lconvert_convert_pixfmt(struct v4lconvert_data *data, height, 1); break; case V4L2_PIX_FMT_YUV420: - v4lconvert_swap_uv(src, dest, src_fmt); + v4lconvert_swap_uv(src, dest, fmt); break; } break; @@ -802,6 +905,10 @@ static int v4lconvert_convert_pixfmt(struct v4lconvert_data *data, errno = EINVAL; return -1; } + + fmt->fmt.pix.pixelformat = dest_pix_fmt; + v4lconvert_fixup_fmt(fmt); + return 0; } @@ -810,14 +917,31 @@ int v4lconvert_convert(struct v4lconvert_data *data, const struct v4l2_format *dest_fmt, /* in */ unsigned char *src, int src_size, unsigned char *dest, int dest_size) { - int res, dest_needed, temp_needed, convert = 0, rotate = 0, crop = 0; - unsigned char *convert_dest = dest, *rotate_src = src, *rotate_dest = dest; + int res, dest_needed, temp_needed, processing, convert = 0; + int rotate90, vflip, hflip, crop; + unsigned char *convert1_dest = dest; + int convert1_dest_size = dest_size; + unsigned char *convert2_src = src, *convert2_dest = dest; + int convert2_dest_size = dest_size; + unsigned char *rotate90_src = src, *rotate90_dest = dest; + unsigned char *flip_src = src, *flip_dest = dest; unsigned char *crop_src = src; struct v4l2_format my_src_fmt = *src_fmt; struct v4l2_format my_dest_fmt = *dest_fmt; - /* Special case when no conversion is needed */ - if (!v4lconvert_needs_conversion(data, src_fmt, dest_fmt)) { + processing = v4lprocessing_pre_processing(data->processing); + rotate90 = data->control_flags & V4LCONTROL_ROTATED_90_JPEG; + hflip = v4lcontrol_get_ctrl(data->control, V4LCONTROL_HFLIP); + vflip = v4lcontrol_get_ctrl(data->control, V4LCONTROL_VFLIP); + crop = my_dest_fmt.fmt.pix.width != my_src_fmt.fmt.pix.width || + my_dest_fmt.fmt.pix.height != my_src_fmt.fmt.pix.height; + + if (/* If no conversion/processing is needed */ + (src_fmt->fmt.pix.pixelformat == dest_fmt->fmt.pix.pixelformat && + !processing && !rotate90 && !hflip && !vflip && !crop) || + /* or if we should do processing/rotating/flipping but the app tries to + use the native cam format, we just return an unprocessed frame copy */ + !v4lconvert_supported_dst_format(dest_fmt->fmt.pix.pixelformat)) { int to_copy = MIN(dest_size, src_size); memcpy(dest, src, to_copy); return to_copy; @@ -856,52 +980,96 @@ int v4lconvert_convert(struct v4lconvert_data *data, return -1; } - if (my_dest_fmt.fmt.pix.pixelformat != my_src_fmt.fmt.pix.pixelformat) + + /* Sometimes we need foo -> rgb -> bar as video processing (whitebalance, + etc.) can only be done on rgb data */ + if (processing && v4lconvert_processing_needs_double_conversion( + my_src_fmt.fmt.pix.pixelformat, + my_dest_fmt.fmt.pix.pixelformat)) + convert = 2; + else if (my_dest_fmt.fmt.pix.pixelformat != my_src_fmt.fmt.pix.pixelformat) convert = 1; - if (data->flags & V4LCONVERT_ROTATE_90) - rotate += 90; - if (data->flags & V4LCONVERT_ROTATE_180) - rotate += 180; + /* convert_pixfmt (only if convert == 2) -> processing -> convert_pixfmt -> + rotate -> flip -> crop, all steps are optional */ + if (convert == 2) { + convert1_dest = v4lconvert_alloc_buffer(data, + my_src_fmt.fmt.pix.width * my_src_fmt.fmt.pix.height * 3, + &data->convert1_buf, &data->convert1_buf_size); + if (!convert1_dest) + return -1; + + convert1_dest_size = + my_src_fmt.fmt.pix.width * my_src_fmt.fmt.pix.height * 3; + convert2_src = convert1_dest; + } + + if (convert && (rotate90 || hflip || vflip || crop)) { + convert2_dest = v4lconvert_alloc_buffer(data, temp_needed, + &data->convert2_buf, &data->convert2_buf_size); + if (!convert2_dest) + return -1; - if (my_dest_fmt.fmt.pix.width != my_src_fmt.fmt.pix.width || - my_dest_fmt.fmt.pix.height != my_src_fmt.fmt.pix.height) - crop = 1; + convert2_dest_size = temp_needed; + rotate90_src = flip_src = crop_src = convert2_dest; + } - /* convert_pixfmt -> rotate -> crop, all steps are optional */ - if (convert && (rotate || crop)) { - convert_dest = v4lconvert_alloc_buffer(data, temp_needed, - &data->convert_buf, &data->convert_buf_size); - if (!convert_dest) + if (rotate90 && (hflip || vflip || crop)) { + rotate90_dest = v4lconvert_alloc_buffer(data, temp_needed, + &data->rotate90_buf, &data->rotate90_buf_size); + if (!rotate90_dest) return -1; - rotate_src = crop_src = convert_dest; + flip_src = crop_src = rotate90_dest; } - if (rotate && crop) { - rotate_dest = v4lconvert_alloc_buffer(data, temp_needed, - &data->rotate_buf, &data->rotate_buf_size); - if (!rotate_dest) + if ((vflip || hflip) && crop) { + flip_dest = v4lconvert_alloc_buffer(data, temp_needed, &data->flip_buf, + &data->flip_buf_size); + if (!flip_dest) return -1; - crop_src = rotate_dest; + crop_src = flip_dest; + } + + /* Done setting sources / dest and allocating intermediate buffers, + real conversion / processing / ... starts here. */ + if (convert == 2) { + res = v4lconvert_convert_pixfmt(data, src, src_size, + convert1_dest, convert1_dest_size, + &my_src_fmt, + V4L2_PIX_FMT_RGB24); + if (res) + return res; + + src_size = my_src_fmt.fmt.pix.sizeimage; } + if (processing) + v4lprocessing_processing(data->processing, convert2_src, &my_src_fmt); + if (convert) { - res = v4lconvert_convert_pixfmt(data, src, src_size, convert_dest, + res = v4lconvert_convert_pixfmt(data, convert2_src, src_size, + convert2_dest, convert2_dest_size, &my_src_fmt, my_dest_fmt.fmt.pix.pixelformat); if (res) return res; - my_src_fmt.fmt.pix.pixelformat = my_dest_fmt.fmt.pix.pixelformat; - v4lconvert_fixup_fmt(&my_src_fmt); + src_size = my_src_fmt.fmt.pix.sizeimage; + + /* We call processing here again in case the source format was not + rgb, but the dest is. v4lprocessing checks it self it only actually + does the processing once per frame. */ + if (processing) + v4lprocessing_processing(data->processing, convert2_dest, &my_src_fmt); } - if (rotate) - v4lconvert_rotate(rotate_src, rotate_dest, - my_src_fmt.fmt.pix.width, my_src_fmt.fmt.pix.height, - my_src_fmt.fmt.pix.pixelformat, rotate); + if (rotate90) + v4lconvert_rotate90(rotate90_src, rotate90_dest, &my_src_fmt); + + if (hflip || vflip) + v4lconvert_flip(flip_src, flip_dest, &my_src_fmt, hflip, vflip); if (crop) v4lconvert_crop(crop_src, dest, &my_src_fmt, &my_dest_fmt); @@ -922,7 +1090,7 @@ static void v4lconvert_get_framesizes(struct v4lconvert_data *data, for (i = 0; ; i++) { frmsize.index = i; - if (syscall(SYS_ioctl, data->fd, VIDIOC_ENUM_FRAMESIZES, &frmsize)) + if (SYS_IOCTL(data->fd, VIDIOC_ENUM_FRAMESIZES, &frmsize)) break; /* We got a framesize, check we don't have the same one already */ @@ -977,8 +1145,13 @@ static void v4lconvert_get_framesizes(struct v4lconvert_data *data, int v4lconvert_enum_framesizes(struct v4lconvert_data *data, struct v4l2_frmsizeenum *frmsize) { - if (!v4lconvert_supported_dst_format(frmsize->pixel_format)) - return syscall(SYS_ioctl, data->fd, VIDIOC_ENUM_FRAMESIZES, frmsize); + if (!v4lconvert_supported_dst_format(frmsize->pixel_format)) { + if (v4lconvert_supported_dst_fmt_only(data)) { + errno = EINVAL; + return -1; + } + return SYS_IOCTL(data->fd, VIDIOC_ENUM_FRAMESIZES, frmsize); + } if (frmsize->index >= data->no_framesizes) { errno = EINVAL; @@ -989,6 +1162,9 @@ int v4lconvert_enum_framesizes(struct v4lconvert_data *data, switch(frmsize->type) { case V4L2_FRMSIZE_TYPE_DISCRETE: frmsize->discrete = data->framesizes[frmsize->index].discrete; + /* Apply the same rounding algorithm as v4lconvert_try_format */ + frmsize->discrete.width &= ~7; + frmsize->discrete.height &= ~1; break; case V4L2_FRMSIZE_TYPE_CONTINUOUS: case V4L2_FRMSIZE_TYPE_STEPWISE: @@ -1006,7 +1182,11 @@ int v4lconvert_enum_frameintervals(struct v4lconvert_data *data, struct v4l2_format src_fmt, dest_fmt; if (!v4lconvert_supported_dst_format(frmival->pixel_format)) { - res = syscall(SYS_ioctl, data->fd, VIDIOC_ENUM_FRAMEINTERVALS, frmival); + if (v4lconvert_supported_dst_fmt_only(data)) { + errno = EINVAL; + return -1; + } + res = SYS_IOCTL(data->fd, VIDIOC_ENUM_FRAMEINTERVALS, frmival); if (res) V4LCONVERT_ERR("%s\n", strerror(errno)); return res; @@ -1050,7 +1230,7 @@ int v4lconvert_enum_frameintervals(struct v4lconvert_data *data, frmival->pixel_format = src_fmt.fmt.pix.pixelformat; frmival->width = src_fmt.fmt.pix.width; frmival->height = src_fmt.fmt.pix.height; - res = syscall(SYS_ioctl, data->fd, VIDIOC_ENUM_FRAMEINTERVALS, frmival); + res = SYS_IOCTL(data->fd, VIDIOC_ENUM_FRAMEINTERVALS, frmival); if (res) { int dest_pixfmt = dest_fmt.fmt.pix.pixelformat; int src_pixfmt = src_fmt.fmt.pix.pixelformat; @@ -1076,3 +1256,15 @@ int v4lconvert_enum_frameintervals(struct v4lconvert_data *data, return res; } + +int v4lconvert_vidioc_queryctrl(struct v4lconvert_data *data, void *arg) { + return v4lcontrol_vidioc_queryctrl(data->control, arg); +} + +int v4lconvert_vidioc_g_ctrl(struct v4lconvert_data *data, void *arg) { + return v4lcontrol_vidioc_g_ctrl(data->control, arg); +} + +int v4lconvert_vidioc_s_ctrl(struct v4lconvert_data *data, void *arg){ + return v4lcontrol_vidioc_s_ctrl(data->control, arg); +} diff --git a/v4l2-apps/libv4l/libv4lconvert/libv4lsyscall-priv.h b/v4l2-apps/libv4l/libv4lconvert/libv4lsyscall-priv.h new file mode 100644 index 000000000..a456ceea3 --- /dev/null +++ b/v4l2-apps/libv4l/libv4lconvert/libv4lsyscall-priv.h @@ -0,0 +1,112 @@ +/*- + * Copyright (c) 2009 Hans Petter Selasky. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * The following file allows for having the complete V4L stack and + * hardware drivers in userland. + */ + +#ifndef _LIBV4LSYSCALL_PRIV_H_ +#define _LIBV4LSYSCALL_PRIV_H_ + +/* Some of these headers are not needed by us, but by linux/videodev2.h, + which is broken on some systems and doesn't include them itself :( */ + +#ifdef linux +#include <sys/time.h> +#include <syscall.h> +#include <linux/types.h> +#include <linux/ioctl.h> +/* On 32 bits archs we always use mmap2, on 64 bits archs there is no mmap2 */ +#ifdef __NR_mmap2 +#define SYS_mmap2 __NR_mmap2 +#define MMAP2_PAGE_SHIFT 12 +#else +#define SYS_mmap2 SYS_mmap +#define MMAP2_PAGE_SHIFT 0 +#endif +#endif + +#ifdef __FreeBSD__ +#include <sys/time.h> +#include <sys/syscall.h> +#include <sys/types.h> +#include <sys/ioctl.h> +#define _IOC_NR(cmd) ((cmd) & 0xFF) +#define _IOC_TYPE(cmd) IOCGROUP(cmd) +#define _IOC_SIZE(cmd) IOCPARM_LEN(cmd) +#define MAP_ANONYMOUS MAP_ANON +#define SYS_mmap2 SYS_mmap +#define MMAP2_PAGE_SHIFT 0 +typedef off_t __off_t; +#endif + +#undef SYS_OPEN +#undef SYS_CLOSE +#undef SYS_IOCTL +#undef SYS_READ +#undef SYS_WRITE +#undef SYS_MMAP +#undef SYS_MUNMAP + +#ifndef CONFIG_SYS_WRAPPER + +#define SYS_OPEN(file, oflag, mode) \ + syscall(SYS_open, (const char *)(file), (int)(oflag), (mode_t)(mode)) +#define SYS_CLOSE(fd) \ + syscall(SYS_close, (int)(fd)) +#define SYS_IOCTL(fd, cmd, arg) \ + syscall(SYS_ioctl, (int)(fd), (unsigned long)(cmd), (void *)(arg)) +#define SYS_READ(fd, buf, len) \ + syscall(SYS_read, (int)(fd), (void *)(buf), (size_t)(len)); +#define SYS_WRITE(fd, buf, len) \ + syscall(SYS_write, (int)(fd), (void *)(buf), (size_t)(len)); +#define SYS_MMAP(addr, len, prot, flags, fd, off) \ + syscall(SYS_mmap2, (void *)(addr), (size_t)(len), \ + (int)(prot), (int)(flags), (int)(fd), (__off_t)((off) >> MMAP2_PAGE_SHIFT)) +#define SYS_MUNMAP(addr, len) \ + syscall(SYS_munmap, (void *)(addr), (size_t)(len)) + +#else + +int v4lx_open_wrapper(const char *, int, int); +int v4lx_close_wrapper(int); +int v4lx_ioctl_wrapper(int, unsigned long, void *); +int v4lx_read_wrapper(int, void *, size_t); +int v4lx_write_wrapper(int, void *, size_t); +void *v4lx_mmap_wrapper(void *, size_t, int, int, int, off_t); +int v4lx_munmap_wrapper(void *, size_t); + +#define SYS_OPEN(...) v4lx_open_wrapper(__VA_ARGS__) +#define SYS_CLOSE(...) v4lx_close_wrapper(__VA_ARGS__) +#define SYS_IOCTL(...) v4lx_ioctl_wrapper(__VA_ARGS__) +#define SYS_READ(...) v4lx_read_wrapper(__VA_ARGS__) +#define SYS_WRITE(...) v4lx_write_wrapper(__VA_ARGS__) +#define SYS_MMAP(...) v4lx_mmap_wrapper(__VA_ARGS__) +#define SYS_MUNMAP(...) v4lx_munmap_wrapper(__VA_ARGS__) + +#endif + +#endif /* _LIBV4LSYSCALL_PRIV_H_ */ diff --git a/v4l2-apps/libv4l/libv4lconvert/ov511-decomp.c b/v4l2-apps/libv4l/libv4lconvert/ov511-decomp.c new file mode 100644 index 000000000..11ed2a601 --- /dev/null +++ b/v4l2-apps/libv4l/libv4lconvert/ov511-decomp.c @@ -0,0 +1,666 @@ +/* We would like to embed this inside libv4l, but we cannot as I've failed + to contact Mark W. McClelland to get permission to relicense this, + so this lives in an external (GPL licensed) helper */ + +/* OV511 Decompression Support Module + * + * Copyright (c) 1999-2003 Mark W. McClelland. All rights reserved. + * http://alpha.dyndns.org/ov511/ + * + * Original decompression code Copyright 1998-2000 OmniVision Technologies + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; version 2 of the License. + */ + +#include <string.h> +#include <unistd.h> +#include "helper-funcs.h" + +/****************************************************************************** + * Decompression Functions + ******************************************************************************/ + +static void +DecompressYHI(unsigned char *pIn, + unsigned char *pOut, + int *iIn, /* in/out */ + int *iOut, /* in/out */ + const int w, + const int YUVFlag) +{ + short ZigZag[64]; + int temp[64]; + int Zcnt_Flag = 0; + int Num8_Flag = 0; + int in_pos = *iIn; + int out_pos = *iOut; + int tmp, tmp1, tmp2, tmp3; + unsigned char header, ZTable[64]; + short tmpl, tmph, half_byte, idx, count; + unsigned long ZigZag_length = 0, ZT_length, i, j; + short DeZigZag[64]; + + const short a = 11584; + const short b = 16068; + const short c = 15136; + const short d = 13624; + const short e = 9104; + const short f = 6270; + const short g = 3196; + + int out_idx; + + /* Take off every 'Zig' */ + for (i = 0; i < 64; i++) { + ZigZag[i] = 0; + } + + /***************************** + * Read in the Y header byte * + *****************************/ + + header = pIn[in_pos]; + in_pos++; + + ZigZag_length = header & 0x3f; + ZigZag_length = ZigZag_length + 1; + + Num8_Flag = header & 0x40; + Zcnt_Flag = header & 0x80; + + /************************* + * Read in the Y content * + *************************/ + + if (Zcnt_Flag == 0) { /* Without Zero Table read contents directly */ + /* Read in ZigZag[0] */ + ZigZag[0] = pIn[in_pos++]; + tmpl = pIn[in_pos++]; + tmph = tmpl<<8; + ZigZag[0] = ZigZag[0] | tmph; + ZigZag[0] = ZigZag[0]<<4; + ZigZag[0] = ZigZag[0]>>4; + + if (Num8_Flag) { /* 8 Bits */ + for (i = 1; i < ZigZag_length; i++) { + ZigZag[i] = pIn[in_pos++]; + ZigZag[i] = ZigZag[i]<<8; + ZigZag[i] = ZigZag[i]>>8; + } + } else { /* 12 bits and has no Zero Table */ + idx = 1; + half_byte = 0; + for (i = 1; i < ZigZag_length; i++) { + if (half_byte == 0) { + ZigZag[i] = pIn[in_pos++]; + tmpl = pIn[in_pos++]; + tmph = tmpl<<8; + tmph = tmph&0x0f00; + ZigZag[i] = ZigZag[i] | tmph; + ZigZag[i] = ZigZag[i]<<4; + ZigZag[i] = ZigZag[i]>>4; + half_byte = 1; + } else { + ZigZag[i] = pIn[in_pos++]; + ZigZag[i] = ZigZag[i]<<8; + tmpl = tmpl & 0x00f0; + ZigZag[i] = ZigZag[i] | tmpl; + ZigZag[i] = ZigZag[i]>>4; + half_byte = 0; + } + } + } + } else { /* Has Zero Table */ + /* Calculate Z-Table length */ + ZT_length = ZigZag_length/8; + tmp = ZigZag_length%8; + + if (tmp > 0) { + ZT_length = ZT_length + 1; + } + + /* Read in Zero Table */ + for (j = 0; j < ZT_length; j++) { + ZTable[j] = pIn[in_pos++]; + } + + /* Read in ZigZag[0] */ + ZigZag[0] = pIn[in_pos++]; + tmpl = pIn[in_pos++]; + tmph = tmpl<<8; + ZigZag[0] = ZigZag[0] | tmph; + ZigZag[0] = ZigZag[0]<<4; + ZigZag[0] = ZigZag[0]>>4; + + /* Decode ZigZag */ + idx = 0; + ZTable[idx] = ZTable[idx]<<1; + count = 7; + + if (Num8_Flag) { /* 8 Bits and has zero table */ + for (i = 1; i < ZigZag_length; i++) { + if ((ZTable[idx]&0x80)) { + ZigZag[i] = pIn[in_pos++]; + ZigZag[i] = ZigZag[i]<<8; + ZigZag[i] = ZigZag[i]>>8; + } + + ZTable[idx]=ZTable[idx]<<1; + count--; + if (count == 0) { + count = 8; + idx++; + } + } + } else { /* 12 bits and has Zero Table */ + half_byte = 0; + for (i = 1; i < ZigZag_length; i++) { + if (ZTable[idx]&0x80) { + if (half_byte == 0) { + ZigZag[i] = pIn[in_pos++]; + tmpl = pIn[in_pos++]; + tmph = tmpl <<8; + tmph = tmph & 0x0f00; + ZigZag[i] = ZigZag[i] | tmph; + ZigZag[i] = ZigZag[i]<<4; + ZigZag[i] = ZigZag[i]>>4; + half_byte = 1; + } else { + ZigZag[i] = pIn[in_pos++]; + ZigZag[i] = ZigZag[i]<<8; + tmpl = tmpl & 0x00f0; + ZigZag[i] = ZigZag[i] | tmpl; + ZigZag[i] = ZigZag[i]>>4; + half_byte = 0; + } + } + + ZTable[idx] = ZTable[idx]<<1; + count--; + if (count == 0) { + count = 8; + idx++; + } + } + } + } + + /************* + * De-ZigZag * + *************/ + + for (j = 0; j < 64; j++) { + DeZigZag[j] = 0; + } + + if (YUVFlag == 1) { + DeZigZag[0] = ZigZag[0]; + DeZigZag[1] = ZigZag[1]<<1; + DeZigZag[2] = ZigZag[5]<<1; + DeZigZag[3] = ZigZag[6]<<2; + + DeZigZag[8] = ZigZag[2]<<1; + DeZigZag[9] = ZigZag[4]<<1; + DeZigZag[10] = ZigZag[7]<<1; + DeZigZag[11] = ZigZag[13]<<2; + + DeZigZag[16] = ZigZag[3]<<1; + DeZigZag[17] = ZigZag[8]<<1; + DeZigZag[18] = ZigZag[12]<<2; + DeZigZag[19] = ZigZag[17]<<2; + + DeZigZag[24] = ZigZag[9]<<2; + DeZigZag[25] = ZigZag[11]<<2; + DeZigZag[26] = ZigZag[18]<<2; + DeZigZag[27] = ZigZag[24]<<3; + } else { + DeZigZag[0] = ZigZag[0]; + DeZigZag[1] = ZigZag[1]<<2; + DeZigZag[2] = ZigZag[5]<<2; + DeZigZag[3] = ZigZag[6]<<3; + + DeZigZag[8] = ZigZag[2]<<2; + DeZigZag[9] = ZigZag[4]<<2; + DeZigZag[10] = ZigZag[7]<<2; + DeZigZag[11] = ZigZag[13]<<4; + + DeZigZag[16] = ZigZag[3]<<2; + DeZigZag[17] = ZigZag[8]<<2; + DeZigZag[18] = ZigZag[12]<<3; + DeZigZag[19] = ZigZag[17]<<4; + + DeZigZag[24] = ZigZag[9]<<3; + DeZigZag[25] = ZigZag[11]<<4; + DeZigZag[26] = ZigZag[18]<<4; + DeZigZag[27] = ZigZag[24]<<4; + } + + /***************** + **** IDCT 1D **** + *****************/ + +#define IDCT_1D(c0, c1, c2, c3, in) \ + do { \ + tmp1=((c0)*DeZigZag[in])+((c2)*DeZigZag[(in)+2]); \ + tmp2=(c1)*DeZigZag[(in)+1]; \ + tmp3=(c3)*DeZigZag[(in)+3]; \ + } while (0) + +#define COMPOSE_1(out1, out2) \ + do { \ + tmp=tmp1+tmp2+tmp3; \ + temp[out1] = tmp>>15; \ + tmp=tmp1-tmp2-tmp3; \ + temp[out2] = tmp>>15; \ + } while (0) + +#define COMPOSE_2(out1, out2) \ + do { \ + tmp=tmp1+tmp2-tmp3; \ + temp[out1] = tmp>>15; \ + tmp=tmp1-tmp2+tmp3; \ + temp[out2] = tmp>>15; \ + } while (0) + + /* j = 0 */ + IDCT_1D(a, b, c, d, 0); COMPOSE_1( 0, 56); + IDCT_1D(a, b, c, d, 8); COMPOSE_1( 1, 57); + IDCT_1D(a, b, c, d, 16); COMPOSE_1( 2, 58); + IDCT_1D(a, b, c, d, 24); COMPOSE_1( 3, 59); + + /* j = 1 */ + IDCT_1D(a, d, f, g, 0); COMPOSE_2( 8, 48); + IDCT_1D(a, d, f, g, 8); COMPOSE_2( 9, 49); + IDCT_1D(a, d, f, g, 16); COMPOSE_2(10, 50); + IDCT_1D(a, d, f, g, 24); COMPOSE_2(11, 51); + + /* j = 2 */ + IDCT_1D(a, e, -f, b, 0); COMPOSE_2(16, 40); + IDCT_1D(a, e, -f, b, 8); COMPOSE_2(17, 41); + IDCT_1D(a, e, -f, b, 16); COMPOSE_2(18, 42); + IDCT_1D(a, e, -f, b, 24); COMPOSE_2(19, 43); + + /* j = 3 */ + IDCT_1D(a, g, -c, e, 0); COMPOSE_2(24, 32); + IDCT_1D(a, g, -c, e, 8); COMPOSE_2(25, 33); + IDCT_1D(a, g, -c, e, 16); COMPOSE_2(26, 34); + IDCT_1D(a, g, -c, e, 24); COMPOSE_2(27, 35); + +#undef IDCT_1D +#undef COMPOSE_1 +#undef COMPOSE_2 + + /***************** + **** IDCT 2D **** + *****************/ + +#define IDCT_2D(c0, c1, c2, c3, in) \ + do { \ + tmp = temp[in]*(c0) + temp[(in)+1]*(c1) \ + + temp[(in)+2]*(c2) + temp[(in)+3]*(c3); \ + } while (0) + +#define STORE(i) \ + do { \ + tmp = tmp >> 15; \ + tmp = tmp + 128; \ + if (tmp > 255) tmp = 255; \ + if (tmp < 0) tmp = 0; \ + pOut[i] = (unsigned char) tmp; \ + } while (0) + +#define IDCT_2D_ROW(in) \ + do { \ + IDCT_2D(a, b, c, d, in); STORE(0+out_idx); \ + IDCT_2D(a, d, f, -g, in); STORE(1+out_idx); \ + IDCT_2D(a, e, -f, -b, in); STORE(2+out_idx); \ + IDCT_2D(a, g, -c, -e, in); STORE(3+out_idx); \ + IDCT_2D(a, -g, -c, e, in); STORE(4+out_idx); \ + IDCT_2D(a, -e, -f, b, in); STORE(5+out_idx); \ + IDCT_2D(a, -d, f, g, in); STORE(6+out_idx); \ + IDCT_2D(a, -b, c, -d, in); STORE(7+out_idx); \ + } while (0) + + +#define IDCT_2D_FAST(c0, c1, c2, c3, in) \ + do { \ + tmp1=((c0)*temp[in])+((c2)*temp[(in)+2]); \ + tmp2=(c1)*temp[(in)+1]; \ + tmp3=(c3)*temp[(in)+3]; \ + } while (0) + +#define STORE_FAST_1(out1, out2) \ + do { \ + tmp=tmp1+tmp2+tmp3; \ + STORE((out1)+out_idx); \ + tmp=tmp1-tmp2-tmp3; \ + STORE((out2)+out_idx); \ + } while (0) + +#define STORE_FAST_2(out1, out2) \ + do { \ + tmp=tmp1+tmp2-tmp3; \ + STORE((out1)+out_idx); \ + tmp=tmp1-tmp2+tmp3; \ + STORE((out2)+out_idx); \ + } while (0) + +#define IDCT_2D_FAST_ROW(in) \ + do { \ + IDCT_2D_FAST(a, b, c, d, in); STORE_FAST_1(0, 7); \ + IDCT_2D_FAST(a, d, f, g, in); STORE_FAST_2(1, 6); \ + IDCT_2D_FAST(a, e, -f, b, in); STORE_FAST_2(2, 5); \ + IDCT_2D_FAST(a, g, -c, e, in); STORE_FAST_2(3, 4); \ + } while (0) + + out_idx = out_pos; + + IDCT_2D_ROW(0); out_idx += w; + IDCT_2D_ROW(8); out_idx += w; + IDCT_2D_ROW(16); out_idx += w; + IDCT_2D_ROW(24); out_idx += w; + IDCT_2D_ROW(32); out_idx += w; + IDCT_2D_ROW(40); out_idx += w; + IDCT_2D_FAST_ROW(48); out_idx += w; + IDCT_2D_FAST_ROW(56); + + *iIn = in_pos; + *iOut = out_pos + 8; +} + +#define DECOMP_Y() DecompressYHI(pIn, pY, &iIn, &iY, w, 1) +#define DECOMP_U() DecompressYHI(pIn, pU, &iIn, &iU, w/2, 2) +#define DECOMP_V() DecompressYHI(pIn, pV, &iIn, &iV, w/2, 2) + +#if 0 +inline static int +Decompress400HiNoMMX(unsigned char *pIn, + unsigned char *pOut, + const int w, + const int h, + const int inSize) +{ + unsigned char *pY = pOut; + int x, y, iIn, iY; + + iIn = 0; + for (y = 0; y < h; y += 8) { + iY = w*y; + + for (x = 0; x < w; x += 8) + DECOMP_Y(); + } + + return 0; +} +#endif + +inline static int +Decompress420HiNoMMX(unsigned char *pIn, + unsigned char *pOut, + const int w, + const int h, + const int inSize) +{ + unsigned char *pY = pOut; + unsigned char *pU = pY + w*h; + unsigned char *pV = pU + w*h/4; + int xY, xUV, iY, iU, iV, iIn, count; + const int nBlocks = (w*h) / (32*8); + + iIn = 0; + iY = iU = iV = 0; + xY = xUV = 0; + + for (count = 0; count < nBlocks; count++) { + DECOMP_U(); + DECOMP_V(); xUV += 16; + if (xUV >= w) { + iU += (w*7)/2; + iV += (w*7)/2; + xUV = 0; + } + + DECOMP_Y(); xY += 8; + DECOMP_Y(); xY += 8; + if (xY >= w) { + iY += w*7; + xY = 0; + } + DECOMP_Y(); xY += 8; + DECOMP_Y(); xY += 8; + if (xY >= w) { + iY += w*7; + xY = 0; + } + } + + return 0; +} + +/* Copies a 64-byte segment at pIn to an 8x8 block at pOut. The width of the + * image at pOut is specified by w. + */ +static inline void +make_8x8(unsigned char *pIn, unsigned char *pOut, int w) +{ + unsigned char *pOut1 = pOut; + int x, y; + + for (y = 0; y < 8; y++) { + pOut1 = pOut; + for (x = 0; x < 8; x++) { + *pOut1++ = *pIn++; + } + pOut += w; + } +} + +#if 0 +/* + * For RAW BW (YUV 4:0:0) images, data show up in 256 byte segments. + * The segments represent 4 squares of 8x8 pixels as follows: + * + * 0 1 ... 7 64 65 ... 71 ... 192 193 ... 199 + * 8 9 ... 15 72 73 ... 79 200 201 ... 207 + * ... ... ... + * 56 57 ... 63 120 121 ... 127 248 249 ... 255 + * + */ +static void +yuv400raw_to_yuv400p(struct ov511_frame *frame, + unsigned char *pIn0, unsigned char *pOut0) +{ + int x, y; + unsigned char *pIn, *pOut, *pOutLine; + + /* Copy Y */ + pIn = pIn0; + pOutLine = pOut0; + for (y = 0; y < frame->rawheight - 1; y += 8) { + pOut = pOutLine; + for (x = 0; x < frame->rawwidth - 1; x += 8) { + make_8x8(pIn, pOut, frame->rawwidth); + pIn += 64; + pOut += 8; + } + pOutLine += 8 * frame->rawwidth; + } +} +#endif + +/* + * For YUV 4:2:0 images, the data show up in 384 byte segments. + * The first 64 bytes of each segment are U, the next 64 are V. The U and + * V are arranged as follows: + * + * 0 1 ... 7 + * 8 9 ... 15 + * ... + * 56 57 ... 63 + * + * U and V are shipped at half resolution (1 U,V sample -> one 2x2 block). + * + * The next 256 bytes are full resolution Y data and represent 4 squares + * of 8x8 pixels as follows: + * + * 0 1 ... 7 64 65 ... 71 ... 192 193 ... 199 + * 8 9 ... 15 72 73 ... 79 200 201 ... 207 + * ... ... ... + * 56 57 ... 63 120 121 ... 127 ... 248 249 ... 255 + * + * Note that the U and V data in one segment represent a 16 x 16 pixel + * area, but the Y data represent a 32 x 8 pixel area. If the width is not an + * even multiple of 32, the extra 8x8 blocks within a 32x8 block belong to the + * next horizontal stripe. + * + * If dumppix module param is set, _parse_data just dumps the incoming segments, + * verbatim, in order, into the frame. When used with vidcat -f ppm -s 640x480 + * this puts the data on the standard output and can be analyzed with the + * parseppm.c utility I wrote. That's a much faster way for figuring out how + * these data are scrambled. + */ + +/* Converts from raw, uncompressed segments at pIn0 to a YUV420P frame at pOut0. + * + * FIXME: Currently only handles width and height that are multiples of 16 + */ +static void +yuv420raw_to_yuv420p(unsigned char *pIn0, unsigned char *pOut0, + int width, int height) +{ + int k, x, y; + unsigned char *pIn, *pOut, *pOutLine; + const unsigned int a = width * height; + const unsigned int w = width / 2; + + /* Copy U and V */ + pIn = pIn0; + pOutLine = pOut0 + a; + for (y = 0; y < height - 1; y += 16) { + pOut = pOutLine; + for (x = 0; x < width - 1; x += 16) { + make_8x8(pIn, pOut, w); + make_8x8(pIn + 64, pOut + a/4, w); + pIn += 384; + pOut += 8; + } + pOutLine += 8 * w; + } + + /* Copy Y */ + pIn = pIn0 + 128; + pOutLine = pOut0; + k = 0; + for (y = 0; y < height - 1; y += 8) { + pOut = pOutLine; + for (x = 0; x < width - 1; x += 8) { + make_8x8(pIn, pOut, width); + pIn += 64; + pOut += 8; + if ((++k) > 3) { + k = 0; + pIn += 128; + } + } + pOutLine += 8 * width; + } +} + + +/* Remove all 0 blocks from input */ +static void remove0blocks(unsigned char *pIn, int *inSize) +{ + long long *in = (long long *)pIn; + long long *out = (long long *)pIn; + int i, j; + + for (i = 0; i < *inSize; i += 32, in += 4) { + int all_zero = 1; + for (j = 0; j < 4; j++) + if (in[j]) { + all_zero = 0; + break; + } + + /* Skip 32 byte blocks of all 0 */ + if (all_zero) + continue; + + for (j = 0; j < 4; j++) + *out++ = in[j]; + } + + *inSize -= (in - out) * 8; +} + +static int v4lconvert_ov511_to_yuv420(unsigned char *src, unsigned char *dest, + int w, int h, int yvu, int src_size) +{ + int rc = 0; + + src_size -= 11; /* Remove footer */ + + remove0blocks(src, &src_size); + + /* Compressed ? */ + if (src[8] & 0x40) { + rc = Decompress420HiNoMMX(src + 9, dest, w, h, src_size); + } else { + yuv420raw_to_yuv420p(src + 9, dest, w, h); + } + + return rc; +} + +int main(int argc, char *argv[]) +{ + int width, height, yvu, src_size, dest_size; + unsigned char src_buf[500000]; + unsigned char dest_buf[500000]; + + while (1) { + if (v4lconvert_helper_read(STDIN_FILENO, &width, sizeof(int), argv[0])) + return 1; /* Erm, no way to recover without loosing sync with libv4l */ + + if (v4lconvert_helper_read(STDIN_FILENO, &height, sizeof(int), argv[0])) + return 1; /* Erm, no way to recover without loosing sync with libv4l */ + + if (v4lconvert_helper_read(STDIN_FILENO, &yvu, sizeof(int), argv[0])) + return 1; /* Erm, no way to recover without loosing sync with libv4l */ + + if (v4lconvert_helper_read(STDIN_FILENO, &src_size, sizeof(int), argv[0])) + return 1; /* Erm, no way to recover without loosing sync with libv4l */ + + if (src_size > sizeof(src_buf)) { + fprintf(stderr, "%s: error: src_buf too small, need: %d\n", + argv[0], src_size); + return 2; + } + + if (v4lconvert_helper_read(STDIN_FILENO, src_buf, src_size, argv[0])) + return 1; /* Erm, no way to recover without loosing sync with libv4l */ + + + dest_size = width * height * 3 / 2; + if (dest_size > sizeof(dest_buf)) { + fprintf(stderr, "%s: error: dest_buf too small, need: %d\n", + argv[0], dest_size); + dest_size = -1; + } else if (v4lconvert_ov511_to_yuv420(src_buf, dest_buf, width, height, + yvu, src_size)) + dest_size = -1; + + if (v4lconvert_helper_write(STDOUT_FILENO, &dest_size, sizeof(int), + argv[0])) + return 1; /* Erm, no way to recover without loosing sync with libv4l */ + + if (dest_size == -1) + continue; + + if (v4lconvert_helper_write(STDOUT_FILENO, dest_buf, dest_size, argv[0])) + return 1; /* Erm, no way to recover without loosing sync with libv4l */ + } +} diff --git a/v4l2-apps/libv4l/libv4lconvert/ov518-decomp.c b/v4l2-apps/libv4l/libv4lconvert/ov518-decomp.c new file mode 100644 index 000000000..51b8d8c60 --- /dev/null +++ b/v4l2-apps/libv4l/libv4lconvert/ov518-decomp.c @@ -0,0 +1,1477 @@ +/* We would like to embed this inside libv4l, but we cannot as I've failed + to contact Mark W. McClelland to get permission to relicense this, + so this lives in an external (GPL licensed) helper */ + +/* OV518 Decompression Support Module (No-MMX version) + * + * Copyright (c) 2002-2003 Mark W. McClelland. All rights reserved. + * http://alpha.dyndns.org/ov511/ + * + * Fast integer iDCT by Yuri van Oers <yvanoers AT xs4all.nl> + * Original OV511 decompression code Copyright 1998-2000 OmniVision Technologies + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; version 2 of the License. + */ + +#include <string.h> +#include <unistd.h> +#include "helper-funcs.h" + +/****************************************************************************** + * Compile-time Options + ******************************************************************************/ + +/* Defining APPROXIMATE_MUL_BY_SHIFT increases performance by approximation + * the multiplications by shifts. I think there's no change in the + * calculated picture, but I'm not sure, so the choice is still in here. */ +#undef APPROXIMATE_MUL_BY_SHIFT + +/****************************************************************************** + * Local Data Types + ******************************************************************************/ + +/* Make sure this remains naturally aligned and 2^n bytes in size */ +struct tree_node { + short left; /* Pointer to left child node */ + short right; /* Pointer to right child node */ + signed char depth; /* Depth (starting at 1) if leaf, else -1 */ + signed char coeffbits; /* Size of coefficient data, or zero if none */ + signed char skip; /* Number of zero coefficients. Unused w/ DC */ + char padding; /* Pad out to 8 bytes */ +}; + +struct comp_info { + int bytes; /* Number of processed input bytes */ + int bits; /* Number of unprocessed input bits */ + int rawLen; /* Total number of bytes in input buffer */ + unsigned char *qt; /* Current quantization table */ +}; + +/****************************************************************************** + * Constant Data Definitions + ******************************************************************************/ + +/* Zig-Zag Table */ +static const unsigned char ZigZag518[] = { + 0x00, 0x02, 0x03, 0x09, + 0x01, 0x04, 0x08, 0x0a, + 0x05, 0x07, 0x0b, 0x11, + 0x06, 0x0c, 0x10, 0x12, + 0x0d, 0x0f, 0x13, 0x19, + 0x0e, 0x14, 0x18, 0x1a, + 0x15, 0x17, 0x1b, 0x1e, + 0x16, 0x1c, 0x1d, 0x1f +}; + +/* Huffman trees */ + +static const struct tree_node treeYAC[] = { + { 1, 4, -1, 0, -1}, { 2, 3, -1, 0, -1}, + { -1, -1, 2, 1, 0}, { -1, -1, 2, 2, 0}, + { 5, 9, -1, 0, -1}, { 6, 7, -1, 0, -1}, + { -1, -1, 3, 3, 0}, {323, 8, -1, 0, -1}, + { -1, -1, 4, 4, 0}, { 10, 13, -1, 0, -1}, + { 38, 11, -1, 0, -1}, { 12, 39, -1, 0, -1}, + { -1, -1, 5, 5, 0}, { 59, 14, -1, 0, -1}, + { 15, 18, -1, 0, -1}, { 16, 113, -1, 0, -1}, + { 17, 40, -1, 0, -1}, { -1, -1, 7, 6, 0}, + { 19, 22, -1, 0, -1}, { 20, 41, -1, 0, -1}, + { 21, 61, -1, 0, -1}, { -1, -1, 8, 7, 0}, + { 23, 27, -1, 0, -1}, {169, 24, -1, 0, -1}, + {208, 25, -1, 0, -1}, { 26, 62, -1, 0, -1}, + { -1, -1, 10, 8, 0}, { 44, 28, -1, 0, -1}, + { 63, 29, -1, 0, -1}, { 30, 191, -1, 0, -1}, + { 31, 119, -1, 0, -1}, { 32, 82, -1, 0, -1}, + { 33, 55, -1, 0, -1}, { 34, 48, -1, 0, -1}, + {171, 35, -1, 0, -1}, { 36, 37, -1, 0, -1}, + { -1, -1, 16, 9, 0}, { -1, -1, 16, 10, 0}, + { -1, -1, 4, 1, 1}, { -1, -1, 5, 2, 1}, + { -1, -1, 7, 3, 1}, {151, 42, -1, 0, -1}, + { 43, 79, -1, 0, -1}, { -1, -1, 9, 4, 1}, + { 96, 45, -1, 0, -1}, {246, 46, -1, 0, -1}, + { 47, 115, -1, 0, -1}, { -1, -1, 11, 5, 1}, + { 49, 52, -1, 0, -1}, { 50, 51, -1, 0, -1}, + { -1, -1, 16, 6, 1}, { -1, -1, 16, 7, 1}, + { 53, 54, -1, 0, -1}, { -1, -1, 16, 8, 1}, + { -1, -1, 16, 9, 1}, { 56, 71, -1, 0, -1}, + { 57, 68, -1, 0, -1}, { 58, 67, -1, 0, -1}, + { -1, -1, 16, 10, 1}, { 60, 77, -1, 0, -1}, + { -1, -1, 5, 1, 2}, { -1, -1, 8, 2, 2}, + { -1, -1, 10, 3, 2}, {265, 64, -1, 0, -1}, + { 65, 134, -1, 0, -1}, { 66, 80, -1, 0, -1}, + { -1, -1, 12, 4, 2}, { -1, -1, 16, 5, 2}, + { 69, 70, -1, 0, -1}, { -1, -1, 16, 6, 2}, + { -1, -1, 16, 7, 2}, { 72, 75, -1, 0, -1}, + { 73, 74, -1, 0, -1}, { -1, -1, 16, 8, 2}, + { -1, -1, 16, 9, 2}, { 76, 81, -1, 0, -1}, + { -1, -1, 16, 10, 2}, { 78, 95, -1, 0, -1}, + { -1, -1, 6, 1, 3}, { -1, -1, 9, 2, 3}, + { -1, -1, 12, 3, 3}, { -1, -1, 16, 4, 3}, + { 83, 101, -1, 0, -1}, { 84, 91, -1, 0, -1}, + { 85, 88, -1, 0, -1}, { 86, 87, -1, 0, -1}, + { -1, -1, 16, 5, 3}, { -1, -1, 16, 6, 3}, + { 89, 90, -1, 0, -1}, { -1, -1, 16, 7, 3}, + { -1, -1, 16, 8, 3}, { 92, 98, -1, 0, -1}, + { 93, 94, -1, 0, -1}, { -1, -1, 16, 9, 3}, + { -1, -1, 16, 10, 3}, { -1, -1, 6, 1, 4}, + { 97, 225, -1, 0, -1}, { -1, -1, 10, 2, 4}, + { 99, 100, -1, 0, -1}, { -1, -1, 16, 3, 4}, + { -1, -1, 16, 4, 4}, {102, 109, -1, 0, -1}, + {103, 106, -1, 0, -1}, {104, 105, -1, 0, -1}, + { -1, -1, 16, 5, 4}, { -1, -1, 16, 6, 4}, + {107, 108, -1, 0, -1}, { -1, -1, 16, 7, 4}, + { -1, -1, 16, 8, 4}, {110, 116, -1, 0, -1}, + {111, 112, -1, 0, -1}, { -1, -1, 16, 9, 4}, + { -1, -1, 16, 10, 4}, {114, 133, -1, 0, -1}, + { -1, -1, 7, 1, 5}, { -1, -1, 11, 2, 5}, + {117, 118, -1, 0, -1}, { -1, -1, 16, 3, 5}, + { -1, -1, 16, 4, 5}, {120, 156, -1, 0, -1}, + {121, 139, -1, 0, -1}, {122, 129, -1, 0, -1}, + {123, 126, -1, 0, -1}, {124, 125, -1, 0, -1}, + { -1, -1, 16, 5, 5}, { -1, -1, 16, 6, 5}, + {127, 128, -1, 0, -1}, { -1, -1, 16, 7, 5}, + { -1, -1, 16, 8, 5}, {130, 136, -1, 0, -1}, + {131, 132, -1, 0, -1}, { -1, -1, 16, 9, 5}, + { -1, -1, 16, 10, 5}, { -1, -1, 7, 1, 6}, + {135, 152, -1, 0, -1}, { -1, -1, 12, 2, 6}, + {137, 138, -1, 0, -1}, { -1, -1, 16, 3, 6}, + { -1, -1, 16, 4, 6}, {140, 147, -1, 0, -1}, + {141, 144, -1, 0, -1}, {142, 143, -1, 0, -1}, + { -1, -1, 16, 5, 6}, { -1, -1, 16, 6, 6}, + {145, 146, -1, 0, -1}, { -1, -1, 16, 7, 6}, + { -1, -1, 16, 8, 6}, {148, 153, -1, 0, -1}, + {149, 150, -1, 0, -1}, { -1, -1, 16, 9, 6}, + { -1, -1, 16, 10, 6}, { -1, -1, 8, 1, 7}, + { -1, -1, 12, 2, 7}, {154, 155, -1, 0, -1}, + { -1, -1, 16, 3, 7}, { -1, -1, 16, 4, 7}, + {157, 175, -1, 0, -1}, {158, 165, -1, 0, -1}, + {159, 162, -1, 0, -1}, {160, 161, -1, 0, -1}, + { -1, -1, 16, 5, 7}, { -1, -1, 16, 6, 7}, + {163, 164, -1, 0, -1}, { -1, -1, 16, 7, 7}, + { -1, -1, 16, 8, 7}, {166, 172, -1, 0, -1}, + {167, 168, -1, 0, -1}, { -1, -1, 16, 9, 7}, + { -1, -1, 16, 10, 7}, {170, 187, -1, 0, -1}, + { -1, -1, 9, 1, 8}, { -1, -1, 15, 2, 8}, + {173, 174, -1, 0, -1}, { -1, -1, 16, 3, 8}, + { -1, -1, 16, 4, 8}, {176, 183, -1, 0, -1}, + {177, 180, -1, 0, -1}, {178, 179, -1, 0, -1}, + { -1, -1, 16, 5, 8}, { -1, -1, 16, 6, 8}, + {181, 182, -1, 0, -1}, { -1, -1, 16, 7, 8}, + { -1, -1, 16, 8, 8}, {184, 188, -1, 0, -1}, + {185, 186, -1, 0, -1}, { -1, -1, 16, 9, 8}, + { -1, -1, 16, 10, 8}, { -1, -1, 9, 1, 9}, + {189, 190, -1, 0, -1}, { -1, -1, 16, 2, 9}, + { -1, -1, 16, 3, 9}, {192, 258, -1, 0, -1}, + {193, 226, -1, 0, -1}, {194, 210, -1, 0, -1}, + {195, 202, -1, 0, -1}, {196, 199, -1, 0, -1}, + {197, 198, -1, 0, -1}, { -1, -1, 16, 4, 9}, + { -1, -1, 16, 5, 9}, {200, 201, -1, 0, -1}, + { -1, -1, 16, 6, 9}, { -1, -1, 16, 7, 9}, + {203, 206, -1, 0, -1}, {204, 205, -1, 0, -1}, + { -1, -1, 16, 8, 9}, { -1, -1, 16, 9, 9}, + {207, 209, -1, 0, -1}, { -1, -1, 16, 10, 9}, + { -1, -1, 9, 1, 10}, { -1, -1, 16, 2, 10}, + {211, 218, -1, 0, -1}, {212, 215, -1, 0, -1}, + {213, 214, -1, 0, -1}, { -1, -1, 16, 3, 10}, + { -1, -1, 16, 4, 10}, {216, 217, -1, 0, -1}, + { -1, -1, 16, 5, 10}, { -1, -1, 16, 6, 10}, + {219, 222, -1, 0, -1}, {220, 221, -1, 0, -1}, + { -1, -1, 16, 7, 10}, { -1, -1, 16, 8, 10}, + {223, 224, -1, 0, -1}, { -1, -1, 16, 9, 10}, + { -1, -1, 16, 10, 10}, { -1, -1, 10, 1, 11}, + {227, 242, -1, 0, -1}, {228, 235, -1, 0, -1}, + {229, 232, -1, 0, -1}, {230, 231, -1, 0, -1}, + { -1, -1, 16, 2, 11}, { -1, -1, 16, 3, 11}, + {233, 234, -1, 0, -1}, { -1, -1, 16, 4, 11}, + { -1, -1, 16, 5, 11}, {236, 239, -1, 0, -1}, + {237, 238, -1, 0, -1}, { -1, -1, 16, 6, 11}, + { -1, -1, 16, 7, 11}, {240, 241, -1, 0, -1}, + { -1, -1, 16, 8, 11}, { -1, -1, 16, 9, 11}, + {243, 251, -1, 0, -1}, {244, 248, -1, 0, -1}, + {245, 247, -1, 0, -1}, { -1, -1, 16, 10, 11}, + { -1, -1, 10, 1, 12}, { -1, -1, 16, 2, 12}, + {249, 250, -1, 0, -1}, { -1, -1, 16, 3, 12}, + { -1, -1, 16, 4, 12}, {252, 255, -1, 0, -1}, + {253, 254, -1, 0, -1}, { -1, -1, 16, 5, 12}, + { -1, -1, 16, 6, 12}, {256, 257, -1, 0, -1}, + { -1, -1, 16, 7, 12}, { -1, -1, 16, 8, 12}, + {259, 292, -1, 0, -1}, {260, 277, -1, 0, -1}, + {261, 270, -1, 0, -1}, {262, 267, -1, 0, -1}, + {263, 264, -1, 0, -1}, { -1, -1, 16, 9, 12}, + { -1, -1, 16, 10, 12}, {266, 322, -1, 0, -1}, + { -1, -1, 11, 1, 13}, {268, 269, -1, 0, -1}, + { -1, -1, 16, 2, 13}, { -1, -1, 16, 3, 13}, + {271, 274, -1, 0, -1}, {272, 273, -1, 0, -1}, + { -1, -1, 16, 4, 13}, { -1, -1, 16, 5, 13}, + {275, 276, -1, 0, -1}, { -1, -1, 16, 6, 13}, + { -1, -1, 16, 7, 13}, {278, 285, -1, 0, -1}, + {279, 282, -1, 0, -1}, {280, 281, -1, 0, -1}, + { -1, -1, 16, 8, 13}, { -1, -1, 16, 9, 13}, + {283, 284, -1, 0, -1}, { -1, -1, 16, 10, 13}, + { -1, -1, 16, 1, 14}, {286, 289, -1, 0, -1}, + {287, 288, -1, 0, -1}, { -1, -1, 16, 2, 14}, + { -1, -1, 16, 3, 14}, {290, 291, -1, 0, -1}, + { -1, -1, 16, 4, 14}, { -1, -1, 16, 5, 14}, + {293, 308, -1, 0, -1}, {294, 301, -1, 0, -1}, + {295, 298, -1, 0, -1}, {296, 297, -1, 0, -1}, + { -1, -1, 16, 6, 14}, { -1, -1, 16, 7, 14}, + {299, 300, -1, 0, -1}, { -1, -1, 16, 8, 14}, + { -1, -1, 16, 9, 14}, {302, 305, -1, 0, -1}, + {303, 304, -1, 0, -1}, { -1, -1, 16, 10, 14}, + { -1, -1, 16, 1, 15}, {306, 307, -1, 0, -1}, + { -1, -1, 16, 2, 15}, { -1, -1, 16, 3, 15}, + {309, 316, -1, 0, -1}, {310, 313, -1, 0, -1}, + {311, 312, -1, 0, -1}, { -1, -1, 16, 4, 15}, + { -1, -1, 16, 5, 15}, {314, 315, -1, 0, -1}, + { -1, -1, 16, 6, 15}, { -1, -1, 16, 7, 15}, + {317, 320, -1, 0, -1}, {318, 319, -1, 0, -1}, + { -1, -1, 16, 8, 15}, { -1, -1, 16, 9, 15}, + {321, -1, -1, 0, -1}, { -1, -1, 16, 10, 15}, + { -1, -1, 11, 0, 16}, { -1, -1, 4, 0, -1}, +}; + +static const struct tree_node treeUVAC[] = { + { 1, 3, -1, 0, -1}, {323, 2, -1, 0, -1}, + { -1, -1, 2, 1, 0}, { 4, 8, -1, 0, -1}, + { 5, 6, -1, 0, -1}, { -1, -1, 3, 2, 0}, + { 7, 37, -1, 0, -1}, { -1, -1, 4, 3, 0}, + { 9, 13, -1, 0, -1}, { 10, 60, -1, 0, -1}, + { 11, 12, -1, 0, -1}, { -1, -1, 5, 4, 0}, + { -1, -1, 5, 5, 0}, { 14, 17, -1, 0, -1}, + { 15, 97, -1, 0, -1}, { 16, 38, -1, 0, -1}, + { -1, -1, 6, 6, 0}, { 18, 21, -1, 0, -1}, + { 19, 39, -1, 0, -1}, { 20, 135, -1, 0, -1}, + { -1, -1, 7, 7, 0}, { 22, 26, -1, 0, -1}, + { 82, 23, -1, 0, -1}, { 24, 99, -1, 0, -1}, + { 25, 42, -1, 0, -1}, { -1, -1, 9, 8, 0}, + { 27, 31, -1, 0, -1}, {211, 28, -1, 0, -1}, + {248, 29, -1, 0, -1}, { 30, 63, -1, 0, -1}, + { -1, -1, 10, 9, 0}, { 43, 32, -1, 0, -1}, + { 33, 48, -1, 0, -1}, {153, 34, -1, 0, -1}, + { 35, 64, -1, 0, -1}, { 36, 47, -1, 0, -1}, + { -1, -1, 12, 10, 0}, { -1, -1, 4, 1, 1}, + { -1, -1, 6, 2, 1}, {152, 40, -1, 0, -1}, + { 41, 62, -1, 0, -1}, { -1, -1, 8, 3, 1}, + { -1, -1, 9, 4, 1}, { 84, 44, -1, 0, -1}, + {322, 45, -1, 0, -1}, { 46, 136, -1, 0, -1}, + { -1, -1, 11, 5, 1}, { -1, -1, 12, 6, 1}, + { 49, 189, -1, 0, -1}, { 50, 119, -1, 0, -1}, + { 51, 76, -1, 0, -1}, { 66, 52, -1, 0, -1}, + { 53, 69, -1, 0, -1}, { 54, 57, -1, 0, -1}, + { 55, 56, -1, 0, -1}, { -1, -1, 16, 7, 1}, + { -1, -1, 16, 8, 1}, { 58, 59, -1, 0, -1}, + { -1, -1, 16, 9, 1}, { -1, -1, 16, 10, 1}, + { 61, 81, -1, 0, -1}, { -1, -1, 5, 1, 2}, + { -1, -1, 8, 2, 2}, { -1, -1, 10, 3, 2}, + { 65, 86, -1, 0, -1}, { -1, -1, 12, 4, 2}, + {286, 67, -1, 0, -1}, { 68, 304, -1, 0, -1}, + { -1, -1, 15, 5, 2}, { 70, 73, -1, 0, -1}, + { 71, 72, -1, 0, -1}, { -1, -1, 16, 6, 2}, + { -1, -1, 16, 7, 2}, { 74, 75, -1, 0, -1}, + { -1, -1, 16, 8, 2}, { -1, -1, 16, 9, 2}, + { 77, 102, -1, 0, -1}, { 78, 91, -1, 0, -1}, + { 79, 88, -1, 0, -1}, { 80, 87, -1, 0, -1}, + { -1, -1, 16, 10, 2}, { -1, -1, 5, 1, 3}, + { 83, 171, -1, 0, -1}, { -1, -1, 8, 2, 3}, + { 85, 117, -1, 0, -1}, { -1, -1, 10, 3, 3}, + { -1, -1, 12, 4, 3}, { -1, -1, 16, 5, 3}, + { 89, 90, -1, 0, -1}, { -1, -1, 16, 6, 3}, + { -1, -1, 16, 7, 3}, { 92, 95, -1, 0, -1}, + { 93, 94, -1, 0, -1}, { -1, -1, 16, 8, 3}, + { -1, -1, 16, 9, 3}, { 96, 101, -1, 0, -1}, + { -1, -1, 16, 10, 3}, { 98, 116, -1, 0, -1}, + { -1, -1, 6, 1, 4}, {100, 188, -1, 0, -1}, + { -1, -1, 9, 2, 4}, { -1, -1, 16, 3, 4}, + {103, 110, -1, 0, -1}, {104, 107, -1, 0, -1}, + {105, 106, -1, 0, -1}, { -1, -1, 16, 4, 4}, + { -1, -1, 16, 5, 4}, {108, 109, -1, 0, -1}, + { -1, -1, 16, 6, 4}, { -1, -1, 16, 7, 4}, + {111, 114, -1, 0, -1}, {112, 113, -1, 0, -1}, + { -1, -1, 16, 8, 4}, { -1, -1, 16, 9, 4}, + {115, 118, -1, 0, -1}, { -1, -1, 16, 10, 4}, + { -1, -1, 6, 1, 5}, { -1, -1, 10, 2, 5}, + { -1, -1, 16, 3, 5}, {120, 156, -1, 0, -1}, + {121, 138, -1, 0, -1}, {122, 129, -1, 0, -1}, + {123, 126, -1, 0, -1}, {124, 125, -1, 0, -1}, + { -1, -1, 16, 4, 5}, { -1, -1, 16, 5, 5}, + {127, 128, -1, 0, -1}, { -1, -1, 16, 6, 5}, + { -1, -1, 16, 7, 5}, {130, 133, -1, 0, -1}, + {131, 132, -1, 0, -1}, { -1, -1, 16, 8, 5}, + { -1, -1, 16, 9, 5}, {134, 137, -1, 0, -1}, + { -1, -1, 16, 10, 5}, { -1, -1, 7, 1, 6}, + { -1, -1, 11, 2, 6}, { -1, -1, 16, 3, 6}, + {139, 146, -1, 0, -1}, {140, 143, -1, 0, -1}, + {141, 142, -1, 0, -1}, { -1, -1, 16, 4, 6}, + { -1, -1, 16, 5, 6}, {144, 145, -1, 0, -1}, + { -1, -1, 16, 6, 6}, { -1, -1, 16, 7, 6}, + {147, 150, -1, 0, -1}, {148, 149, -1, 0, -1}, + { -1, -1, 16, 8, 6}, { -1, -1, 16, 9, 6}, + {151, 155, -1, 0, -1}, { -1, -1, 16, 10, 6}, + { -1, -1, 7, 1, 7}, {154, 267, -1, 0, -1}, + { -1, -1, 11, 2, 7}, { -1, -1, 16, 3, 7}, + {157, 173, -1, 0, -1}, {158, 165, -1, 0, -1}, + {159, 162, -1, 0, -1}, {160, 161, -1, 0, -1}, + { -1, -1, 16, 4, 7}, { -1, -1, 16, 5, 7}, + {163, 164, -1, 0, -1}, { -1, -1, 16, 6, 7}, + { -1, -1, 16, 7, 7}, {166, 169, -1, 0, -1}, + {167, 168, -1, 0, -1}, { -1, -1, 16, 8, 7}, + { -1, -1, 16, 9, 7}, {170, 172, -1, 0, -1}, + { -1, -1, 16, 10, 7}, { -1, -1, 8, 1, 8}, + { -1, -1, 16, 2, 8}, {174, 181, -1, 0, -1}, + {175, 178, -1, 0, -1}, {176, 177, -1, 0, -1}, + { -1, -1, 16, 3, 8}, { -1, -1, 16, 4, 8}, + {179, 180, -1, 0, -1}, { -1, -1, 16, 5, 8}, + { -1, -1, 16, 6, 8}, {182, 185, -1, 0, -1}, + {183, 184, -1, 0, -1}, { -1, -1, 16, 7, 8}, + { -1, -1, 16, 8, 8}, {186, 187, -1, 0, -1}, + { -1, -1, 16, 9, 8}, { -1, -1, 16, 10, 8}, + { -1, -1, 9, 1, 9}, {190, 257, -1, 0, -1}, + {191, 224, -1, 0, -1}, {192, 207, -1, 0, -1}, + {193, 200, -1, 0, -1}, {194, 197, -1, 0, -1}, + {195, 196, -1, 0, -1}, { -1, -1, 16, 2, 9}, + { -1, -1, 16, 3, 9}, {198, 199, -1, 0, -1}, + { -1, -1, 16, 4, 9}, { -1, -1, 16, 5, 9}, + {201, 204, -1, 0, -1}, {202, 203, -1, 0, -1}, + { -1, -1, 16, 6, 9}, { -1, -1, 16, 7, 9}, + {205, 206, -1, 0, -1}, { -1, -1, 16, 8, 9}, + { -1, -1, 16, 9, 9}, {208, 217, -1, 0, -1}, + {209, 214, -1, 0, -1}, {210, 213, -1, 0, -1}, + { -1, -1, 16, 10, 9}, {212, 230, -1, 0, -1}, + { -1, -1, 9, 1, 10}, { -1, -1, 16, 2, 10}, + {215, 216, -1, 0, -1}, { -1, -1, 16, 3, 10}, + { -1, -1, 16, 4, 10}, {218, 221, -1, 0, -1}, + {219, 220, -1, 0, -1}, { -1, -1, 16, 5, 10}, + { -1, -1, 16, 6, 10}, {222, 223, -1, 0, -1}, + { -1, -1, 16, 7, 10}, { -1, -1, 16, 8, 10}, + {225, 241, -1, 0, -1}, {226, 234, -1, 0, -1}, + {227, 231, -1, 0, -1}, {228, 229, -1, 0, -1}, + { -1, -1, 16, 9, 10}, { -1, -1, 16, 10, 10}, + { -1, -1, 9, 1, 11}, {232, 233, -1, 0, -1}, + { -1, -1, 16, 2, 11}, { -1, -1, 16, 3, 11}, + {235, 238, -1, 0, -1}, {236, 237, -1, 0, -1}, + { -1, -1, 16, 4, 11}, { -1, -1, 16, 5, 11}, + {239, 240, -1, 0, -1}, { -1, -1, 16, 6, 11}, + { -1, -1, 16, 7, 11}, {242, 250, -1, 0, -1}, + {243, 246, -1, 0, -1}, {244, 245, -1, 0, -1}, + { -1, -1, 16, 8, 11}, { -1, -1, 16, 9, 11}, + {247, 249, -1, 0, -1}, { -1, -1, 16, 10, 11}, + { -1, -1, 9, 1, 12}, { -1, -1, 16, 2, 12}, + {251, 254, -1, 0, -1}, {252, 253, -1, 0, -1}, + { -1, -1, 16, 3, 12}, { -1, -1, 16, 4, 12}, + {255, 256, -1, 0, -1}, { -1, -1, 16, 5, 12}, + { -1, -1, 16, 6, 12}, {258, 291, -1, 0, -1}, + {259, 275, -1, 0, -1}, {260, 268, -1, 0, -1}, + {261, 264, -1, 0, -1}, {262, 263, -1, 0, -1}, + { -1, -1, 16, 7, 12}, { -1, -1, 16, 8, 12}, + {265, 266, -1, 0, -1}, { -1, -1, 16, 9, 12}, + { -1, -1, 16, 10, 12}, { -1, -1, 11, 1, 13}, + {269, 272, -1, 0, -1}, {270, 271, -1, 0, -1}, + { -1, -1, 16, 2, 13}, { -1, -1, 16, 3, 13}, + {273, 274, -1, 0, -1}, { -1, -1, 16, 4, 13}, + { -1, -1, 16, 5, 13}, {276, 283, -1, 0, -1}, + {277, 280, -1, 0, -1}, {278, 279, -1, 0, -1}, + { -1, -1, 16, 6, 13}, { -1, -1, 16, 7, 13}, + {281, 282, -1, 0, -1}, { -1, -1, 16, 8, 13}, + { -1, -1, 16, 9, 13}, {284, 288, -1, 0, -1}, + {285, 287, -1, 0, -1}, { -1, -1, 16, 10, 13}, + { -1, -1, 14, 1, 14}, { -1, -1, 16, 2, 14}, + {289, 290, -1, 0, -1}, { -1, -1, 16, 3, 14}, + { -1, -1, 16, 4, 14}, {292, 308, -1, 0, -1}, + {293, 300, -1, 0, -1}, {294, 297, -1, 0, -1}, + {295, 296, -1, 0, -1}, { -1, -1, 16, 5, 14}, + { -1, -1, 16, 6, 14}, {298, 299, -1, 0, -1}, + { -1, -1, 16, 7, 14}, { -1, -1, 16, 8, 14}, + {301, 305, -1, 0, -1}, {302, 303, -1, 0, -1}, + { -1, -1, 16, 9, 14}, { -1, -1, 16, 10, 14}, + { -1, -1, 15, 1, 15}, {306, 307, -1, 0, -1}, + { -1, -1, 16, 2, 15}, { -1, -1, 16, 3, 15}, + {309, 316, -1, 0, -1}, {310, 313, -1, 0, -1}, + {311, 312, -1, 0, -1}, { -1, -1, 16, 4, 15}, + { -1, -1, 16, 5, 15}, {314, 315, -1, 0, -1}, + { -1, -1, 16, 6, 15}, { -1, -1, 16, 7, 15}, + {317, 320, -1, 0, -1}, {318, 319, -1, 0, -1}, + { -1, -1, 16, 8, 15}, { -1, -1, 16, 9, 15}, + {321, -1, -1, 0, -1}, { -1, -1, 16, 10, 15}, + { -1, -1, 10, 0, 16}, { -1, -1, 2, 0, -1}, +}; + +static const struct tree_node treeYDC[] = { + { 1, 6, -1, 0}, { 2, 3, -1, 0}, + { -1, -1, 2, 0}, { 4, 5, -1, 0}, + { -1, -1, 3, 1}, { -1, -1, 3, 2}, + { 7, 10, -1, 0}, { 8, 9, -1, 0}, + { -1, -1, 3, 3}, { -1, -1, 3, 4}, + { 11, 12, -1, 0}, { -1, -1, 3, 5}, + { 13, 14, -1, 0}, { -1, -1, 4, 6}, + { 15, 16, -1, 0}, { -1, -1, 5, 7}, + { 17, 18, -1, 0}, { -1, -1, 6, 8}, + { 19, 20, -1, 0}, { -1, -1, 7, 9}, + { 21, 22, -1, 0}, { -1, -1, 8, 10}, + { 23, -1, -1, 0}, { -1, -1, 9, 11}, +}; + +static const struct tree_node treeUVDC[] = { + { 1, 4, -1, 0}, { 2, 3, -1, 0}, + { -1, -1, 2, 0}, { -1, -1, 2, 1}, + { 5, 6, -1, 0}, { -1, -1, 2, 2}, + { 7, 8, -1, 0}, { -1, -1, 3, 3}, + { 9, 10, -1, 0}, { -1, -1, 4, 4}, + { 11, 12, -1, 0}, { -1, -1, 5, 5}, + { 13, 14, -1, 0}, { -1, -1, 6, 6}, + { 15, 16, -1, 0}, { -1, -1, 7, 7}, + { 17, 18, -1, 0}, { -1, -1, 8, 8}, + { 19, 20, -1, 0}, { -1, -1, 9, 9}, + { 21, 22, -1, 0}, { -1, -1, 10, 10}, + { 23, -1, -1, 0}, { -1, -1, 11, 11}, +}; + +/****************************************************************************** + * Huffman Decoder + ******************************************************************************/ + +/* Note: There is no penalty for passing the tree as an argument, since dummy + * args are passed anyway (to maintain 16-byte stack alignment), and since the + * address is loaded into a register either way. */ + +/* If no node is found, coeffbits and skip will not be modified */ +/* Return: Depth of node found, or -1 if invalid input code */ +static int +getNodeAC(unsigned int in, signed char *coeffbits, signed char *skip, + const struct tree_node *tree) +{ + int node = 0; + int i = 0; + int depth; + + do { + if ((in & 0x80000000) == 0) + node = tree[node].left; + else + node = tree[node].right; + + if (node == -1) + break; + + depth = tree[node].depth; + + /* Is it a leaf? If not, branch downward */ + if (depth != -1) { + *coeffbits = tree[node].coeffbits; + *skip = tree[node].skip; + return depth; + } + + in <<= 1; + ++i; + } while (i <= 15); + + return -1; +} + +/* If no node is found, coeffbits will not be modified */ +/* Return: Depth of node found, or -1 if invalid input code */ +static int +getNodeDC(unsigned int in, signed char *coeffbits, const struct tree_node *tree) +{ + int node = 0; + int i = 0; + int depth; + + do { + if ((in & 0x80000000) == 0) + node = tree[node].left; + else + node = tree[node].right; + + if (node == -1) + break; + + depth = tree[node].depth; + + /* Is it a leaf? If not, branch downward */ + if (depth != -1) { + *coeffbits = tree[node].coeffbits; + return depth; + } + + in <<= 1; + ++i; + } while (i <= 15); + + return -1; +} + +static inline unsigned int +getBytes(int *rawData, struct comp_info *cinfo) +{ + int bufLen = cinfo->rawLen; + int bits = cinfo->bits; + int bytes = cinfo->bytes; + unsigned char *in = bytes + (unsigned char *) rawData; + unsigned char b1, b2, b3, b4, b5; + unsigned int packedIn; + + /* Pull 5 bytes out of raw data */ + if (bytes < bufLen - 4) { + b1 = in[0]; + b2 = in[1]; + b3 = in[2]; + b4 = in[3]; + b5 = in[4]; + } else { + if (bytes < bufLen - 3) { + b1 = in[0]; + b2 = in[1]; + b3 = in[2]; + b4 = in[3]; + } else { + if (bytes < bufLen - 2) { + b1 = in[0]; + b2 = in[1]; + b3 = in[2]; + } else { + if (bytes < bufLen - 1) { + b1 = in[0]; + b2 = in[1]; + } else { + if (bytes <= bufLen) { + b1 = in[0]; + } else { + b1 = 0; + } + b2 = 0; + } + b3 = 0; + } + b4 = 0; + } + b5 = 0; + } + + /* Pack the bytes */ + packedIn = b1 << 24; + packedIn += b2 << 16; + packedIn += b3 << 8; + packedIn += b4; + + if (bits != 0) { + packedIn = packedIn << bits; + packedIn += b5 >> (8 - bits); + } + + return packedIn; +} + +static int +getACCoefficient(int *rawData, int *coeff, struct comp_info *cinfo, + const struct tree_node *tree) +{ + int input, bits, bytes, tmp_c; + signed char coeffbits = 0; + signed char skip = 0; + + input = getBytes(rawData, cinfo); + bits = getNodeAC(input, &coeffbits, &skip, tree); + + if (coeffbits) { + input = input << (bits - 1); + input &= 0x7fffffff; + if (! (input & 0x40000000)) + input |= 0x80000000; + + tmp_c = input >> (31 - coeffbits); + if (tmp_c < 0) + tmp_c++; + *coeff = tmp_c; + + bits += coeffbits; + } + + bytes = (bits + cinfo->bits) >> 3; + cinfo->bytes += bytes; + cinfo->bits += bits - (bytes << 3); + + return skip; +} + +static void +getDCCoefficient(int *rawData, int *coeff, struct comp_info *cinfo, + const struct tree_node *tree) +{ + int input, bits, bytes, tmp_c; + signed char coeffbits = 0; + + input = getBytes(rawData, cinfo); + bits = getNodeDC(input, &coeffbits, tree); + + if (bits == -1) { + bits = 1; /* Try to re-sync at the next bit */ + *coeff = 0; /* Indicates no change from last DC */ + } else { + + input = input << (bits - 1); + input &= 0x7fffffff; + if (! (input & 0x40000000)) + input |= 0x80000000; + + tmp_c = input >> (31 - coeffbits); + if (tmp_c < 0) + tmp_c++; + *coeff = tmp_c; + + bits += coeffbits; + } + + bytes = (bits + cinfo->bits) >> 3; + cinfo->bytes += bytes; + cinfo->bits += bits - (bytes << 3); +} + +/* For AC coefficients, here is what the "skip" value means: + * -1: Either the 8x4 block has ended, or the decoding failed. + * 0: Use the returned coeff. Don't skip anything. + * 1-15: The next <skip> coeffs are zero. The returned coeff is used. + * 16: The next 16 coeffs are zero. The returned coeff is ignored. + * + * You must ensure that the C[] array not be overrun, or stack corruption will + * result. + */ +static void +huffmanDecoderY(int *C, int *pIn, struct comp_info *cinfo) +{ + int coeff = 0; + int i = 1; + int k, skip; + + getDCCoefficient(pIn, C, cinfo, treeYDC); + + i = 1; + do { + skip = getACCoefficient(pIn, &coeff, cinfo, treeYAC); + + if (skip == -1) { + break; + } else if (skip == 0) { + C[i++] = coeff; + } else if (skip == 16) { + k = 16; + if (i > 16) + k = 32 - i; + + while (k--) + C[i++] = 0; + } else { + k = skip; + if (skip > 31 - i) + k = 31 - i; + + while (k--) + C[i++] = 0; + + C[i++] = coeff; + } + } while (i <= 31); + + if (skip == -1) + while (i <= 31) C[i++] = 0; + else + getACCoefficient(pIn, &coeff, cinfo, treeYAC); +} + +/* Same as huffmanDecoderY, except for the tables used */ +static void +huffmanDecoderUV(int *C, int *pIn, struct comp_info *cinfo) +{ + int coeff = 0; + int i = 1; + int k, skip; + + getDCCoefficient(pIn, C, cinfo, treeUVDC); + + i = 1; + do { + skip = getACCoefficient(pIn, &coeff, cinfo, treeUVAC); + + if (skip == -1) { + break; + } else if (skip == 0) { + C[i++] = coeff; + } else if (skip == 16) { + k = 16; + if (i > 16) + k = 32 - i; + + while (k--) + C[i++] = 0; + } else { + k = skip; + if (skip > 31 - i) + k = 31 - i; + + while (k--) + C[i++] = 0; + + C[i++] = coeff; + } + } while (i <= 31); + + if (skip == -1) + while (i <= 31) C[i++] = 0; + else + getACCoefficient(pIn, &coeff, cinfo, treeUVAC); +} + +/****************************************************************************** + * iDCT Functions + ******************************************************************************/ + +#ifndef APPROXIMATE_MUL_BY_SHIFT + +#define IDCT_MESSAGE "iDCT with multiply" + +#define TIMES_16382(u) ((u)? 16382 * (u):0) +#define TIMES_23168(u) ((u)? 23168 * (u):0) +#define TIMES_30270(u) ((u)? 30270 * (u):0) +#define TIMES_41986(u) ((u)? 41986 * (u):0) +#define TIMES_35594(u) ((u)? 35594 * (u):0) +#define TIMES_23783(u) ((u)? 23783 * (u):0) +#define TIMES_8351(u) ((u)? 8351 * (u):0) +#define TIMES_17391(u) ((u)? 17391 * (u):0) +#define TIMES_14743(u) ((u)? 14743 * (u):0) +#define TIMES_9851(u) ((u)? 9851 * (u):0) +#define TIMES_3459(u) ((u)? 3459 * (u):0) +#define TIMES_32134(u) ((u)? 32134 * (u):0) +#define TIMES_27242(u) ((u)? 27242 * (u):0) +#define TIMES_18202(u) ((u)? 18202 * (u):0) +#define TIMES_6392(u) ((u)? 6392 * (u):0) +#define TIMES_39550(u) ((u)? 39550 * (u):0) +#define TIMES_6785(u) ((u)? 6785 * (u):0) +#define TIMES_12538(u) ((u)? 12538 * (u):0) + +#else + +#define IDCT_MESSAGE "iDCT with shift" + +#define TIMES_16382(u) ( (u)? x=(u) , (x<<14) - (x<<1) :0 ) +#define TIMES_23168(u) ( (u)? x=(u) , (x<<14) + (x<<12) + (x<<11) + (x<<9) :0 ) +#define TIMES_30270(u) ( (u)? x=(u) , (x<<15) - (x<<11) :0 ) +#define TIMES_41986(u) ( (u)? x=(u) , (x<<15) + (x<<13) + (x<<10) :0 ) +#define TIMES_35594(u) ( (u)? x=(u) , (x<<15) + (x<<11) + (x<<9) + (x<<8) :0 ) +#define TIMES_23783(u) ( (u)? x=(u) , (x<<14) + (x<<13) - (x<<9) - (x<<8) :0 ) +#define TIMES_8351(u) ( (u)? x=(u) , (x<<13) :0 ) +#define TIMES_17391(u) ( (u)? x=(u) , (x<<14) + (x<<10) :0 ) +#define TIMES_14743(u) ( (u)? x=(u) , (x<<14) - (x<<10) - (x<<9) :0 ) +#define TIMES_9851(u) ( (u)? x=(u) , (x<<13) + (x<<10) + (x<<9) :0 ) +#define TIMES_3459(u) ( (u)? x=(u) , (x<<12) - (x<<9) :0 ) +#define TIMES_32134(u) ( (u)? x=(u) , (x<<15) - (x<<9) :0 ) +#define TIMES_27242(u) ( (u)? x=(u) , (x<<14) + (x<<13) + (x<<11) + (x<<9) :0 ) +#define TIMES_18202(u) ( (u)? x=(u) , (x<<14) + (x<<11) - (x<<8) :0 ) +#define TIMES_6392(u) ( (u)? x=(u) , (x<<13) - (x<<11) + (x<<8) :0 ) +#define TIMES_39550(u) ( (u)? x=(u) , (x<<15) + (x<<12) + (x<<11) + (x<<9) :0 ) +#define TIMES_6785(u) ( (u)? x=(u) , (x<<12) + (x<<11) + (x<<9) :0 ) +#define TIMES_12538(u) ( (u)? x=(u) , (x<<13) + (x<<12) + (x<<8) :0 ) + +/* + * The variables C0, C4, C16 and C20 can also be removed from the algorithm + * if APPROXIMATE_MUL_BY_SHIFTS is defined. They store correction values + * and can be considered insignificant. + */ + +#endif + +static void +DCT_8x4(int *coeff, unsigned char *out) +/* pre: coeff == coefficients + post: coeff != coefficients + ** DO NOT ASSUME coeff TO BE THE SAME BEFORE AND AFTER CALLING THIS FUNCTION! +*/ +{ + register int base,val1,val2,val3; + int tmp1,tmp2; + int C0,C4,C16,C20; + int C2_18,C6_22,C1_17,C3_19,C5_21,C7_23; + register int t; +#ifdef APPROXIMATE_MUL_BY_SHIFT + register int x; +#endif + + C0=coeff[0]; + C4=coeff[4]; + C16=coeff[16]; + C20=coeff[20]; + + coeff[0]=TIMES_23168(coeff[0]); + coeff[4]=TIMES_23168(coeff[4]); + coeff[16]=TIMES_23168(coeff[16]); + coeff[20]=TIMES_23168(coeff[20]); + + C2_18 = coeff[2]+coeff[18]; + C6_22 = coeff[6]+coeff[22]; + C1_17 = coeff[1]+coeff[17]; + C3_19 = coeff[3]+coeff[19]; + C5_21 = coeff[5]+coeff[21]; + C7_23 = coeff[7]+coeff[23]; + +// 0,7,25,32 + + base = 0x1000000; + base += coeff[0]+coeff[4]+coeff[16]+coeff[20]; + base += TIMES_30270(C2_18); + base += TIMES_12538(C6_22); + + val1 = TIMES_41986(coeff[9]); + val1 += TIMES_35594(coeff[11]); + val1 += TIMES_23783(coeff[13]); + val1 += TIMES_8351(coeff[15]); + val1 += TIMES_17391(coeff[25]); + val1 += TIMES_14743(coeff[27]); + val1 += TIMES_9851(coeff[29]); + val1 += TIMES_3459(coeff[31]); + + val2 = TIMES_32134(C1_17); + val2 += TIMES_27242(C3_19); + val2 += TIMES_18202(C5_21); + val2 += TIMES_6392(C7_23); + + val3 = TIMES_39550(coeff[10]); + val3 += TIMES_16382(coeff[14]+coeff[26]); + val3 += TIMES_6785(coeff[30]); + val3 += TIMES_30270(coeff[8]+coeff[12]); + val3 += TIMES_12538(coeff[24]+coeff[28]); + + t=(base + val1 + val2 + val3) >> 17; + out[0]= t&0xFFFFFF00? t<0?0:255 : (unsigned char)t; + t=(base - val1 - val2 + val3 - C4 - C20) >> 17; + out[7]= t&0xFFFFFF00? t<0?0:255 : (unsigned char)t; + t=(base - val1 + val2 - val3 - C16- C20) >> 17; + out[24]= t&0xFFFFFF00? t<0?0:255 : (unsigned char)t; + t=(base + val1 - val2 - val3 - C4 - C16 - C20) >> 17; + out[31]= t&0xFFFFFF00? t<0?0:255 : (unsigned char)t; + +//1,6,25,30 + + base = 0x1000000; + base += coeff[0]-coeff[4]+coeff[16]-coeff[20]; + base += TIMES_12538(C2_18); + base -= TIMES_30270(C6_22); + + val1 = TIMES_35594(coeff[9]); + val1 -= TIMES_8351(coeff[11]); + val1 -= TIMES_41986(coeff[13]); + val1 -= TIMES_23783(coeff[15]); + val1 -= TIMES_14743(coeff[25]); + val1 -= TIMES_3459(coeff[27]); + val1 -= TIMES_17391(coeff[29]); + val1 -= TIMES_9851(coeff[31]); + + val2 = TIMES_27242(C1_17); + val2 -= TIMES_6392(C3_19); + val2 -= TIMES_32134(C5_21); + val2 -= TIMES_18202(C7_23); + + val3 = TIMES_16382(coeff[10]-coeff[30]); + val3 -= TIMES_39550(coeff[14]); + val3 += TIMES_6785(coeff[26]); + val3 += TIMES_12538(coeff[24]-coeff[28]); + val3 += TIMES_30270(coeff[8]-coeff[12]); + + t=(base + val1 + val2 + val3 + C4 + C20) >> 17; + out[1]= t&0xFFFFFF00? t<0?0:255 : (unsigned char)t; + t=(base - val1 - val2 + val3) >> 17; + out[6]= t&0xFFFFFF00? t<0?0:255 : (unsigned char)t; + t=(base - val1 + val2 - val3 + C4 - C16 + C20) >> 17; + out[25]= t&0xFFFFFF00? t<0?0:255 : (unsigned char)t; + t=(base + val1 - val2 - val3 + C20) >> 17; + out[30]= t&0xFFFFFF00? t<0?0:255 : (unsigned char)t; + +//2,5,26,29 + + base = 0x1000000; + base += coeff[0] - coeff[4] + coeff[16] - coeff[20]; + base -= TIMES_12538(C2_18); + base += TIMES_30270(C6_22); + + val1 = TIMES_23783(coeff[9]); + val1 -= TIMES_41986(coeff[11]); + val1 += TIMES_8351(coeff[13]); + val1 += TIMES_35594(coeff[15]); + val1 += TIMES_9851(coeff[25]); + val1 -= TIMES_17391(coeff[27]); + val1 += TIMES_3459(coeff[29]); + val1 += TIMES_14743(coeff[31]); + + val2 = TIMES_18202(C1_17); + val2 -= TIMES_32134(C3_19); + val2 += TIMES_6392(C5_21); + val2 += TIMES_27242(C7_23); + + val3 = -TIMES_16382(coeff[10] - coeff[30]); + val3 += TIMES_39550(coeff[14]); + val3 -= TIMES_6785(coeff[26]); + val3 += TIMES_12538(coeff[24] - coeff[28]); + val3 += TIMES_30270(coeff[8] - coeff[12]); + + t=(base + val1 + val2 + val3) >> 17; + out[2]= t&0xFFFFFF00? t<0?0:255 : (unsigned char)t; + t=(base - val1 - val2 + val3) >> 17; + out[5]= t&0xFFFFFF00? t<0?0:255 : (unsigned char)t; + t=(base - val1 + val2 - val3 - C16) >> 17; + out[26]= t&0xFFFFFF00? t<0?0:255 : (unsigned char)t; + t=(base + val1 - val2 - val3 + C4 - C16 + C20) >> 17; + out[29]= t&0xFFFFFF00? t<0?0:255 : (unsigned char)t; + +//3,4,27,28 + + base = 0x1000000; + base += coeff[0] + coeff[4] + coeff[16] + coeff[20]; + base -= TIMES_30270(C2_18); + base -= TIMES_12538(C6_22); + + val1 = TIMES_8351(coeff[9]); + val1 -= TIMES_23783(coeff[11]); + val1 += TIMES_35594(coeff[13]); + val1 += TIMES_3459(coeff[25]); + val1 -= TIMES_9851(coeff[27]); + val1 += TIMES_14743(coeff[29]); + + val2 = TIMES_6392(C1_17); + val2 -= TIMES_18202(C3_19); + val2 += TIMES_27242(C5_21); + + val3 = -TIMES_39550(coeff[10]); + val3 += TIMES_16382(coeff[14] + coeff[26]); + val3 -= TIMES_6785(coeff[30]); + val3 += TIMES_30270(coeff[8] + coeff[12]); + val3 += TIMES_12538(coeff[24] + coeff[28]); + + tmp1 = TIMES_32134(C7_23); + tmp2 = TIMES_41986(coeff[15]) + TIMES_17391(coeff[31]); + + t=(base + val1 + val2 + val3 - tmp1 - tmp2 - C4 - C20) >> 17; + out[3]= t&0xFFFFFF00? t<0?0:255 : (unsigned char)t; + t=(base - val1 - val2 + val3) >> 17; + out[4]= t&0xFFFFFF00? t<0?0:255 : (unsigned char)t; + t=(base - val1 + val2 - val3 - tmp1 + tmp2) >> 17; + out[27]= t&0xFFFFFF00? t<0?0:255 : (unsigned char)t; + t=(base + val1 - val2 - val3 - C16 - C20) >> 17; + out[28]= t&0xFFFFFF00? t<0?0:255 : (unsigned char)t; + +// Second half + C2_18 = coeff[2] - coeff[18]; + C6_22 = coeff[6] - coeff[22]; + C1_17 = coeff[1] - coeff[17]; + C3_19 = coeff[3] - coeff[19]; + C5_21 = coeff[5] - coeff[21]; + C7_23 = coeff[7] - coeff[23]; + +// 8,15,16,23 + + base = 0x1000000; + base += coeff[0] + coeff[4] - coeff[16] - coeff[20]; + base +=TIMES_30270(C2_18); + base +=TIMES_12538(C6_22); + + val1 = TIMES_17391(coeff[9]); + val1 += TIMES_14743(coeff[11]); + val1 += TIMES_9851(coeff[13]); + val1 += TIMES_3459(coeff[15]); + val1 -= TIMES_41986(coeff[25]); + val1 -= TIMES_35594(coeff[27]); + val1 -= TIMES_23783(coeff[29]); + val1 -= TIMES_8351(coeff[31]); + + val2 = TIMES_32134(C1_17); + val2 += TIMES_27242(C3_19); + val2 += TIMES_18202(C5_21); + val2 += TIMES_6392(C7_23); + + val3 = TIMES_16382(coeff[10] - coeff[30]); + val3 += TIMES_6785(coeff[14]); + val3 -= TIMES_39550(coeff[26]); + val3 -=TIMES_30270(coeff[24] + coeff[28]); + val3 +=TIMES_12538(coeff[8] + coeff[12]); + + t=(base + val1 + val2 + val3) >> 17; + out[8]= t&0xFFFFFF00? t<0?0:255 : (unsigned char)t; + t=(base - val1 - val2 + val3 - C4 + C16 + C20) >> 17; + out[15]= t&0xFFFFFF00? t<0?0:255 : (unsigned char)t; + t=(base - val1 + val2 - val3) >> 17; + out[16]= t&0xFFFFFF00? t<0?0:255 : (unsigned char)t; + t=(base + val1 - val2 - val3 - C4 + C20) >> 17; + out[23]= t&0xFFFFFF00? t<0?0:255 : (unsigned char)t; + +//9,14,17,22 + + base = 0x1000000; + base += coeff[0] - coeff[4] - coeff[16] + coeff[20]; + base += TIMES_12538(C2_18); + base -= TIMES_30270(C6_22); + + val1 = TIMES_14743(coeff[9]); + val1 -= TIMES_3459(coeff[11]); + val1 -= TIMES_17391(coeff[13]); + val1 -= TIMES_9851(coeff[15]); + val1 -= TIMES_35594(coeff[25]); + val1 += TIMES_8351(coeff[27]); + val1 += TIMES_41986(coeff[29]); + val1 += TIMES_23783(coeff[31]); + + val2 = TIMES_27242(C1_17); + val2 -= TIMES_6392(C3_19); + val2 -= TIMES_32134(C5_21); + val2 -= TIMES_18202(C7_23); + + val3 = TIMES_6785(coeff[10]); + val3 -= TIMES_16382(coeff[14] + coeff[26]); + val3 += TIMES_39550(coeff[30]); + val3 += TIMES_12538(coeff[8] - coeff[12]); + val3 -= TIMES_30270(coeff[24] - coeff[28]); + + t=(base + val1 + val2 + val3 + C4 + C16 - C20) >> 17; + out[9]= t&0xFFFFFF00? t<0?0:255 : (unsigned char)t; + t=(base - val1 - val2 + val3 + C16) >> 17; + out[14]= t&0xFFFFFF00? t<0?0:255 : (unsigned char)t; + t=(base - val1 + val2 - val3 + C4) >> 17; + out[17]= t&0xFFFFFF00? t<0?0:255 : (unsigned char)t; + t=(base + val1 - val2 - val3) >> 17; + out[22]= t&0xFFFFFF00? t<0?0:255 : (unsigned char)t; + +//10,13,18,21 + + base = 0x1000000; + base += coeff[0] - coeff[4] - coeff[16] + coeff[20]; + base -= TIMES_12538(C2_18); + base += TIMES_30270(C6_22); + + val1 = TIMES_9851(coeff[9]); + val1 -= TIMES_17391(coeff[11]); + val1 += TIMES_3459(coeff[13]); + val1 += TIMES_14743(coeff[15]); + val1 -= TIMES_23783(coeff[25]); + val1 += TIMES_41986(coeff[27]); + val1 -= TIMES_8351(coeff[29]); + val1 -= TIMES_35594(coeff[31]); + + val2 = TIMES_18202(C1_17); + val2 -= TIMES_32134(C3_19); + val2 += TIMES_6392(C5_21); + val2 += TIMES_27242(C7_23); + + val3 = -TIMES_6785(coeff[10]); + val3 += TIMES_16382(coeff[14]+coeff[26]); + val3 -= TIMES_39550(coeff[30]); + val3 += TIMES_12538(coeff[8]-coeff[12]); + val3 -= TIMES_30270(coeff[24]-coeff[28]); + + t=(base + val1 + val2 + val3) >> 17; + out[10]= t&0xFFFFFF00? t<0?0:255 : (unsigned char)t; + t=(base - val1 - val2 + val3 + C4 + C16 - C20) >> 17; + out[13]= t&0xFFFFFF00? t<0?0:255 : (unsigned char)t; + t=(base - val1 + val2 - val3) >> 17; + out[18]= t&0xFFFFFF00? t<0?0:255 : (unsigned char)t; + t=(base + val1 - val2 - val3 + C4) >> 17; + out[21]= t&0xFFFFFF00? t<0?0:255 : (unsigned char)t; + +// 11,12,19,20 + + base = 0x1000000; + base += coeff[0]+coeff[4]-coeff[16]-coeff[20]; + base -= TIMES_30270(C2_18); + base -= TIMES_12538(C6_22); + + val1 = TIMES_3459(coeff[9]); + val1 -= TIMES_9851(coeff[11]); + val1 += TIMES_14743(coeff[13]); + val1 -= TIMES_8351(coeff[25]); + val1 += TIMES_23783(coeff[27]); + val1 -= TIMES_35594(coeff[29]); + + val2 = TIMES_6392(C1_17); + val2 -= TIMES_18202(C3_19); + val2 += TIMES_27242(C5_21); + + val3 = -TIMES_16382(coeff[10] - coeff[30]); + val3 -= TIMES_6785(coeff[14]); + val3 += TIMES_39550(coeff[26]); + val3 -= TIMES_30270(coeff[24]+coeff[28]); + val3 += TIMES_12538(coeff[8]+coeff[12]); + + tmp1 = TIMES_32134(C7_23); + tmp2 = -TIMES_17391(coeff[15]) + TIMES_41986(coeff[31]); + + t=(base + val1 + val2 + val3 - tmp1 + tmp2 + C16 + C20) >> 17; + out[11]= t&0xFFFFFF00? t<0?0:255 : (unsigned char)t; + t=(base - val1 - val2 + val3 + C16 + C20) >> 17; + out[12]= t&0xFFFFFF00? t<0?0:255 : (unsigned char)t; + t=(base - val1 + val2 - val3 - tmp1 - tmp2 - C4 + C20) >> 17; + out[19]= t&0xFFFFFF00? t<0?0:255 : (unsigned char)t; + t=(base + val1 - val2 - val3) >> 17; + out[20]= t&0xFFFFFF00? t<0?0:255 : (unsigned char)t; +} + +#undef TIMES_16382 +#undef TIMES_23168 +#undef TIMES_30270 +#undef TIMES_41986 +#undef TIMES_35594 +#undef TIMES_23783 +#undef TIMES_8351 +#undef TIMES_17391 +#undef TIMES_14743 +#undef TIMES_9851 +#undef TIMES_3459 +#undef TIMES_32134 +#undef TIMES_27242 +#undef TIMES_18202 +#undef TIMES_6392 +#undef TIMES_39550 +#undef TIMES_6785 +#undef TIMES_12538 + +/****************************************************************************** + * Main Decoder Functions + ******************************************************************************/ + +/* This function handles the decompression of a single 8x4 block. It is + * independent of the palette (YUV422, YUV420, YUV400, GBR422...). cinfo->bytes + * determines the positin in the input buffer. + */ +static int +decompress8x4(unsigned char *pOut, + unsigned char *pIn, + int *lastDC, + int uvFlag, + struct comp_info *cinfo) +{ + int i, x, y, dc; + int coeffs[32]; + int deZigZag[32]; + int *dest; + int *src; + unsigned char *qt = cinfo->qt; + + if (! uvFlag) { + huffmanDecoderY(coeffs, (int*) pIn, cinfo); + + /* iDPCM and dequantize first coefficient */ + dc = (*lastDC) + coeffs[0]; + coeffs[0] = dc * (qt[0] + 1); + *lastDC = dc; + + /* ...and the second coefficient */ + coeffs[1] = ((qt[1] + 1) * coeffs[1]) >> 1; + + /* Dequantize, starting at 3rd element */ + for (i = 2; i < 32; i++) + coeffs[i] = (qt[i] + 1) * coeffs[i]; + } else { + huffmanDecoderUV(coeffs, (int*) pIn, cinfo); + + /* iDPCM */ + dc = (*lastDC) + coeffs[0]; + coeffs[0] = dc; + *lastDC = dc; + + /* Dequantize */ + for (i = 0; i < 32; i++) + coeffs[i] = (qt[32 + i] + 1) * coeffs[i]; + } + + /* Dezigzag */ + for (i = 0; i < 32; i++) + deZigZag[i] = coeffs[ZigZag518[i]]; + + /* Transpose the dezigzagged coefficient matrix */ + src = deZigZag; + dest = coeffs; + for (y = 0; y <= 3; ++y) { + for (x = 0; x <= 7; ++x) { + dest[x] = src[x * 4]; + } + src += 1; + dest += 8; + } + + /* Do the inverse DCT transform */ + DCT_8x4(coeffs, pOut); + + return 0; /* Always returns 0 */ +} + +static inline void +copyBlock(unsigned char *src, unsigned char *dest, int destInc) +{ + int i; + unsigned int *pSrc, *pDest; + + for (i = 0; i <= 3; i++) { + pSrc = (unsigned int *) src; + pDest = (unsigned int *) dest; + pDest[0] = pSrc[0]; + pDest[1] = pSrc[1]; + src += 8; + dest += destInc; + } +} + +#if 0 +static inline int +decompress400NoMMXOV518(unsigned char *pIn, + unsigned char *pOut, + unsigned char *pTmp, + const int w, + const int h, + const int numpix, + struct comp_info *cinfo) +{ + int iOutY, x, y; + int lastYDC = 0; + + /* Start Y loop */ + y = 0; + do { + iOutY = w * y; + x = 0; + do { + decompress8x4(pTmp, pIn, &lastYDC, 0, cinfo); + copyBlock(pTmp, pOut + iOutY, w); + iOutY += 8; + x += 8; + } while (x < w); + y += 4; + } while (y < h); + + /* Did we decode too much? */ + if (cinfo->bytes > cinfo->rawLen + 897) + return 1; + + /* Did we decode enough? */ + if (cinfo->bytes >= cinfo->rawLen - 897) + return 0; + else + return 1; +} +#endif + +static inline int +decompress420NoMMXOV518(unsigned char *pIn, + unsigned char *pOut, + unsigned char *pTmp, + const int w, + const int h, + const int numpix, + struct comp_info *cinfo, + int yvu) +{ + unsigned char *pOutU, *pOutV; + int iOutY, iOutU, iOutV, x, y; + int lastYDC = 0; + int lastUDC = 0; + int lastVDC = 0; + + if (yvu) { + pOutV = pOut + numpix; + pOutU = pOutV + numpix / 4; + } else { + pOutU = pOut + numpix; + pOutV = pOutU + numpix / 4; + } + + /* Start Y loop */ + y = 0; + do { + iOutY = w * y; + iOutV = iOutU = iOutY / 4; + + x = 0; + do { + decompress8x4(pTmp, pIn, &lastYDC, 0, cinfo); + copyBlock(pTmp, pOut + iOutY, w); + iOutY += 8; + x += 8; + } while (x < w); + + + + iOutY = w * (y + 4); + x = 0; + do { + decompress8x4(pTmp, pIn, &lastUDC, 1, cinfo); + copyBlock(pTmp, pOutU + iOutU, w/2); + iOutU += 8; + + decompress8x4(pTmp, pIn, &lastVDC, 1, cinfo); + copyBlock(pTmp, pOutV + iOutV, w/2); + iOutV += 8; + + decompress8x4(pTmp, pIn, &lastYDC, 0, cinfo); + copyBlock(pTmp, pOut + iOutY, w); + iOutY += 8; + + decompress8x4(pTmp, pIn, &lastYDC, 0, cinfo); + copyBlock(pTmp, pOut + iOutY, w); + iOutY += 8; + + x += 16; + } while (x < w); + + y += 8; + } while (y < h); + + /* Did we decode too much? */ + if (cinfo->bytes > cinfo->rawLen + 897) + return 1; + + /* Did we decode enough? */ + if (cinfo->bytes >= cinfo->rawLen - (897 + 64)) + return 0; + else + return 1; +} + +/* Get quantization tables from input + * Returns: <0 if error, or >=0 otherwise */ +static int +get_qt_dynamic(unsigned char *pIn, struct comp_info *cinfo) +{ + int rawLen = cinfo->rawLen; + + /* Make sure input is actually big enough to hold trailer */ + if (rawLen < 72) { + return -1; + } + + cinfo->qt = pIn + rawLen - 64; + + return 0; +} + +/* Remove all 0 blocks from input */ +static void remove0blocks(unsigned char *pIn, int *inSize) +{ + long long *in = (long long *)pIn; + long long *out = (long long *)pIn; + int i; + + for (i = 0; i < *inSize; i += 8, in++) + /* Skip 8 byte blocks of all 0 */ + if (*in) + *out++ = *in; + + *inSize -= (in - out) * 8; +} + +#if 0 /* not used */ +/* Input format is raw isoc. data (with intact SOF header, packet numbers + * stripped, and all-zero blocks removed). + * Output format is planar YUV400 + * Returns uncompressed data length if success, or zero if error + */ +static int +Decompress400(unsigned char *pIn, + unsigned char *pOut, + int w, + int h, + int inSize) +{ + struct comp_info cinfo; + int numpix = w * h; + unsigned char pTmp[32]; + + remove0blocks(pIn, &inSize); + + cinfo.bytes = 0; + cinfo.bits = 0; + cinfo.rawLen = inSize; + + if (get_qt_dynamic(pIn, &cinfo) < 0) + return 0; + + /* Decompress, skipping the 8-byte SOF header */ + if (decompress400NoMMXOV518(pIn + 8, pOut, pTmp, w, h, numpix, &cinfo)) +// return 0; + ; /* Don't return error yet */ + + return (numpix); +} +#endif + +/* Input format is raw isoc. data (with intact SOF header, packet numbers + * stripped, and all-zero blocks removed). + * Output format is planar YUV420 + * Returns uncompressed data length if success, or zero if error + */ +static int v4lconvert_ov518_to_yuv420(unsigned char *src, unsigned char *dst, + int w, int h, int yvu, int inSize) +{ + struct comp_info cinfo; + int numpix = w * h; + unsigned char pTmp[32]; + + remove0blocks(src, &inSize); + + cinfo.bytes = 0; + cinfo.bits = 0; + cinfo.rawLen = inSize; + + if (get_qt_dynamic(src, &cinfo) < 0) + return -1; + + /* Decompress, skipping the 8-byte SOF header */ + if (decompress420NoMMXOV518(src + 8, dst, pTmp, w, h, numpix, &cinfo, yvu)) + return -1; + + return 0; +} + +int main(int argc, char *argv[]) +{ + int width, height, yvu, src_size, dest_size; + unsigned char src_buf[200000]; + unsigned char dest_buf[500000]; + + while (1) { + if (v4lconvert_helper_read(STDIN_FILENO, &width, sizeof(int), argv[0])) + return 1; /* Erm, no way to recover without loosing sync with libv4l */ + + if (v4lconvert_helper_read(STDIN_FILENO, &height, sizeof(int), argv[0])) + return 1; /* Erm, no way to recover without loosing sync with libv4l */ + + if (v4lconvert_helper_read(STDIN_FILENO, &yvu, sizeof(int), argv[0])) + return 1; /* Erm, no way to recover without loosing sync with libv4l */ + + if (v4lconvert_helper_read(STDIN_FILENO, &src_size, sizeof(int), argv[0])) + return 1; /* Erm, no way to recover without loosing sync with libv4l */ + + if (src_size > sizeof(src_buf)) { + fprintf(stderr, "%s: error: src_buf too small, need: %d\n", + argv[0], src_size); + return 2; + } + + if (v4lconvert_helper_read(STDIN_FILENO, src_buf, src_size, argv[0])) + return 1; /* Erm, no way to recover without loosing sync with libv4l */ + + + dest_size = width * height * 3 / 2; + if (dest_size > sizeof(dest_buf)) { + fprintf(stderr, "%s: error: dest_buf too small, need: %d\n", + argv[0], dest_size); + dest_size = -1; + } else if (v4lconvert_ov518_to_yuv420(src_buf, dest_buf, width, height, + yvu, src_size)) + dest_size = -1; + + if (v4lconvert_helper_write(STDOUT_FILENO, &dest_size, sizeof(int), + argv[0])) + return 1; /* Erm, no way to recover without loosing sync with libv4l */ + + if (dest_size == -1) + continue; + + if (v4lconvert_helper_write(STDOUT_FILENO, dest_buf, dest_size, argv[0])) + return 1; /* Erm, no way to recover without loosing sync with libv4l */ + } +} diff --git a/v4l2-apps/libv4l/libv4lconvert/processing/autogain.c b/v4l2-apps/libv4l/libv4lconvert/processing/autogain.c new file mode 100644 index 000000000..358264c68 --- /dev/null +++ b/v4l2-apps/libv4l/libv4lconvert/processing/autogain.c @@ -0,0 +1,140 @@ +/* +# (C) 2008-2009 Hans de Goede <hdegoede@redhat.com> + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include <errno.h> +#include <string.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +#include "libv4lprocessing.h" +#include "libv4lprocessing-priv.h" +#include "../libv4lconvert-priv.h" /* for PIX_FMT defines */ +#include "../libv4lsyscall-priv.h" + +static int autogain_active(struct v4lprocessing_data *data) { + return v4lcontrol_get_ctrl(data->control, V4LCONTROL_AUTOGAIN); +} + +/* auto gain and exposure algorithm based on the knee algorithm described here: + http://ytse.tricolour.net/docs/LowLightOptimization.html */ +static int autogain_calculate_lookup_tables( + struct v4lprocessing_data *data, + unsigned char *buf, const struct v4l2_format *fmt) +{ + int x, y, target, steps, avg_lum = 0; + int gain, exposure, orig_gain, orig_exposure; + struct v4l2_control ctrl; + struct v4l2_queryctrl gainctrl, expoctrl; + const int deadzone = 8; + + ctrl.id = V4L2_CID_EXPOSURE; + expoctrl.id = V4L2_CID_EXPOSURE; + if (SYS_IOCTL(data->fd, VIDIOC_QUERYCTRL, &expoctrl) || + SYS_IOCTL(data->fd, VIDIOC_G_CTRL, &ctrl)) + return 0; + exposure = orig_exposure = ctrl.value; + + ctrl.id = V4L2_CID_GAIN; + gainctrl.id = V4L2_CID_GAIN; + if (SYS_IOCTL(data->fd, VIDIOC_QUERYCTRL, &gainctrl) || + SYS_IOCTL(data->fd, VIDIOC_G_CTRL, &ctrl)) + return 0; + gain = orig_gain = ctrl.value; + + switch (fmt->fmt.pix.pixelformat) { + case V4L2_PIX_FMT_SGBRG8: + case V4L2_PIX_FMT_SGRBG8: + case V4L2_PIX_FMT_SBGGR8: + case V4L2_PIX_FMT_SRGGB8: + buf += fmt->fmt.pix.height * fmt->fmt.pix.bytesperline / 4 + + fmt->fmt.pix.width / 4; + + for (y = 0; y < fmt->fmt.pix.height / 2; y++) { + for (x = 0; x < fmt->fmt.pix.width / 2; x++) { + avg_lum += *buf++; + } + buf += fmt->fmt.pix.bytesperline - fmt->fmt.pix.width / 2; + } + avg_lum /= fmt->fmt.pix.height * fmt->fmt.pix.width / 4; + break; + + case V4L2_PIX_FMT_RGB24: + case V4L2_PIX_FMT_BGR24: + buf += fmt->fmt.pix.height * fmt->fmt.pix.bytesperline / 4 + + fmt->fmt.pix.width * 3 / 4; + + for (y = 0; y < fmt->fmt.pix.height / 2; y++) { + for (x = 0; x < fmt->fmt.pix.width / 2; x++) { + avg_lum += *buf++; + avg_lum += *buf++; + avg_lum += *buf++; + } + buf += fmt->fmt.pix.bytesperline - fmt->fmt.pix.width * 3 / 2; + } + avg_lum /= fmt->fmt.pix.height * fmt->fmt.pix.width * 3 / 4; + break; + } + + /* If we are off a multiple of deadzone, do multiple steps to reach the + desired lumination fast (with the risc of a slight overshoot) */ + target = v4lcontrol_get_ctrl(data->control, V4LCONTROL_AUTOGAIN_TARGET); + steps = abs(target - avg_lum) / deadzone; + + for (x = 0; x < steps; x++) { + if (avg_lum > target) { + if (exposure > expoctrl.default_value) + exposure--; + else if (gain > gainctrl.default_value) + gain--; + else if (exposure > expoctrl.minimum) + exposure--; + else if (gain > gainctrl.minimum) + gain--; + else + break; + } else { + if (gain < gainctrl.default_value) + gain++; + else if (exposure < expoctrl.default_value) + exposure++; + else if (gain < gainctrl.maximum) + gain++; + else if (exposure < expoctrl.maximum) + exposure++; + else + break; + } + } + + if (gain != orig_gain) { + ctrl.id = V4L2_CID_GAIN; + ctrl.value = gain; + SYS_IOCTL(data->fd, VIDIOC_S_CTRL, &ctrl); + } + if (exposure != orig_exposure) { + ctrl.id = V4L2_CID_EXPOSURE; + ctrl.value = exposure; + SYS_IOCTL(data->fd, VIDIOC_S_CTRL, &ctrl); + } + + return 0; +} + +struct v4lprocessing_filter autogain_filter = { + autogain_active, autogain_calculate_lookup_tables }; diff --git a/v4l2-apps/libv4l/libv4lconvert/processing/gamma.c b/v4l2-apps/libv4l/libv4lconvert/processing/gamma.c new file mode 100644 index 000000000..fcb5bb0cf --- /dev/null +++ b/v4l2-apps/libv4l/libv4lconvert/processing/gamma.c @@ -0,0 +1,57 @@ +/* +# (C) 2008-2009 Hans de Goede <hdegoede@redhat.com> + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include <math.h> +#include "libv4lprocessing.h" +#include "libv4lprocessing-priv.h" + +#define CLIP(color) (unsigned char)(((color)>0xff)?0xff:(((color)<0)?0:(color))) + +static int gamma_active(struct v4lprocessing_data *data) { + int gamma = v4lcontrol_get_ctrl(data->control, V4LCONTROL_GAMMA); + + return gamma && gamma != 1000; +} + +static int gamma_calculate_lookup_tables( + struct v4lprocessing_data *data, + unsigned char *buf, const struct v4l2_format *fmt) +{ + int i, x, gamma; + + gamma = v4lcontrol_get_ctrl(data->control, V4LCONTROL_GAMMA); + + if (gamma != data->last_gamma) { + for (i = 0; i < 256; i++) { + x = powf(i / 255.0, 1000.0 / gamma) * 255; + data->gamma_table[i] = CLIP(x); + } + data->last_gamma = gamma; + } + + for (i = 0; i < 256; i++) { + data->comp1[i] = data->gamma_table[data->comp1[i]]; + data->green[i] = data->gamma_table[data->green[i]]; + data->comp2[i] = data->gamma_table[data->comp2[i]]; + } + + return 1; +} + +struct v4lprocessing_filter gamma_filter = { + gamma_active, gamma_calculate_lookup_tables }; diff --git a/v4l2-apps/libv4l/libv4lconvert/processing/libv4lprocessing-priv.h b/v4l2-apps/libv4l/libv4lconvert/processing/libv4lprocessing-priv.h new file mode 100644 index 000000000..b73c73b53 --- /dev/null +++ b/v4l2-apps/libv4l/libv4lconvert/processing/libv4lprocessing-priv.h @@ -0,0 +1,60 @@ +/* +# (C) 2008-2009 Elmar Kleijn <elmar_kleijn@hotmail.com> +# (C) 2008-2009 Sjoerd Piepenbrink <need4weed@gmail.com> +# (C) 2008-2009 Hans de Goede <hdegoede@redhat.com> + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef __LIBV4LPROCESSING_PRIV_H +#define __LIBV4LPROCESSING_PRIV_H + +#include "../control/libv4lcontrol.h" +#include "../libv4lsyscall-priv.h" + +#define V4L2PROCESSING_UPDATE_RATE 10 + +struct v4lprocessing_data { + struct v4lcontrol_data *control; + int fd; + int do_process; + /* True if any of the lookup tables does not contain + linear 0-255 */ + int lookup_table_active; + /* Counts the number of processed frames until a + V4L2PROCESSING_UPDATE_RATE overflow happens */ + int lookup_table_update_counter; + /* RGB/BGR lookup tables */ + unsigned char comp1[256]; + unsigned char green[256]; + unsigned char comp2[256]; + /* Filter private data for filters which need it */ + int last_gamma; + unsigned char gamma_table[256]; +}; + +struct v4lprocessing_filter { + /* Returns 1 if the filter is active */ + int (*active)(struct v4lprocessing_data *data); + /* Returns 1 if any of the lookup tables was changed */ + int (*calculate_lookup_tables)(struct v4lprocessing_data *data, + unsigned char *buf, const struct v4l2_format *fmt); +}; + +extern struct v4lprocessing_filter whitebalance_filter; +extern struct v4lprocessing_filter autogain_filter; +extern struct v4lprocessing_filter gamma_filter; + +#endif diff --git a/v4l2-apps/libv4l/libv4lconvert/processing/libv4lprocessing.c b/v4l2-apps/libv4l/libv4lconvert/processing/libv4lprocessing.c new file mode 100644 index 000000000..cbbcca73c --- /dev/null +++ b/v4l2-apps/libv4l/libv4lconvert/processing/libv4lprocessing.c @@ -0,0 +1,181 @@ +/* +# (C) 2008-2009 Elmar Kleijn <elmar_kleijn@hotmail.com> +# (C) 2008-2009 Sjoerd Piepenbrink <need4weed@gmail.com> +# (C) 2008-2009 Hans de Goede <hdegoede@redhat.com> + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include <errno.h> +#include <string.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include "libv4lprocessing.h" +#include "libv4lprocessing-priv.h" +#include "../libv4lconvert-priv.h" /* for PIX_FMT defines */ + +static struct v4lprocessing_filter *filters[] = { + &whitebalance_filter, + &autogain_filter, + &gamma_filter, +}; + +struct v4lprocessing_data *v4lprocessing_create(int fd, struct v4lcontrol_data* control) +{ + struct v4lprocessing_data *data = + calloc(1, sizeof(struct v4lprocessing_data)); + + if (!data) + return NULL; + + data->fd = fd; + data->control = control; + + return data; +} + +void v4lprocessing_destroy(struct v4lprocessing_data *data) +{ + free(data); +} + +int v4lprocessing_pre_processing(struct v4lprocessing_data *data) +{ + int i; + + data->do_process = 0; + for (i = 0; i < ARRAY_SIZE(filters); i++) { + if (filters[i]->active(data)) + data->do_process = 1; + } + + return data->do_process; +} + +static void v4lprocessing_update_lookup_tables(struct v4lprocessing_data *data, + unsigned char *buf, const struct v4l2_format *fmt) +{ + int i; + + for (i = 0; i < 256; i++) { + data->comp1[i] = i; + data->green[i] = i; + data->comp2[i] = i; + } + + data->lookup_table_active = 0; + for (i = 0; i < ARRAY_SIZE(filters); i++) { + if (filters[i]->active(data)) { + if (filters[i]->calculate_lookup_tables(data, buf, fmt)) + data->lookup_table_active = 1; + } + } +} + +static void v4lprocessing_do_processing(struct v4lprocessing_data *data, + unsigned char *buf, const struct v4l2_format *fmt) +{ + int x, y; + + switch (fmt->fmt.pix.pixelformat) { + case V4L2_PIX_FMT_SGBRG8: + case V4L2_PIX_FMT_SGRBG8: /* Bayer patterns starting with green */ + for (y = 0; y < fmt->fmt.pix.height / 2; y++) { + for (x = 0; x < fmt->fmt.pix.width / 2; x++) { + *buf = data->green[*buf]; + buf++; + *buf = data->comp1[*buf]; + buf++; + } + buf += fmt->fmt.pix.bytesperline - fmt->fmt.pix.width; + for (x = 0; x < fmt->fmt.pix.width / 2; x++) { + *buf = data->comp2[*buf]; + buf++; + *buf = data->green[*buf]; + buf++; + } + buf += fmt->fmt.pix.bytesperline - fmt->fmt.pix.width; + } + break; + + case V4L2_PIX_FMT_SBGGR8: + case V4L2_PIX_FMT_SRGGB8: /* Bayer patterns *NOT* starting with green */ + for (y = 0; y < fmt->fmt.pix.height / 2; y++) { + for (x = 0; x < fmt->fmt.pix.width / 2; x++) { + *buf = data->comp1[*buf]; + buf++; + *buf = data->green[*buf]; + buf++; + } + buf += fmt->fmt.pix.bytesperline - fmt->fmt.pix.width; + for (x = 0; x < fmt->fmt.pix.width / 2; x++) { + *buf = data->green[*buf]; + buf++; + *buf = data->comp2[*buf]; + buf++; + } + buf += fmt->fmt.pix.bytesperline - fmt->fmt.pix.width; + } + break; + + case V4L2_PIX_FMT_RGB24: + case V4L2_PIX_FMT_BGR24: + for (y = 0; y < fmt->fmt.pix.height; y++) { + for (x = 0; x < fmt->fmt.pix.width; x++) { + *buf = data->comp1[*buf]; + buf++; + *buf = data->green[*buf]; + buf++; + *buf = data->comp2[*buf]; + buf++; + } + buf += fmt->fmt.pix.bytesperline - 3 * fmt->fmt.pix.width; + } + break; + } +} + +void v4lprocessing_processing(struct v4lprocessing_data *data, + unsigned char *buf, const struct v4l2_format *fmt) +{ + if (!data->do_process) + return; + + /* Do we support the current pixformat? */ + switch (fmt->fmt.pix.pixelformat) { + case V4L2_PIX_FMT_SGBRG8: + case V4L2_PIX_FMT_SGRBG8: + case V4L2_PIX_FMT_SBGGR8: + case V4L2_PIX_FMT_SRGGB8: + case V4L2_PIX_FMT_RGB24: + case V4L2_PIX_FMT_BGR24: + break; + default: + return; /* Non supported pix format */ + } + + if (v4lcontrol_controls_changed(data->control) || + data->lookup_table_update_counter == V4L2PROCESSING_UPDATE_RATE) { + v4lprocessing_update_lookup_tables(data, buf, fmt); + data->lookup_table_update_counter = 0; + } else + data->lookup_table_update_counter++; + + if (data->lookup_table_active) + v4lprocessing_do_processing(data, buf, fmt); + + data->do_process = 0; +} diff --git a/v4l2-apps/libv4l/libv4lconvert/processing/libv4lprocessing.h b/v4l2-apps/libv4l/libv4lconvert/processing/libv4lprocessing.h new file mode 100644 index 000000000..69d8865b2 --- /dev/null +++ b/v4l2-apps/libv4l/libv4lconvert/processing/libv4lprocessing.h @@ -0,0 +1,43 @@ +/* +# (C) 2008-2009 Elmar Kleijn <elmar_kleijn@hotmail.com> +# (C) 2008-2009 Sjoerd Piepenbrink <need4weed@gmail.com> +# (C) 2008-2009 Hans de Goede <hdegoede@redhat.com> + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 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 Lesser General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef __LIBV4LPROCESSING_H +#define __LIBV4LPROCESSING_H + +#include "../libv4lsyscall-priv.h" +#include <linux/videodev2.h> + +struct v4lprocessing_data; +struct v4lcontrol_data; + +struct v4lprocessing_data *v4lprocessing_create(int fd, struct v4lcontrol_data *data); +void v4lprocessing_destroy(struct v4lprocessing_data *data); + +/* Prepare to process 1 frame, returns 1 if processing is necesary, + return 0 if no processing will be done */ +int v4lprocessing_pre_processing(struct v4lprocessing_data *data); + +/* Do the actual processing, this is a nop if v4lprocessing_pre_processing() + returned 0, or if called more then 1 time after a single + v4lprocessing_pre_processing() call. */ +void v4lprocessing_processing(struct v4lprocessing_data *data, + unsigned char *buf, const struct v4l2_format *fmt); + +#endif diff --git a/v4l2-apps/libv4l/libv4lconvert/processing/whitebalance.c b/v4l2-apps/libv4l/libv4lconvert/processing/whitebalance.c new file mode 100644 index 000000000..64d20b3ff --- /dev/null +++ b/v4l2-apps/libv4l/libv4lconvert/processing/whitebalance.c @@ -0,0 +1,142 @@ +/* +# (C) 2008-2009 Elmar Kleijn <elmar_kleijn@hotmail.com> +# (C) 2008-2009 Sjoerd Piepenbrink <need4weed@gmail.com> +# (C) 2008-2009 Hans de Goede <hdegoede@redhat.com> + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include <errno.h> +#include <string.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include "libv4lprocessing.h" +#include "libv4lprocessing-priv.h" +#include "../libv4lconvert-priv.h" /* for PIX_FMT defines */ + +#define CLIP(color) (unsigned char)(((color)>0xff)?0xff:(((color)<0)?0:(color))) + +static int whitebalance_active(struct v4lprocessing_data *data) { + return v4lcontrol_get_ctrl(data->control, V4LCONTROL_WHITEBALANCE); +} + +static int whitebalance_calculate_lookup_tables_bayer( + struct v4lprocessing_data *data, unsigned char *buf, + const struct v4l2_format *fmt, int starts_with_green) +{ + int x, y, a1 = 0, a2 = 0, b1 = 0, b2 = 0; + int green_avg, comp1_avg, comp2_avg, avg_avg; + + for (y = 0; y < fmt->fmt.pix.height; y += 2) { + for (x = 0; x < fmt->fmt.pix.width; x += 2) { + a1 += *buf++; + a2 += *buf++; + } + buf += fmt->fmt.pix.bytesperline - fmt->fmt.pix.width; + for (x = 0; x < fmt->fmt.pix.width; x += 2) { + b1 += *buf++; + b2 += *buf++; + } + buf += fmt->fmt.pix.bytesperline - fmt->fmt.pix.width; + } + + if (starts_with_green) { + green_avg = (a1 + b2) / 512; + comp1_avg = a2 / 256; + comp2_avg = b1 / 256; + } else { + green_avg = (a2 + b1) / 512; + comp1_avg = a1 / 256; + comp2_avg = b2 / 256; + } + + x = fmt->fmt.pix.width * fmt->fmt.pix.height / 64; + if (abs(green_avg - comp1_avg) < x && + abs(green_avg - comp2_avg) < x && + abs(comp1_avg - comp2_avg) < x) + return 0; + + avg_avg = (green_avg + comp1_avg + comp2_avg) / 3; + + for (x = 0; x < 256; x++) { + data->comp1[x] = CLIP(data->comp1[x] * avg_avg / comp1_avg); + data->green[x] = CLIP(data->green[x] * avg_avg / green_avg); + data->comp2[x] = CLIP(data->comp2[x] * avg_avg / comp2_avg); + } + + return 1; +} + +static int whitebalance_calculate_lookup_tables_rgb( + struct v4lprocessing_data *data, unsigned char *buf, + const struct v4l2_format *fmt) +{ + int x, y, green_avg = 0, comp1_avg = 0, comp2_avg = 0, avg_avg; + + for (y = 0; y < fmt->fmt.pix.height; y++) { + for (x = 0; x < fmt->fmt.pix.width; x++) { + comp1_avg += *buf++; + green_avg += *buf++; + comp2_avg += *buf++; + } + buf += fmt->fmt.pix.bytesperline - fmt->fmt.pix.width * 3; + } + + x = fmt->fmt.pix.width * fmt->fmt.pix.height * 4; + if (abs(green_avg - comp1_avg) < x && + abs(green_avg - comp2_avg) < x && + abs(comp1_avg - comp2_avg) < x) + return 0; + + /* scale to avoid integer overflows */ + green_avg /= 256; + comp1_avg /= 256; + comp2_avg /= 256; + avg_avg = (green_avg + comp1_avg + comp2_avg) / 3; + + for (x = 0; x < 256; x++) { + data->comp1[x] = CLIP(data->comp1[x] * avg_avg / comp1_avg); + data->green[x] = CLIP(data->green[x] * avg_avg / green_avg); + data->comp2[x] = CLIP(data->comp2[x] * avg_avg / comp2_avg); + } + + return 1; +} + + +static int whitebalance_calculate_lookup_tables( + struct v4lprocessing_data *data, + unsigned char *buf, const struct v4l2_format *fmt) +{ + switch (fmt->fmt.pix.pixelformat) { + case V4L2_PIX_FMT_SGBRG8: + case V4L2_PIX_FMT_SGRBG8: /* Bayer patterns starting with green */ + return whitebalance_calculate_lookup_tables_bayer(data, buf, fmt, 1); + + case V4L2_PIX_FMT_SBGGR8: + case V4L2_PIX_FMT_SRGGB8: /* Bayer patterns *NOT* starting with green */ + return whitebalance_calculate_lookup_tables_bayer(data, buf, fmt, 0); + + case V4L2_PIX_FMT_RGB24: + case V4L2_PIX_FMT_BGR24: + return whitebalance_calculate_lookup_tables_rgb(data, buf, fmt); + } + + return 0; /* Should never happen */ +} + +struct v4lprocessing_filter whitebalance_filter = { + whitebalance_active, whitebalance_calculate_lookup_tables }; diff --git a/v4l2-apps/libv4l/libv4lconvert/rgbyuv.c b/v4l2-apps/libv4l/libv4lconvert/rgbyuv.c index 00706be9d..31cbc3c68 100644 --- a/v4l2-apps/libv4l/libv4lconvert/rgbyuv.c +++ b/v4l2-apps/libv4l/libv4lconvert/rgbyuv.c @@ -135,7 +135,7 @@ void v4lconvert_yuv420_to_bgr24(const unsigned char *src, unsigned char *dest, vsrc++; } /* Rewind u and v for next line */ - if (i&1) { + if (!(i&1)) { usrc -= width / 2; vsrc -= width / 2; } @@ -189,7 +189,7 @@ void v4lconvert_yuv420_to_rgb24(const unsigned char *src, unsigned char *dest, vsrc++; } /* Rewind u and v for next line */ - if (i&1) { + if (!(i&1)) { usrc -= width / 2; vsrc -= width / 2; } @@ -409,7 +409,6 @@ void v4lconvert_uyvy_to_yuv420(const unsigned char *src, unsigned char *dest, } /* copy the U and V values */ - src++; /* point to V */ src1 = src + width * 2; /* next line */ if (yvu) { vdest = dest; diff --git a/v4l2-apps/util/v4l2-ctl.cpp b/v4l2-apps/util/v4l2-ctl.cpp index dec9edd61..309e14312 100644 --- a/v4l2-apps/util/v4l2-ctl.cpp +++ b/v4l2-apps/util/v4l2-ctl.cpp @@ -131,6 +131,8 @@ enum Option { OptOverlay, OptGetJpegComp, OptSetJpegComp, + OptGetModulator, + OptSetModulator, OptListDevices, OptLast = 256 }; @@ -260,6 +262,8 @@ static struct option long_options[] = { {"set-crop-output-overlay", required_argument, 0, OptSetOutputOverlayCrop}, {"get-jpeg-comp", no_argument, 0, OptGetJpegComp}, {"set-jpeg-comp", required_argument, 0, OptSetJpegComp}, + {"get-modulator", no_argument, 0, OptGetModulator}, + {"set-modulator", required_argument, 0, OptSetModulator}, {"overlay", required_argument, 0, OptOverlay}, {"list-devices", no_argument, 0, OptListDevices}, {0, 0, 0, 0} @@ -404,6 +408,15 @@ static void usage(void) " display audio outputs [VIDIOC_ENUMAUDOUT]\n" " --list-audio-inputs\n" " display audio inputs [VIDIOC_ENUMAUDIO]\n" + " --get-modulator query the modulator settings [VIDIOC_G_MODULATOR]\n" + " --set-modulator=<txsubchans>\n" + " set the sub-carrier modulation [VIDIOC_S_MODULATOR]\n" + " <txsubchans> is one of:\n" + " mono: Modulate as mono\n" + " stereo: Modulate as stereo\n" + " bilingual: Modulate as bilingual\n" + " mono-sap: Modulate as mono with Second Audio Program\n" + " stereo-sap: Modulate as stereo with Second Audio Program\n" "\n"); printf("Expert options:\n" " --streamoff turn the stream off [VIDIOC_STREAMOFF]\n" @@ -946,6 +959,21 @@ static std::string rxsubchans2s(int rxsubchans) return s; } +static std::string txsubchans2s(int txsubchans) +{ + std::string s; + + if (txsubchans & V4L2_TUNER_SUB_MONO) + s += "mono"; + if (txsubchans & V4L2_TUNER_SUB_STEREO) + s += "stereo"; + if (txsubchans & V4L2_TUNER_SUB_LANG1) + s += "bilingual"; + if (txsubchans & V4L2_TUNER_SUB_SAP) + s += "+sap"; + return s; +} + static std::string tcap2s(unsigned cap) { std::string s; @@ -1384,6 +1412,7 @@ int main(int argc, char **argv) struct v4l2_jpegcompression jpegcomp; /* jpeg compression */ int input; /* set_input/get_input */ int output; /* set_output/get_output */ + int txsubchans; /* set_modulator */ v4l2_std_id std; /* get_std/set_std */ double freq = 0; /* get/set frequency */ struct v4l2_frequency vf; /* get_freq/set_freq */ @@ -1695,6 +1724,24 @@ int main(int argc, char **argv) return 1; } break; + case OptSetModulator: + txsubchans = strtol(optarg, 0L, 0); + if (!strcmp(optarg, "stereo")) + txsubchans = V4L2_TUNER_SUB_STEREO; + else if (!strcmp(optarg, "stereo-sap")) + txsubchans = V4L2_TUNER_SUB_STEREO | V4L2_TUNER_SUB_SAP; + else if (!strcmp(optarg, "bilingual")) + txsubchans = V4L2_TUNER_SUB_LANG1; + else if (!strcmp(optarg, "mono")) + txsubchans = V4L2_TUNER_SUB_MONO; + else if (!strcmp(optarg, "mono-sap")) + txsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_SAP; + else { + fprintf(stderr, "Unknown txsubchans value\n"); + usage(); + return 1; + } + break; case OptSetSlicedVbiFormat: case OptSetSlicedVbiOutFormat: case OptTrySlicedVbiFormat: @@ -1861,6 +1908,7 @@ int main(int argc, char **argv) options[OptGetStandard] = 1; options[OptGetFreq] = 1; options[OptGetTuner] = 1; + options[OptGetModulator] = 1; options[OptGetOverlayFormat] = 1; options[OptGetOutputOverlayFormat] = 1; options[OptGetVbiFormat] = 1; @@ -1954,6 +2002,16 @@ int main(int argc, char **argv) } } + if (options[OptSetModulator]) { + struct v4l2_modulator mt; + + memset(&mt, 0, sizeof(struct v4l2_modulator)); + if (doioctl(fd, VIDIOC_G_MODULATOR, &mt, "VIDIOC_G_MODULATOR") == 0) { + mt.txsubchans = txsubchans; + doioctl(fd, VIDIOC_S_MODULATOR, &mt, "VIDIOC_S_MODULATOR"); + } + } + if (options[OptSetVideoFormat] || options[OptTryVideoFormat]) { struct v4l2_format in_vfmt; @@ -2403,6 +2461,7 @@ set_vid_fmt_error: if (options[OptGetTuner]) { struct v4l2_tuner vt; + memset(&vt, 0, sizeof(struct v4l2_tuner)); if (doioctl(fd, VIDIOC_G_TUNER, &vt, "VIDIOC_G_TUNER") == 0) { printf("Tuner:\n"); @@ -2421,6 +2480,25 @@ set_vid_fmt_error: } } + if (options[OptGetModulator]) { + struct v4l2_modulator mt; + + memset(&mt, 0, sizeof(struct v4l2_modulator)); + if (doioctl(fd, VIDIOC_G_MODULATOR, &mt, "VIDIOC_G_MODULATOR") == 0) { + printf("Modulator:\n"); + printf("\tName : %s\n", mt.name); + printf("\tCapabilities : %s\n", tcap2s(mt.capability).c_str()); + if (mt.capability & V4L2_TUNER_CAP_LOW) + printf("\tFrequency range : %.1f MHz - %.1f MHz\n", + mt.rangelow / 16000.0, mt.rangehigh / 16000.0); + else + printf("\tFrequency range : %.1f MHz - %.1f MHz\n", + mt.rangelow / 16.0, mt.rangehigh / 16.0); + printf("\tSubchannel modulation: %s\n", + txsubchans2s(mt.txsubchans).c_str()); + } + } + if (options[OptLogStatus]) { static char buf[40960]; int len; diff --git a/v4l2-spec/pixfmt.sgml b/v4l2-spec/pixfmt.sgml index 3f39b408e..36a6934c7 100644 --- a/v4l2-spec/pixfmt.sgml +++ b/v4l2-spec/pixfmt.sgml @@ -724,6 +724,11 @@ kernel sources in the file <filename>Documentation/video4linux/cx2341x/README.hm <entry>'M310'</entry> <entry>Compressed BGGR Bayer format used by the gspca driver.</entry> </row> + <row id="V4L2-PIX-FMT-OV518"> + <entry><constant>V4L2_PIX_FMT_OV518</constant></entry> + <entry>'0518'</entry> + <entry>OV518 JPEG format used by the gspca driver.</entry> + </row> <row id="V4L2-PIX-FMT-PJPG"> <entry><constant>V4L2_PIX_FMT_PJPG</constant></entry> <entry>'PJPG'</entry> diff --git a/v4l2-spec/vidioc-g-modulator.sgml b/v4l2-spec/vidioc-g-modulator.sgml index e5d4a3f24..a3ea5ef8a 100644 --- a/v4l2-spec/vidioc-g-modulator.sgml +++ b/v4l2-spec/vidioc-g-modulator.sgml @@ -166,7 +166,7 @@ combine with <constant>V4L2_TUNER_SUB_STEREO</constant> or channel of a stereo audio signal. When the input has only one channel or two channels and <constant>V4L2_TUNER_SUB_SAP</constant> is also set, channel 1 is encoded as left and right channel. This flag does -not combine with <constant>V4L2_TUNER_SUB_MONO </constant> or +not combine with <constant>V4L2_TUNER_SUB_MONO</constant> or <constant>V4L2_TUNER_SUB_LANG1</constant>. When the driver does not support stereo audio it shall fall back to mono.</entry> </row> @@ -177,8 +177,9 @@ support stereo audio it shall fall back to mono.</entry> language of a bilingual audio signal. When the input has only one channel it is used for both languages. It is not possible to encode the primary or secondary language only. This flag does not combine -with <constant>V4L2_TUNER_SUB_MONO </constant> or -<constant>V4L2_TUNER_SUB_STEREO</constant>. If the hardware does not +with <constant>V4L2_TUNER_SUB_MONO</constant>, +<constant>V4L2_TUNER_SUB_STEREO</constant> or +<constant>V4L2_TUNER_SUB_SAP</constant>. If the hardware does not support the respective audio matrix, or the current video standard does not permit bilingual audio the <constant>VIDIOC_S_MODULATOR</constant> ioctl shall return an &EINVAL; @@ -188,7 +189,7 @@ and the driver shall fall back to mono or stereo mode.</entry> <entry><constant>V4L2_TUNER_SUB_LANG2</constant></entry> <entry>0x0004</entry> <entry>Same effect as -<constant>V4L2_TUNER_SUB_LANG1</constant>.</entry> +<constant>V4L2_TUNER_SUB_SAP</constant>.</entry> </row> <row> <entry><constant>V4L2_TUNER_SUB_SAP</constant></entry> @@ -198,7 +199,7 @@ and the driver shall fall back to mono or stereo mode.</entry> channel as Second Audio Program. When the input has only one channel it is used for both audio tracks. When the input has three channels the mono track is a down-mix of channel 1 and 2. When combined with -<constant>V4L2_TUNER_SUB_STEREO </constant> channel 1 and 2 are +<constant>V4L2_TUNER_SUB_STEREO</constant> channel 1 and 2 are encoded as left and right stereo audio, channel 3 as Second Audio Program. When the input has only two channels, the first is encoded as left and right channel and the second as SAP. When the input has only @@ -207,7 +208,7 @@ encode a Second Audio Program only. This flag must combine with <constant>V4L2_TUNER_SUB_MONO</constant> or <constant>V4L2_TUNER_SUB_STEREO</constant>. If the hardware does not support the respective audio matrix, or the current video standard -does not permit SAP the <constant> VIDIOC_S_MODULATOR</constant> ioctl +does not permit SAP the <constant>VIDIOC_S_MODULATOR</constant> ioctl shall return an &EINVAL; and driver shall fall back to mono or stereo mode.</entry> </row> |