diff options
Diffstat (limited to 'drivers/media')
95 files changed, 2709 insertions, 6026 deletions
diff --git a/drivers/media/dvb-core/dvb-usb-ids.h b/drivers/media/dvb-core/dvb-usb-ids.h index dbdbb84294c5..0afad395ef97 100644 --- a/drivers/media/dvb-core/dvb-usb-ids.h +++ b/drivers/media/dvb-core/dvb-usb-ids.h @@ -242,6 +242,7 @@ #define USB_PID_AVERMEDIA_1867 0x1867 #define USB_PID_AVERMEDIA_A867 0xa867 #define USB_PID_AVERMEDIA_H335 0x0335 +#define USB_PID_AVERMEDIA_TD110 0xa110 #define USB_PID_AVERMEDIA_TWINSTAR 0x0825 #define USB_PID_TECHNOTREND_CONNECT_S2400 0x3006 #define USB_PID_TECHNOTREND_CONNECT_S2400_8KEEPROM 0x3009 diff --git a/drivers/media/dvb-core/dvb_frontend.c b/drivers/media/dvb-core/dvb_frontend.c index 4c35eb47472b..c0142614c408 100644 --- a/drivers/media/dvb-core/dvb_frontend.c +++ b/drivers/media/dvb-core/dvb_frontend.c @@ -134,7 +134,6 @@ struct dvb_frontend_private { #if defined(CONFIG_MEDIA_CONTROLLER_DVB) struct media_pipeline pipe; - struct media_entity *pipe_start_entity; #endif }; @@ -600,94 +599,6 @@ static void dvb_frontend_wakeup(struct dvb_frontend *fe) wake_up_interruptible(&fepriv->wait_queue); } -/** - * dvb_enable_media_tuner() - tries to enable the DVB tuner - * - * @fe: struct dvb_frontend pointer - * - * This function ensures that just one media tuner is enabled for a given - * frontend. It has two different behaviors: - * - For trivial devices with just one tuner: - * it just enables the existing tuner->fe link - * - For devices with more than one tuner: - * It is up to the driver to implement the logic that will enable one tuner - * and disable the other ones. However, if more than one tuner is enabled for - * the same frontend, it will print an error message and return -EINVAL. - * - * At return, it will return the error code returned by media_entity_setup_link, - * or 0 if everything is OK, if no tuner is linked to the frontend or if the - * mdev is NULL. - */ -#ifdef CONFIG_MEDIA_CONTROLLER_DVB -static int dvb_enable_media_tuner(struct dvb_frontend *fe) -{ - struct dvb_frontend_private *fepriv = fe->frontend_priv; - struct dvb_adapter *adapter = fe->dvb; - struct media_device *mdev = adapter->mdev; - struct media_entity *entity, *source; - struct media_link *link, *found_link = NULL; - int ret, n_links = 0, active_links = 0; - - fepriv->pipe_start_entity = NULL; - - if (!mdev) - return 0; - - entity = fepriv->dvbdev->entity; - fepriv->pipe_start_entity = entity; - - list_for_each_entry(link, &entity->links, list) { - if (link->sink->entity == entity) { - found_link = link; - n_links++; - if (link->flags & MEDIA_LNK_FL_ENABLED) - active_links++; - } - } - - if (!n_links || active_links == 1 || !found_link) - return 0; - - /* - * If a frontend has more than one tuner linked, it is up to the driver - * to select with one will be the active one, as the frontend core can't - * guess. If the driver doesn't do that, it is a bug. - */ - if (n_links > 1 && active_links != 1) { - dev_err(fe->dvb->device, - "WARNING: there are %d active links among %d tuners. This is a driver's bug!\n", - active_links, n_links); - return -EINVAL; - } - - source = found_link->source->entity; - fepriv->pipe_start_entity = source; - list_for_each_entry(link, &source->links, list) { - struct media_entity *sink; - int flags = 0; - - sink = link->sink->entity; - if (sink == entity) - flags = MEDIA_LNK_FL_ENABLED; - - ret = media_entity_setup_link(link, flags); - if (ret) { - dev_err(fe->dvb->device, - "Couldn't change link %s->%s to %s. Error %d\n", - source->name, sink->name, - flags ? "enabled" : "disabled", - ret); - return ret; - } else - dev_dbg(fe->dvb->device, - "link %s->%s was %s\n", - source->name, sink->name, - flags ? "ENABLED" : "disabled"); - } - return 0; -} -#endif - static int dvb_frontend_thread(void *data) { struct dvb_frontend *fe = data; @@ -695,10 +606,6 @@ static int dvb_frontend_thread(void *data) struct dvb_frontend_private *fepriv = fe->frontend_priv; enum fe_status s; enum dvbfe_algo algo; -#ifdef CONFIG_MEDIA_CONTROLLER_DVB - int ret; -#endif - bool re_tune = false; bool semheld = false; @@ -711,20 +618,6 @@ static int dvb_frontend_thread(void *data) fepriv->wakeup = 0; fepriv->reinitialise = 0; -#ifdef CONFIG_MEDIA_CONTROLLER_DVB - ret = dvb_enable_media_tuner(fe); - if (ret) { - /* FIXME: return an error if it fails */ - dev_info(fe->dvb->device, - "proceeding with FE task\n"); - } else if (fepriv->pipe_start_entity) { - ret = media_entity_pipeline_start(fepriv->pipe_start_entity, - &fepriv->pipe); - if (ret) - return ret; - } -#endif - dvb_frontend_init(fe); set_freezable(); @@ -834,12 +727,6 @@ restart: } } -#ifdef CONFIG_MEDIA_CONTROLLER_DVB - if (fepriv->pipe_start_entity) - media_entity_pipeline_stop(fepriv->pipe_start_entity); - fepriv->pipe_start_entity = NULL; -#endif - if (dvb_powerdown_on_sleep) { if (fe->ops.set_voltage) fe->ops.set_voltage(fe, SEC_VOLTAGE_OFF); @@ -2637,9 +2524,20 @@ static int dvb_frontend_open(struct inode *inode, struct file *file) fepriv->tone = -1; fepriv->voltage = -1; +#ifdef CONFIG_MEDIA_CONTROLLER_DVB + if (fe->dvb->mdev && fe->dvb->mdev->enable_source) { + ret = fe->dvb->mdev->enable_source(dvbdev->entity, + &fepriv->pipe); + if (ret) { + dev_err(fe->dvb->device, + "Tuner is busy. Error %d\n", ret); + goto err2; + } + } +#endif ret = dvb_frontend_start (fe); if (ret) - goto err2; + goto err3; /* empty event queue */ fepriv->events.eventr = fepriv->events.eventw = 0; @@ -2649,7 +2547,12 @@ static int dvb_frontend_open(struct inode *inode, struct file *file) mutex_unlock (&adapter->mfe_lock); return ret; +err3: +#ifdef CONFIG_MEDIA_CONTROLLER_DVB + if (fe->dvb->mdev && fe->dvb->mdev->disable_source) + fe->dvb->mdev->disable_source(dvbdev->entity); err2: +#endif dvb_generic_release(inode, file); err1: if (dvbdev->users == -1 && fe->ops.ts_bus_ctrl) @@ -2678,6 +2581,10 @@ static int dvb_frontend_release(struct inode *inode, struct file *file) if (dvbdev->users == -1) { wake_up(&fepriv->wait_queue); +#ifdef CONFIG_MEDIA_CONTROLLER_DVB + if (fe->dvb->mdev && fe->dvb->mdev->disable_source) + fe->dvb->mdev->disable_source(dvbdev->entity); +#endif if (fe->exit != DVB_FE_NO_EXIT) wake_up(&dvbdev->wait_queue); if (fe->ops.ts_bus_ctrl) diff --git a/drivers/media/dvb-core/dvbdev.c b/drivers/media/dvb-core/dvbdev.c index 1b9732ee0a4f..e1684c570e2f 100644 --- a/drivers/media/dvb-core/dvbdev.c +++ b/drivers/media/dvb-core/dvbdev.c @@ -681,7 +681,7 @@ int dvb_create_media_graph(struct dvb_adapter *adap, if (demux && ca) { ret = media_create_pad_link(demux, 1, ca, 0, MEDIA_LNK_FL_ENABLED); - if (!ret) + if (ret) return -ENOMEM; } diff --git a/drivers/media/dvb-frontends/Kconfig b/drivers/media/dvb-frontends/Kconfig index 310e4b8beae8..a82f77c49bd5 100644 --- a/drivers/media/dvb-frontends/Kconfig +++ b/drivers/media/dvb-frontends/Kconfig @@ -73,6 +73,14 @@ config DVB_SI2165 Say Y when you want to support this frontend. +config DVB_MN88473 + tristate "Panasonic MN88473" + depends on DVB_CORE && I2C + select REGMAP_I2C + default m if !MEDIA_SUBDRV_AUTOSELECT + help + Say Y when you want to support this frontend. + comment "DVB-S (satellite) frontends" depends on DVB_CORE diff --git a/drivers/media/dvb-frontends/Makefile b/drivers/media/dvb-frontends/Makefile index 37ef17b5b995..eb7191f4219d 100644 --- a/drivers/media/dvb-frontends/Makefile +++ b/drivers/media/dvb-frontends/Makefile @@ -95,6 +95,7 @@ obj-$(CONFIG_DVB_STV0900) += stv0900.o obj-$(CONFIG_DVB_STV090x) += stv090x.o obj-$(CONFIG_DVB_STV6110x) += stv6110x.o obj-$(CONFIG_DVB_M88DS3103) += m88ds3103.o +obj-$(CONFIG_DVB_MN88473) += mn88473.o obj-$(CONFIG_DVB_ISL6423) += isl6423.o obj-$(CONFIG_DVB_EC100) += ec100.o obj-$(CONFIG_DVB_HD29L2) += hd29l2.o diff --git a/drivers/media/dvb-frontends/au8522.h b/drivers/media/dvb-frontends/au8522.h index dde61582c158..78bf3f73e58d 100644 --- a/drivers/media/dvb-frontends/au8522.h +++ b/drivers/media/dvb-frontends/au8522.h @@ -89,5 +89,4 @@ enum au8522_audio_input { AU8522_AUDIO_NONE, AU8522_AUDIO_SIF, }; - #endif /* __AU8522_H__ */ diff --git a/drivers/media/dvb-frontends/au8522_decoder.c b/drivers/media/dvb-frontends/au8522_decoder.c index 73612c5353d1..add246382806 100644 --- a/drivers/media/dvb-frontends/au8522_decoder.c +++ b/drivers/media/dvb-frontends/au8522_decoder.c @@ -763,9 +763,10 @@ static int au8522_probe(struct i2c_client *client, v4l2_i2c_subdev_init(sd, client, &au8522_ops); #if defined(CONFIG_MEDIA_CONTROLLER) - state->pads[AU8522_PAD_INPUT].flags = MEDIA_PAD_FL_SINK; - state->pads[AU8522_PAD_VID_OUT].flags = MEDIA_PAD_FL_SOURCE; - state->pads[AU8522_PAD_VBI_OUT].flags = MEDIA_PAD_FL_SOURCE; + state->pads[DEMOD_PAD_IF_INPUT].flags = MEDIA_PAD_FL_SINK; + state->pads[DEMOD_PAD_VID_OUT].flags = MEDIA_PAD_FL_SOURCE; + state->pads[DEMOD_PAD_VBI_OUT].flags = MEDIA_PAD_FL_SOURCE; + state->pads[DEMOD_PAD_AUDIO_OUT].flags = MEDIA_PAD_FL_SOURCE; sd->entity.function = MEDIA_ENT_F_ATV_DECODER; ret = media_entity_pads_init(&sd->entity, ARRAY_SIZE(state->pads), diff --git a/drivers/media/dvb-frontends/au8522_priv.h b/drivers/media/dvb-frontends/au8522_priv.h index 404a0cb0ed8d..f5a9438f6ce5 100644 --- a/drivers/media/dvb-frontends/au8522_priv.h +++ b/drivers/media/dvb-frontends/au8522_priv.h @@ -30,6 +30,7 @@ #include <linux/videodev2.h> #include <media/v4l2-device.h> #include <media/v4l2-ctrls.h> +#include <media/v4l2-mc.h> #include <linux/i2c.h> #include "dvb_frontend.h" #include "au8522.h" @@ -39,14 +40,6 @@ #define AU8522_DIGITAL_MODE 1 #define AU8522_SUSPEND_MODE 2 -enum au8522_media_pads { - AU8522_PAD_INPUT, - AU8522_PAD_VID_OUT, - AU8522_PAD_VBI_OUT, - - AU8522_NUM_PADS -}; - struct au8522_state { struct i2c_client *c; struct i2c_adapter *i2c; @@ -78,7 +71,7 @@ struct au8522_state { struct v4l2_ctrl_handler hdl; #ifdef CONFIG_MEDIA_CONTROLLER - struct media_pad pads[AU8522_NUM_PADS]; + struct media_pad pads[DEMOD_NUM_PADS]; #endif }; diff --git a/drivers/media/dvb-frontends/cx24120.c b/drivers/media/dvb-frontends/cx24120.c index 6ccbd86c9490..066ee387bf25 100644 --- a/drivers/media/dvb-frontends/cx24120.c +++ b/drivers/media/dvb-frontends/cx24120.c @@ -1507,11 +1507,13 @@ static int cx24120_get_frontend(struct dvb_frontend *fe, { struct cx24120_state *state = fe->demodulator_priv; u8 freq1, freq2, freq3; + int status; dev_dbg(&state->i2c->dev, "\n"); /* don't return empty data if we're not tuned in */ - if ((state->fe_status & FE_HAS_LOCK) == 0) + status = cx24120_readreg(state, CX24120_REG_STATUS); + if (!(status & CX24120_HAS_LOCK)) return 0; /* Get frequency */ diff --git a/drivers/media/dvb-frontends/dib0090.c b/drivers/media/dvb-frontends/dib0090.c index 976ee034a430..dc2d41e144fd 100644 --- a/drivers/media/dvb-frontends/dib0090.c +++ b/drivers/media/dvb-frontends/dib0090.c @@ -1115,9 +1115,15 @@ void dib0090_pwm_gain_reset(struct dvb_frontend *fe) dib0090_set_bbramp_pwm(state, bb_ramp); /* activate the ramp generator using PWM control */ - dprintk("ramp RF gain = %d BAND = %s version = %d", state->rf_ramp[0], (state->current_band == BAND_CBAND) ? "CBAND" : "NOT CBAND", state->identity.version & 0x1f); - - if ((state->rf_ramp[0] == 0) || (state->current_band == BAND_CBAND && (state->identity.version & 0x1f) <= P1D_E_F)) { + if (state->rf_ramp) + dprintk("ramp RF gain = %d BAND = %s version = %d", + state->rf_ramp[0], + (state->current_band == BAND_CBAND) ? "CBAND" : "NOT CBAND", + state->identity.version & 0x1f); + + if (rf_ramp && ((state->rf_ramp[0] == 0) || + (state->current_band == BAND_CBAND && + (state->identity.version & 0x1f) <= P1D_E_F))) { dprintk("DE-Engage mux for direct gain reg control"); en_pwm_rf_mux = 0; } else diff --git a/drivers/media/dvb-frontends/dib9000.c b/drivers/media/dvb-frontends/dib9000.c index bab8c5a980a2..5897977d2d00 100644 --- a/drivers/media/dvb-frontends/dib9000.c +++ b/drivers/media/dvb-frontends/dib9000.c @@ -225,7 +225,7 @@ static u16 to_fw_output_mode(u16 mode) } } -static u16 dib9000_read16_attr(struct dib9000_state *state, u16 reg, u8 * b, u32 len, u16 attribute) +static int dib9000_read16_attr(struct dib9000_state *state, u16 reg, u8 *b, u32 len, u16 attribute) { u32 chunk_size = 126; u32 l; @@ -309,7 +309,7 @@ static inline u16 dib9000_read_word_attr(struct dib9000_state *state, u16 reg, u #define dib9000_read16_noinc_attr(state, reg, b, len, attribute) dib9000_read16_attr(state, reg, b, len, (attribute) | DATA_BUS_ACCESS_MODE_NO_ADDRESS_INCREMENT) -static u16 dib9000_write16_attr(struct dib9000_state *state, u16 reg, const u8 * buf, u32 len, u16 attribute) +static int dib9000_write16_attr(struct dib9000_state *state, u16 reg, const u8 *buf, u32 len, u16 attribute) { u32 chunk_size = 126; u32 l; diff --git a/drivers/media/dvb-frontends/drx39xyj/drxj.c b/drivers/media/dvb-frontends/drx39xyj/drxj.c index b28b5787b39a..e48b741d439e 100644 --- a/drivers/media/dvb-frontends/drx39xyj/drxj.c +++ b/drivers/media/dvb-frontends/drx39xyj/drxj.c @@ -4131,7 +4131,7 @@ int drxj_dap_scu_atomic_read_write_block(struct i2c_device_addr *dev_addr, u32 a { struct drxjscu_cmd scu_cmd; int rc; - u16 set_param_parameters[15]; + u16 set_param_parameters[18]; u16 cmd_result[15]; /* Parameter check */ @@ -9597,12 +9597,13 @@ ctrl_get_qam_sig_quality(struct drx_demod_instance *demod) Precision errors still possible. */ - e = post_bit_err_rs * 742686; - m = fec_oc_period * 100; - if (fec_oc_period == 0) + if (!fec_oc_period) { qam_post_rs_ber = 0xFFFFFFFF; - else + } else { + e = post_bit_err_rs * 742686; + m = fec_oc_period * 100; qam_post_rs_ber = e / m; + } /* fill signal quality data structure */ p->pre_bit_count.stat[0].scale = FE_SCALE_COUNTER; diff --git a/drivers/media/dvb-frontends/mn88473.c b/drivers/media/dvb-frontends/mn88473.c new file mode 100644 index 000000000000..6c5d592161d4 --- /dev/null +++ b/drivers/media/dvb-frontends/mn88473.c @@ -0,0 +1,606 @@ +/* + * Panasonic MN88473 DVB-T/T2/C demodulator driver + * + * Copyright (C) 2014 Antti Palosaari <crope@iki.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "mn88473_priv.h" + +static int mn88473_get_tune_settings(struct dvb_frontend *fe, + struct dvb_frontend_tune_settings *s) +{ + s->min_delay_ms = 1000; + return 0; +} + +static int mn88473_set_frontend(struct dvb_frontend *fe) +{ + struct i2c_client *client = fe->demodulator_priv; + struct mn88473_dev *dev = i2c_get_clientdata(client); + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + int ret, i; + unsigned int uitmp; + u32 if_frequency; + u8 delivery_system_val, if_val[3], *conf_val_ptr; + u8 reg_bank2_2d_val, reg_bank0_d2_val; + + dev_dbg(&client->dev, + "delivery_system=%u modulation=%u frequency=%u bandwidth_hz=%u symbol_rate=%u inversion=%d stream_id=%d\n", + c->delivery_system, c->modulation, c->frequency, + c->bandwidth_hz, c->symbol_rate, c->inversion, c->stream_id); + + if (!dev->active) { + ret = -EAGAIN; + goto err; + } + + switch (c->delivery_system) { + case SYS_DVBT: + delivery_system_val = 0x02; + reg_bank2_2d_val = 0x23; + reg_bank0_d2_val = 0x2a; + break; + case SYS_DVBT2: + delivery_system_val = 0x03; + reg_bank2_2d_val = 0x3b; + reg_bank0_d2_val = 0x29; + break; + case SYS_DVBC_ANNEX_A: + delivery_system_val = 0x04; + reg_bank2_2d_val = 0x3b; + reg_bank0_d2_val = 0x29; + break; + default: + ret = -EINVAL; + goto err; + } + + switch (c->delivery_system) { + case SYS_DVBT: + case SYS_DVBT2: + switch (c->bandwidth_hz) { + case 6000000: + conf_val_ptr = "\xe9\x55\x55\x1c\x29\x1c\x29"; + break; + case 7000000: + conf_val_ptr = "\xc8\x00\x00\x17\x0a\x17\x0a"; + break; + case 8000000: + conf_val_ptr = "\xaf\x00\x00\x11\xec\x11\xec"; + break; + default: + ret = -EINVAL; + goto err; + } + break; + case SYS_DVBC_ANNEX_A: + conf_val_ptr = "\x10\xab\x0d\xae\x1d\x9d"; + break; + default: + break; + } + + /* Program tuner */ + if (fe->ops.tuner_ops.set_params) { + ret = fe->ops.tuner_ops.set_params(fe); + if (ret) + goto err; + } + + if (fe->ops.tuner_ops.get_if_frequency) { + ret = fe->ops.tuner_ops.get_if_frequency(fe, &if_frequency); + if (ret) + goto err; + + dev_dbg(&client->dev, "get_if_frequency=%u\n", if_frequency); + } else { + ret = -EINVAL; + goto err; + } + + /* Calculate IF registers */ + uitmp = DIV_ROUND_CLOSEST_ULL((u64) if_frequency * 0x1000000, dev->clk); + if_val[0] = (uitmp >> 16) & 0xff; + if_val[1] = (uitmp >> 8) & 0xff; + if_val[2] = (uitmp >> 0) & 0xff; + + ret = regmap_write(dev->regmap[2], 0x05, 0x00); + if (ret) + goto err; + ret = regmap_write(dev->regmap[2], 0xfb, 0x13); + if (ret) + goto err; + ret = regmap_write(dev->regmap[2], 0xef, 0x13); + if (ret) + goto err; + ret = regmap_write(dev->regmap[2], 0xf9, 0x13); + if (ret) + goto err; + ret = regmap_write(dev->regmap[2], 0x00, 0x18); + if (ret) + goto err; + ret = regmap_write(dev->regmap[2], 0x01, 0x01); + if (ret) + goto err; + ret = regmap_write(dev->regmap[2], 0x02, 0x21); + if (ret) + goto err; + ret = regmap_write(dev->regmap[2], 0x03, delivery_system_val); + if (ret) + goto err; + ret = regmap_write(dev->regmap[2], 0x0b, 0x00); + if (ret) + goto err; + + for (i = 0; i < sizeof(if_val); i++) { + ret = regmap_write(dev->regmap[2], 0x10 + i, if_val[i]); + if (ret) + goto err; + } + + switch (c->delivery_system) { + case SYS_DVBT: + case SYS_DVBT2: + for (i = 0; i < 7; i++) { + ret = regmap_write(dev->regmap[2], 0x13 + i, + conf_val_ptr[i]); + if (ret) + goto err; + } + break; + case SYS_DVBC_ANNEX_A: + ret = regmap_bulk_write(dev->regmap[1], 0x10, conf_val_ptr, 6); + if (ret) + goto err; + break; + default: + break; + } + + ret = regmap_write(dev->regmap[2], 0x2d, reg_bank2_2d_val); + if (ret) + goto err; + ret = regmap_write(dev->regmap[2], 0x2e, 0x00); + if (ret) + goto err; + ret = regmap_write(dev->regmap[2], 0x56, 0x0d); + if (ret) + goto err; + ret = regmap_bulk_write(dev->regmap[0], 0x01, + "\xba\x13\x80\xba\x91\xdd\xe7\x28", 8); + if (ret) + goto err; + ret = regmap_write(dev->regmap[0], 0x0a, 0x1a); + if (ret) + goto err; + ret = regmap_write(dev->regmap[0], 0x13, 0x1f); + if (ret) + goto err; + ret = regmap_write(dev->regmap[0], 0x19, 0x03); + if (ret) + goto err; + ret = regmap_write(dev->regmap[0], 0x1d, 0xb0); + if (ret) + goto err; + ret = regmap_write(dev->regmap[0], 0x2a, 0x72); + if (ret) + goto err; + ret = regmap_write(dev->regmap[0], 0x2d, 0x00); + if (ret) + goto err; + ret = regmap_write(dev->regmap[0], 0x3c, 0x00); + if (ret) + goto err; + ret = regmap_write(dev->regmap[0], 0x3f, 0xf8); + if (ret) + goto err; + ret = regmap_bulk_write(dev->regmap[0], 0x40, "\xf4\x08", 2); + if (ret) + goto err; + ret = regmap_write(dev->regmap[0], 0xd2, reg_bank0_d2_val); + if (ret) + goto err; + ret = regmap_write(dev->regmap[0], 0xd4, 0x55); + if (ret) + goto err; + ret = regmap_write(dev->regmap[1], 0xbe, 0x08); + if (ret) + goto err; + ret = regmap_write(dev->regmap[0], 0xb2, 0x37); + if (ret) + goto err; + ret = regmap_write(dev->regmap[0], 0xd7, 0x04); + if (ret) + goto err; + + /* Reset FSM */ + ret = regmap_write(dev->regmap[2], 0xf8, 0x9f); + if (ret) + goto err; + + return 0; +err: + dev_dbg(&client->dev, "failed=%d\n", ret); + return ret; +} + +static int mn88473_read_status(struct dvb_frontend *fe, enum fe_status *status) +{ + struct i2c_client *client = fe->demodulator_priv; + struct mn88473_dev *dev = i2c_get_clientdata(client); + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + int ret; + unsigned int uitmp; + + if (!dev->active) { + ret = -EAGAIN; + goto err; + } + + *status = 0; + + switch (c->delivery_system) { + case SYS_DVBT: + ret = regmap_read(dev->regmap[0], 0x62, &uitmp); + if (ret) + goto err; + + if (!(uitmp & 0xa0)) { + if ((uitmp & 0x0f) >= 0x09) + *status = FE_HAS_SIGNAL | FE_HAS_CARRIER | + FE_HAS_VITERBI | FE_HAS_SYNC | + FE_HAS_LOCK; + else if ((uitmp & 0x0f) >= 0x03) + *status = FE_HAS_SIGNAL | FE_HAS_CARRIER; + } + break; + case SYS_DVBT2: + ret = regmap_read(dev->regmap[2], 0x8b, &uitmp); + if (ret) + goto err; + + if (!(uitmp & 0x40)) { + if ((uitmp & 0x0f) >= 0x0d) + *status = FE_HAS_SIGNAL | FE_HAS_CARRIER | + FE_HAS_VITERBI | FE_HAS_SYNC | + FE_HAS_LOCK; + else if ((uitmp & 0x0f) >= 0x0a) + *status = FE_HAS_SIGNAL | FE_HAS_CARRIER | + FE_HAS_VITERBI; + else if ((uitmp & 0x0f) >= 0x07) + *status = FE_HAS_SIGNAL | FE_HAS_CARRIER; + } + break; + case SYS_DVBC_ANNEX_A: + ret = regmap_read(dev->regmap[1], 0x85, &uitmp); + if (ret) + goto err; + + if (!(uitmp & 0x40)) { + ret = regmap_read(dev->regmap[1], 0x89, &uitmp); + if (ret) + goto err; + + if (uitmp & 0x01) + *status = FE_HAS_SIGNAL | FE_HAS_CARRIER | + FE_HAS_VITERBI | FE_HAS_SYNC | + FE_HAS_LOCK; + } + break; + default: + ret = -EINVAL; + goto err; + } + + return 0; +err: + dev_dbg(&client->dev, "failed=%d\n", ret); + return ret; +} + +static int mn88473_init(struct dvb_frontend *fe) +{ + struct i2c_client *client = fe->demodulator_priv; + struct mn88473_dev *dev = i2c_get_clientdata(client); + int ret, len, remain; + unsigned int uitmp; + const struct firmware *fw; + const char *name = MN88473_FIRMWARE; + + dev_dbg(&client->dev, "\n"); + + /* Check if firmware is already running */ + ret = regmap_read(dev->regmap[0], 0xf5, &uitmp); + if (ret) + goto err; + + if (!(uitmp & 0x01)) + goto warm; + + /* Request the firmware, this will block and timeout */ + ret = request_firmware(&fw, name, &client->dev); + if (ret) { + dev_err(&client->dev, "firmare file '%s' not found\n", name); + goto err; + } + + dev_info(&client->dev, "downloading firmware from file '%s'\n", name); + + ret = regmap_write(dev->regmap[0], 0xf5, 0x03); + if (ret) + goto err_release_firmware; + + for (remain = fw->size; remain > 0; remain -= (dev->i2c_wr_max - 1)) { + len = min(dev->i2c_wr_max - 1, remain); + ret = regmap_bulk_write(dev->regmap[0], 0xf6, + &fw->data[fw->size - remain], len); + if (ret) { + dev_err(&client->dev, "firmware download failed %d\n", + ret); + goto err_release_firmware; + } + } + + release_firmware(fw); + + /* Parity check of firmware */ + ret = regmap_read(dev->regmap[0], 0xf8, &uitmp); + if (ret) + goto err; + + if (uitmp & 0x10) { + dev_err(&client->dev, "firmware parity check failed\n"); + ret = -EINVAL; + goto err; + } + + ret = regmap_write(dev->regmap[0], 0xf5, 0x00); + if (ret) + goto err; +warm: + /* TS config */ + ret = regmap_write(dev->regmap[2], 0x09, 0x08); + if (ret) + goto err; + ret = regmap_write(dev->regmap[2], 0x08, 0x1d); + if (ret) + goto err; + + dev->active = true; + + return 0; +err_release_firmware: + release_firmware(fw); +err: + dev_dbg(&client->dev, "failed=%d\n", ret); + return ret; +} + +static int mn88473_sleep(struct dvb_frontend *fe) +{ + struct i2c_client *client = fe->demodulator_priv; + struct mn88473_dev *dev = i2c_get_clientdata(client); + int ret; + + dev_dbg(&client->dev, "\n"); + + dev->active = false; + + ret = regmap_write(dev->regmap[2], 0x05, 0x3e); + if (ret) + goto err; + + return 0; +err: + dev_dbg(&client->dev, "failed=%d\n", ret); + return ret; +} + +static const struct dvb_frontend_ops mn88473_ops = { + .delsys = {SYS_DVBT, SYS_DVBT2, SYS_DVBC_ANNEX_A}, + .info = { + .name = "Panasonic MN88473", + .symbol_rate_min = 1000000, + .symbol_rate_max = 7200000, + .caps = FE_CAN_FEC_1_2 | + FE_CAN_FEC_2_3 | + FE_CAN_FEC_3_4 | + FE_CAN_FEC_5_6 | + FE_CAN_FEC_7_8 | + FE_CAN_FEC_AUTO | + FE_CAN_QPSK | + FE_CAN_QAM_16 | + FE_CAN_QAM_32 | + FE_CAN_QAM_64 | + FE_CAN_QAM_128 | + FE_CAN_QAM_256 | + FE_CAN_QAM_AUTO | + FE_CAN_TRANSMISSION_MODE_AUTO | + FE_CAN_GUARD_INTERVAL_AUTO | + FE_CAN_HIERARCHY_AUTO | + FE_CAN_MUTE_TS | + FE_CAN_2G_MODULATION + }, + + .get_tune_settings = mn88473_get_tune_settings, + + .init = mn88473_init, + .sleep = mn88473_sleep, + + .set_frontend = mn88473_set_frontend, + + .read_status = mn88473_read_status, +}; + +static int mn88473_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct mn88473_config *config = client->dev.platform_data; + struct mn88473_dev *dev; + int ret; + unsigned int uitmp; + static const struct regmap_config regmap_config = { + .reg_bits = 8, + .val_bits = 8, + }; + + dev_dbg(&client->dev, "\n"); + + /* Caller really need to provide pointer for frontend we create */ + if (config->fe == NULL) { + dev_err(&client->dev, "frontend pointer not defined\n"); + ret = -EINVAL; + goto err; + } + + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (dev == NULL) { + ret = -ENOMEM; + goto err; + } + + if (config->i2c_wr_max) + dev->i2c_wr_max = config->i2c_wr_max; + else + dev->i2c_wr_max = ~0; + + if (config->xtal) + dev->clk = config->xtal; + else + dev->clk = 25000000; + dev->client[0] = client; + dev->regmap[0] = regmap_init_i2c(dev->client[0], ®map_config); + if (IS_ERR(dev->regmap[0])) { + ret = PTR_ERR(dev->regmap[0]); + goto err_kfree; + } + + /* Check demod answers with correct chip id */ + ret = regmap_read(dev->regmap[0], 0xff, &uitmp); + if (ret) + goto err_regmap_0_regmap_exit; + + dev_dbg(&client->dev, "chip id=%02x\n", uitmp); + + if (uitmp != 0x03) { + ret = -ENODEV; + goto err_regmap_0_regmap_exit; + } + + /* + * Chip has three I2C addresses for different register banks. Used + * addresses are 0x18, 0x1a and 0x1c. We register two dummy clients, + * 0x1a and 0x1c, in order to get own I2C client for each register bank. + * + * Also, register bank 2 do not support sequential I/O. Only single + * register write or read is allowed to that bank. + */ + dev->client[1] = i2c_new_dummy(client->adapter, 0x1a); + if (dev->client[1] == NULL) { + ret = -ENODEV; + dev_err(&client->dev, "I2C registration failed\n"); + if (ret) + goto err_regmap_0_regmap_exit; + } + dev->regmap[1] = regmap_init_i2c(dev->client[1], ®map_config); + if (IS_ERR(dev->regmap[1])) { + ret = PTR_ERR(dev->regmap[1]); + goto err_client_1_i2c_unregister_device; + } + i2c_set_clientdata(dev->client[1], dev); + + dev->client[2] = i2c_new_dummy(client->adapter, 0x1c); + if (dev->client[2] == NULL) { + ret = -ENODEV; + dev_err(&client->dev, "2nd I2C registration failed\n"); + if (ret) + goto err_regmap_1_regmap_exit; + } + dev->regmap[2] = regmap_init_i2c(dev->client[2], ®map_config); + if (IS_ERR(dev->regmap[2])) { + ret = PTR_ERR(dev->regmap[2]); + goto err_client_2_i2c_unregister_device; + } + i2c_set_clientdata(dev->client[2], dev); + + /* Sleep because chip is active by default */ + ret = regmap_write(dev->regmap[2], 0x05, 0x3e); + if (ret) + goto err_client_2_i2c_unregister_device; + + /* Create dvb frontend */ + memcpy(&dev->frontend.ops, &mn88473_ops, sizeof(dev->frontend.ops)); + dev->frontend.demodulator_priv = client; + *config->fe = &dev->frontend; + i2c_set_clientdata(client, dev); + + dev_info(&client->dev, "Panasonic MN88473 successfully identified\n"); + + return 0; + +err_client_2_i2c_unregister_device: + i2c_unregister_device(dev->client[2]); +err_regmap_1_regmap_exit: + regmap_exit(dev->regmap[1]); +err_client_1_i2c_unregister_device: + i2c_unregister_device(dev->client[1]); +err_regmap_0_regmap_exit: + regmap_exit(dev->regmap[0]); +err_kfree: + kfree(dev); +err: + dev_dbg(&client->dev, "failed=%d\n", ret); + return ret; +} + +static int mn88473_remove(struct i2c_client *client) +{ + struct mn88473_dev *dev = i2c_get_clientdata(client); + + dev_dbg(&client->dev, "\n"); + + regmap_exit(dev->regmap[2]); + i2c_unregister_device(dev->client[2]); + + regmap_exit(dev->regmap[1]); + i2c_unregister_device(dev->client[1]); + + regmap_exit(dev->regmap[0]); + + kfree(dev); + + return 0; +} + +static const struct i2c_device_id mn88473_id_table[] = { + {"mn88473", 0}, + {} +}; +MODULE_DEVICE_TABLE(i2c, mn88473_id_table); + +static struct i2c_driver mn88473_driver = { + .driver = { + .name = "mn88473", + .suppress_bind_attrs = true, + }, + .probe = mn88473_probe, + .remove = mn88473_remove, + .id_table = mn88473_id_table, +}; + +module_i2c_driver(mn88473_driver); + +MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>"); +MODULE_DESCRIPTION("Panasonic MN88473 DVB-T/T2/C demodulator driver"); +MODULE_LICENSE("GPL"); +MODULE_FIRMWARE(MN88473_FIRMWARE); diff --git a/drivers/media/dvb-frontends/mn88473.h b/drivers/media/dvb-frontends/mn88473.h index c717ebed0e03..2aa5181f3033 100644 --- a/drivers/media/dvb-frontends/mn88473.h +++ b/drivers/media/dvb-frontends/mn88473.h @@ -22,10 +22,16 @@ struct mn88473_config { /* * Max num of bytes given I2C adapter could write at once. - * Default: none + * Default: unlimited */ u16 i2c_wr_max; + /* + * Xtal frequency Hz. + * Default: 25000000 + */ + u32 xtal; + /* Everything after that is returned by the driver. */ @@ -33,12 +39,6 @@ struct mn88473_config { * DVB frontend. */ struct dvb_frontend **fe; - - /* - * Xtal frequency. - * Hz - */ - u32 xtal; }; #endif diff --git a/drivers/media/dvb-frontends/mn88473_priv.h b/drivers/media/dvb-frontends/mn88473_priv.h new file mode 100644 index 000000000000..e6c65893e451 --- /dev/null +++ b/drivers/media/dvb-frontends/mn88473_priv.h @@ -0,0 +1,36 @@ +/* + * Panasonic MN88473 DVB-T/T2/C demodulator driver + * + * Copyright (C) 2014 Antti Palosaari <crope@iki.fi> + * + * 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. + */ + +#ifndef MN88473_PRIV_H +#define MN88473_PRIV_H + +#include "dvb_frontend.h" +#include "mn88473.h" +#include <linux/firmware.h> +#include <linux/regmap.h> + +#define MN88473_FIRMWARE "dvb-demod-mn88473-01.fw" + +struct mn88473_dev { + struct i2c_client *client[3]; + struct regmap *regmap[3]; + struct dvb_frontend frontend; + u16 i2c_wr_max; + bool active; + u32 clk; +}; + +#endif diff --git a/drivers/media/dvb-frontends/rtl2832.c b/drivers/media/dvb-frontends/rtl2832.c index c2469fb42f12..7c96f7679669 100644 --- a/drivers/media/dvb-frontends/rtl2832.c +++ b/drivers/media/dvb-frontends/rtl2832.c @@ -347,6 +347,10 @@ static int rtl2832_init(struct dvb_frontend *fe) dev_dbg(&client->dev, "\n"); + ret = rtl2832_wr_demod_reg(dev, DVBT_SOFT_RST, 0x0); + if (ret) + goto err; + for (i = 0; i < ARRAY_SIZE(rtl2832_initial_regs); i++) { ret = rtl2832_wr_demod_reg(dev, rtl2832_initial_regs[i].reg, rtl2832_initial_regs[i].value); @@ -404,8 +408,6 @@ static int rtl2832_init(struct dvb_frontend *fe) c->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; c->post_bit_count.len = 1; c->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; - /* start statistics polling */ - schedule_delayed_work(&dev->stat_work, msecs_to_jiffies(2000)); dev->sleeping = false; return 0; @@ -423,8 +425,6 @@ static int rtl2832_sleep(struct dvb_frontend *fe) dev_dbg(&client->dev, "\n"); dev->sleeping = true; - /* stop statistics polling */ - cancel_delayed_work_sync(&dev->stat_work); dev->fe_status = 0; ret = rtl2832_wr_demod_reg(dev, DVBT_SOFT_RST, 0x1); @@ -491,11 +491,6 @@ static int rtl2832_set_frontend(struct dvb_frontend *fe) if (fe->ops.tuner_ops.set_params) fe->ops.tuner_ops.set_params(fe); - /* PIP mode related */ - ret = rtl2832_bulk_write(client, 0x192, "\x00\x0f\xff", 3); - if (ret) - goto err; - /* If the frontend has get_if_frequency(), use it */ if (fe->ops.tuner_ops.get_if_frequency) { u32 if_freq; @@ -692,8 +687,11 @@ static int rtl2832_read_status(struct dvb_frontend *fe, enum fe_status *status) { struct rtl2832_dev *dev = fe->demodulator_priv; struct i2c_client *client = dev->client; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; int ret; u32 uninitialized_var(tmp); + u8 u8tmp, buf[2]; + u16 u16tmp; dev_dbg(&client->dev, "\n"); @@ -714,45 +712,6 @@ static int rtl2832_read_status(struct dvb_frontend *fe, enum fe_status *status) } dev->fe_status = *status; - return 0; -err: - dev_dbg(&client->dev, "failed=%d\n", ret); - return ret; -} - -static int rtl2832_read_snr(struct dvb_frontend *fe, u16 *snr) -{ - struct dtv_frontend_properties *c = &fe->dtv_property_cache; - - /* report SNR in resolution of 0.1 dB */ - if (c->cnr.stat[0].scale == FE_SCALE_DECIBEL) - *snr = div_s64(c->cnr.stat[0].svalue, 100); - else - *snr = 0; - - return 0; -} - -static int rtl2832_read_ber(struct dvb_frontend *fe, u32 *ber) -{ - struct rtl2832_dev *dev = fe->demodulator_priv; - - *ber = (dev->post_bit_error - dev->post_bit_error_prev); - dev->post_bit_error_prev = dev->post_bit_error; - - return 0; -} - -static void rtl2832_stat_work(struct work_struct *work) -{ - struct rtl2832_dev *dev = container_of(work, struct rtl2832_dev, stat_work.work); - struct i2c_client *client = dev->client; - struct dtv_frontend_properties *c = &dev->fe.dtv_property_cache; - int ret, tmp; - u8 u8tmp, buf[2]; - u16 u16tmp; - - dev_dbg(&client->dev, "\n"); /* signal strength */ if (dev->fe_status & FE_HAS_SIGNAL) { @@ -789,11 +748,11 @@ static void rtl2832_stat_work(struct work_struct *work) constellation = (u8tmp >> 2) & 0x03; /* [3:2] */ if (constellation > CONSTELLATION_NUM - 1) - goto err_schedule_delayed_work; + goto err; hierarchy = (u8tmp >> 4) & 0x07; /* [6:4] */ if (hierarchy > HIERARCHY_NUM - 1) - goto err_schedule_delayed_work; + goto err; ret = rtl2832_bulk_read(client, 0x40c, buf, 2); if (ret) @@ -835,11 +794,33 @@ static void rtl2832_stat_work(struct work_struct *work) c->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; } -err_schedule_delayed_work: - schedule_delayed_work(&dev->stat_work, msecs_to_jiffies(2000)); - return; + return 0; err: dev_dbg(&client->dev, "failed=%d\n", ret); + return ret; +} + +static int rtl2832_read_snr(struct dvb_frontend *fe, u16 *snr) +{ + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + + /* report SNR in resolution of 0.1 dB */ + if (c->cnr.stat[0].scale == FE_SCALE_DECIBEL) + *snr = div_s64(c->cnr.stat[0].svalue, 100); + else + *snr = 0; + + return 0; +} + +static int rtl2832_read_ber(struct dvb_frontend *fe, u32 *ber) +{ + struct rtl2832_dev *dev = fe->demodulator_priv; + + *ber = (dev->post_bit_error - dev->post_bit_error_prev); + dev->post_bit_error_prev = dev->post_bit_error; + + return 0; } /* @@ -1081,37 +1062,46 @@ static struct i2c_adapter *rtl2832_get_i2c_adapter(struct i2c_client *client) return dev->i2c_adapter_tuner; } -static int rtl2832_enable_slave_ts(struct i2c_client *client) +static int rtl2832_slave_ts_ctrl(struct i2c_client *client, bool enable) { struct rtl2832_dev *dev = i2c_get_clientdata(client); int ret; - dev_dbg(&client->dev, "\n"); - - ret = rtl2832_bulk_write(client, 0x10c, "\x5f\xff", 2); - if (ret) - goto err; - - ret = rtl2832_wr_demod_reg(dev, DVBT_PIP_ON, 0x1); - if (ret) - goto err; - - ret = rtl2832_bulk_write(client, 0x0bc, "\x18", 1); - if (ret) - goto err; - - ret = rtl2832_bulk_write(client, 0x192, "\x7f\xf7\xff", 3); - if (ret) - goto err; - - /* soft reset */ - ret = rtl2832_wr_demod_reg(dev, DVBT_SOFT_RST, 0x1); - if (ret) - goto err; + dev_dbg(&client->dev, "enable=%d\n", enable); - ret = rtl2832_wr_demod_reg(dev, DVBT_SOFT_RST, 0x0); - if (ret) - goto err; + if (enable) { + ret = rtl2832_wr_demod_reg(dev, DVBT_SOFT_RST, 0x0); + if (ret) + goto err; + ret = rtl2832_bulk_write(client, 0x10c, "\x5f\xff", 2); + if (ret) + goto err; + ret = rtl2832_wr_demod_reg(dev, DVBT_PIP_ON, 0x1); + if (ret) + goto err; + ret = rtl2832_bulk_write(client, 0x0bc, "\x18", 1); + if (ret) + goto err; + ret = rtl2832_bulk_write(client, 0x192, "\x7f\xf7\xff", 3); + if (ret) + goto err; + } else { + ret = rtl2832_bulk_write(client, 0x192, "\x00\x0f\xff", 3); + if (ret) + goto err; + ret = rtl2832_bulk_write(client, 0x0bc, "\x08", 1); + if (ret) + goto err; + ret = rtl2832_wr_demod_reg(dev, DVBT_PIP_ON, 0x0); + if (ret) + goto err; + ret = rtl2832_bulk_write(client, 0x10c, "\x00\x00", 2); + if (ret) + goto err; + ret = rtl2832_wr_demod_reg(dev, DVBT_SOFT_RST, 0x1); + if (ret) + goto err; + } return 0; err: @@ -1227,7 +1217,6 @@ static int rtl2832_probe(struct i2c_client *client, dev->pdata = client->dev.platform_data; dev->sleeping = true; INIT_DELAYED_WORK(&dev->i2c_gate_work, rtl2832_i2c_gate_work); - INIT_DELAYED_WORK(&dev->stat_work, rtl2832_stat_work); /* create regmap */ mutex_init(&dev->regmap_mutex); dev->regmap_config.reg_bits = 8, @@ -1267,7 +1256,7 @@ static int rtl2832_probe(struct i2c_client *client, /* setup callbacks */ pdata->get_dvb_frontend = rtl2832_get_dvb_frontend; pdata->get_i2c_adapter = rtl2832_get_i2c_adapter; - pdata->enable_slave_ts = rtl2832_enable_slave_ts; + pdata->slave_ts_ctrl = rtl2832_slave_ts_ctrl; pdata->pid_filter = rtl2832_pid_filter; pdata->pid_filter_ctrl = rtl2832_pid_filter_ctrl; pdata->bulk_read = rtl2832_bulk_read; diff --git a/drivers/media/dvb-frontends/rtl2832.h b/drivers/media/dvb-frontends/rtl2832.h index c29a4c2bf71a..6390af64cf45 100644 --- a/drivers/media/dvb-frontends/rtl2832.h +++ b/drivers/media/dvb-frontends/rtl2832.h @@ -31,7 +31,7 @@ * @tuner: Used tuner model. * @get_dvb_frontend: Get DVB frontend. * @get_i2c_adapter: Get I2C adapter. - * @enable_slave_ts: Enable slave TS IF. + * @slave_ts_ctrl: Control slave TS interface. * @pid_filter: Set PID to PID filter. * @pid_filter_ctrl: Control PID filter. */ @@ -53,7 +53,7 @@ struct rtl2832_platform_data { struct dvb_frontend* (*get_dvb_frontend)(struct i2c_client *); struct i2c_adapter* (*get_i2c_adapter)(struct i2c_client *); - int (*enable_slave_ts)(struct i2c_client *); + int (*slave_ts_ctrl)(struct i2c_client *, bool); int (*pid_filter)(struct dvb_frontend *, u8, u16, int); int (*pid_filter_ctrl)(struct dvb_frontend *, int); /* private: Register access for SDR module use only */ diff --git a/drivers/media/dvb-frontends/rtl2832_priv.h b/drivers/media/dvb-frontends/rtl2832_priv.h index 5dcd3a41d23f..6b875f462f8b 100644 --- a/drivers/media/dvb-frontends/rtl2832_priv.h +++ b/drivers/media/dvb-frontends/rtl2832_priv.h @@ -38,7 +38,6 @@ struct rtl2832_dev { struct regmap *regmap; struct i2c_adapter *i2c_adapter_tuner; struct dvb_frontend fe; - struct delayed_work stat_work; enum fe_status fe_status; u64 post_bit_error_prev; /* for old DVBv3 read_ber() calculation */ u64 post_bit_error; diff --git a/drivers/media/dvb-frontends/stv0900_core.c b/drivers/media/dvb-frontends/stv0900_core.c index 28239b1fd954..f667005a6661 100644 --- a/drivers/media/dvb-frontends/stv0900_core.c +++ b/drivers/media/dvb-frontends/stv0900_core.c @@ -1087,7 +1087,7 @@ u8 stv0900_get_optim_carr_loop(s32 srate, enum fe_stv0900_modcode modcode, s32 pilot, u8 chip_id) { u8 aclc_value = 0x29; - s32 i; + s32 i, cllas2_size; const struct stv0900_car_loop_optim *cls2, *cllqs2, *cllas2; dprintk("%s\n", __func__); @@ -1096,14 +1096,17 @@ u8 stv0900_get_optim_carr_loop(s32 srate, enum fe_stv0900_modcode modcode, cls2 = FE_STV0900_S2CarLoop; cllqs2 = FE_STV0900_S2LowQPCarLoopCut30; cllas2 = FE_STV0900_S2APSKCarLoopCut30; + cllas2_size = ARRAY_SIZE(FE_STV0900_S2APSKCarLoopCut30); } else if (chip_id == 0x20) { cls2 = FE_STV0900_S2CarLoopCut20; cllqs2 = FE_STV0900_S2LowQPCarLoopCut20; cllas2 = FE_STV0900_S2APSKCarLoopCut20; + cllas2_size = ARRAY_SIZE(FE_STV0900_S2APSKCarLoopCut20); } else { cls2 = FE_STV0900_S2CarLoopCut30; cllqs2 = FE_STV0900_S2LowQPCarLoopCut30; cllas2 = FE_STV0900_S2APSKCarLoopCut30; + cllas2_size = ARRAY_SIZE(FE_STV0900_S2APSKCarLoopCut30); } if (modcode < STV0900_QPSK_12) { @@ -1178,7 +1181,7 @@ u8 stv0900_get_optim_carr_loop(s32 srate, enum fe_stv0900_modcode modcode, aclc_value = cls2[i].car_loop_pilots_off_30; } - } else { + } else if (i < cllas2_size) { if (srate <= 3000000) aclc_value = cllas2[i].car_loop_pilots_on_2; else if (srate <= 7000000) diff --git a/drivers/media/i2c/adv7604.c b/drivers/media/i2c/adv7604.c index d35e0ba8b269..41a1bfc5eaa7 100644 --- a/drivers/media/i2c/adv7604.c +++ b/drivers/media/i2c/adv7604.c @@ -1946,10 +1946,9 @@ static int adv76xx_isr(struct v4l2_subdev *sd, u32 status, bool *handled) } /* tx 5v detect */ - tx_5v = io_read(sd, 0x70) & info->cable_det_mask; + tx_5v = irq_reg_0x70 & info->cable_det_mask; if (tx_5v) { v4l2_dbg(1, debug, sd, "%s: tx_5v: 0x%x\n", __func__, tx_5v); - io_write(sd, 0x71, tx_5v); adv76xx_s_detect_tx_5v_ctrl(sd); if (handled) *handled = true; @@ -2096,7 +2095,8 @@ static int adv76xx_set_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid) rep_write(sd, 0x76, spa_loc & 0xff); rep_write_clr_set(sd, 0x77, 0x40, (spa_loc & 0x100) >> 2); } else { - /* FIXME: Where is the SPA location LSB register ? */ + /* ADV7612 Software Manual Rev. A, p. 15 */ + rep_write(sd, 0x70, spa_loc & 0xff); rep_write_clr_set(sd, 0x71, 0x01, (spa_loc & 0x100) >> 8); } diff --git a/drivers/media/i2c/tc358743.c b/drivers/media/i2c/tc358743.c index da7469bc6e56..972e0d47259d 100644 --- a/drivers/media/i2c/tc358743.c +++ b/drivers/media/i2c/tc358743.c @@ -1198,21 +1198,21 @@ static int tc358743_log_status(struct v4l2_subdev *sd) #ifdef CONFIG_VIDEO_ADV_DEBUG static void tc358743_print_register_map(struct v4l2_subdev *sd) { - v4l2_info(sd, "0x0000–0x00FF: Global Control Register\n"); - v4l2_info(sd, "0x0100–0x01FF: CSI2-TX PHY Register\n"); - v4l2_info(sd, "0x0200–0x03FF: CSI2-TX PPI Register\n"); - v4l2_info(sd, "0x0400–0x05FF: Reserved\n"); - v4l2_info(sd, "0x0600–0x06FF: CEC Register\n"); - v4l2_info(sd, "0x0700–0x84FF: Reserved\n"); - v4l2_info(sd, "0x8500–0x85FF: HDMIRX System Control Register\n"); - v4l2_info(sd, "0x8600–0x86FF: HDMIRX Audio Control Register\n"); - v4l2_info(sd, "0x8700–0x87FF: HDMIRX InfoFrame packet data Register\n"); - v4l2_info(sd, "0x8800–0x88FF: HDMIRX HDCP Port Register\n"); - v4l2_info(sd, "0x8900–0x89FF: HDMIRX Video Output Port & 3D Register\n"); - v4l2_info(sd, "0x8A00–0x8BFF: Reserved\n"); - v4l2_info(sd, "0x8C00–0x8FFF: HDMIRX EDID-RAM (1024bytes)\n"); - v4l2_info(sd, "0x9000–0x90FF: HDMIRX GBD Extraction Control\n"); - v4l2_info(sd, "0x9100–0x92FF: HDMIRX GBD RAM read\n"); + v4l2_info(sd, "0x0000-0x00FF: Global Control Register\n"); + v4l2_info(sd, "0x0100-0x01FF: CSI2-TX PHY Register\n"); + v4l2_info(sd, "0x0200-0x03FF: CSI2-TX PPI Register\n"); + v4l2_info(sd, "0x0400-0x05FF: Reserved\n"); + v4l2_info(sd, "0x0600-0x06FF: CEC Register\n"); + v4l2_info(sd, "0x0700-0x84FF: Reserved\n"); + v4l2_info(sd, "0x8500-0x85FF: HDMIRX System Control Register\n"); + v4l2_info(sd, "0x8600-0x86FF: HDMIRX Audio Control Register\n"); + v4l2_info(sd, "0x8700-0x87FF: HDMIRX InfoFrame packet data Register\n"); + v4l2_info(sd, "0x8800-0x88FF: HDMIRX HDCP Port Register\n"); + v4l2_info(sd, "0x8900-0x89FF: HDMIRX Video Output Port & 3D Register\n"); + v4l2_info(sd, "0x8A00-0x8BFF: Reserved\n"); + v4l2_info(sd, "0x8C00-0x8FFF: HDMIRX EDID-RAM (1024bytes)\n"); + v4l2_info(sd, "0x9000-0x90FF: HDMIRX GBD Extraction Control\n"); + v4l2_info(sd, "0x9100-0x92FF: HDMIRX GBD RAM read\n"); v4l2_info(sd, "0x9300- : Reserved\n"); } diff --git a/drivers/media/i2c/tvp5150.c b/drivers/media/i2c/tvp5150.c index ef393f5daf2a..ff18444e19e4 100644 --- a/drivers/media/i2c/tvp5150.c +++ b/drivers/media/i2c/tvp5150.c @@ -1386,7 +1386,7 @@ static int tvp5150_parse_dt(struct tvp5150 *decoder, struct device_node *np) goto err_connector; } - if (input_type > TVP5150_INPUT_NUM) { + if (input_type >= TVP5150_INPUT_NUM) { ret = -EINVAL; goto err_connector; } diff --git a/drivers/media/media-device.c b/drivers/media/media-device.c index 5ebb3cd31345..6e43c95629ea 100644 --- a/drivers/media/media-device.c +++ b/drivers/media/media-device.c @@ -20,6 +20,9 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +/* We need to access legacy defines from linux/media.h */ +#define __NEED_MEDIA_LEGACY_API + #include <linux/compat.h> #include <linux/export.h> #include <linux/idr.h> @@ -27,6 +30,8 @@ #include <linux/media.h> #include <linux/slab.h> #include <linux/types.h> +#include <linux/pci.h> +#include <linux/usb.h> #include <media/media-device.h> #include <media/media-devnode.h> @@ -38,6 +43,11 @@ * Userspace API */ +static inline void __user *media_get_uptr(__u64 arg) +{ + return (void __user *)(uintptr_t)arg; +} + static int media_device_open(struct file *filp) { return 0; @@ -119,6 +129,26 @@ static long media_device_enum_entities(struct media_device *mdev, u_ent.group_id = 0; /* Unused */ u_ent.pads = ent->num_pads; u_ent.links = ent->num_links - ent->num_backlinks; + + /* + * Workaround for a bug at media-ctl <= v1.10 that makes it to + * do the wrong thing if the entity function doesn't belong to + * either MEDIA_ENT_F_OLD_BASE or MEDIA_ENT_F_OLD_SUBDEV_BASE + * Ranges. + * + * Non-subdevices are expected to be at the MEDIA_ENT_F_OLD_BASE, + * or, otherwise, will be silently ignored by media-ctl when + * printing the graphviz diagram. So, map them into the devnode + * old range. + */ + if (ent->function < MEDIA_ENT_F_OLD_BASE || + ent->function > MEDIA_ENT_T_DEVNODE_UNKNOWN) { + if (is_media_entity_v4l2_subdev(ent)) + u_ent.type = MEDIA_ENT_F_V4L2_SUBDEV_UNKNOWN; + else if (ent->function != MEDIA_ENT_F_IO_V4L) + u_ent.type = MEDIA_ENT_T_DEVNODE_UNKNOWN; + } + memcpy(&u_ent.raw, &ent->info, sizeof(ent->info)); if (copy_to_user(uent, &u_ent, sizeof(u_ent))) return -EFAULT; @@ -245,10 +275,10 @@ static long __media_device_get_topology(struct media_device *mdev, struct media_interface *intf; struct media_pad *pad; struct media_link *link; - struct media_v2_entity kentity, *uentity; - struct media_v2_interface kintf, *uintf; - struct media_v2_pad kpad, *upad; - struct media_v2_link klink, *ulink; + struct media_v2_entity kentity, __user *uentity; + struct media_v2_interface kintf, __user *uintf; + struct media_v2_pad kpad, __user *upad; + struct media_v2_link klink, __user *ulink; unsigned int i; int ret = 0; @@ -540,6 +570,7 @@ static void media_device_release(struct media_devnode *mdev) int __must_check media_device_register_entity(struct media_device *mdev, struct media_entity *entity) { + struct media_entity_notify *notify, *next; unsigned int i; int ret; @@ -579,8 +610,33 @@ int __must_check media_device_register_entity(struct media_device *mdev, media_gobj_create(mdev, MEDIA_GRAPH_PAD, &entity->pads[i].graph_obj); + /* invoke entity_notify callbacks */ + list_for_each_entry_safe(notify, next, &mdev->entity_notify, list) { + (notify)->notify(entity, notify->notify_data); + } + spin_unlock(&mdev->lock); + mutex_lock(&mdev->graph_mutex); + if (mdev->entity_internal_idx_max + >= mdev->pm_count_walk.ent_enum.idx_max) { + struct media_entity_graph new = { .top = 0 }; + + /* + * Initialise the new graph walk before cleaning up + * the old one in order not to spoil the graph walk + * object of the media device if graph walk init fails. + */ + ret = media_entity_graph_walk_init(&new, mdev); + if (ret) { + mutex_unlock(&mdev->graph_mutex); + return ret; + } + media_entity_graph_walk_cleanup(&mdev->pm_count_walk); + mdev->pm_count_walk = new; + } + mutex_unlock(&mdev->graph_mutex); + return 0; } EXPORT_SYMBOL_GPL(media_device_register_entity); @@ -612,6 +668,8 @@ static void __media_device_unregister_entity(struct media_entity *entity) /* Remove the entity */ media_gobj_destroy(&entity->graph_obj); + /* invoke entity_notify callbacks to handle entity removal?? */ + entity->graph_obj.mdev = NULL; } @@ -644,6 +702,7 @@ void media_device_init(struct media_device *mdev) INIT_LIST_HEAD(&mdev->interfaces); INIT_LIST_HEAD(&mdev->pads); INIT_LIST_HEAD(&mdev->links); + INIT_LIST_HEAD(&mdev->entity_notify); spin_lock_init(&mdev->lock); mutex_init(&mdev->graph_mutex); ida_init(&mdev->entity_internal_idx); @@ -656,6 +715,7 @@ void media_device_cleanup(struct media_device *mdev) { ida_destroy(&mdev->entity_internal_idx); mdev->entity_internal_idx_max = 0; + media_entity_graph_walk_cleanup(&mdev->pm_count_walk); mutex_destroy(&mdev->graph_mutex); } EXPORT_SYMBOL_GPL(media_device_cleanup); @@ -689,11 +749,40 @@ int __must_check __media_device_register(struct media_device *mdev, } EXPORT_SYMBOL_GPL(__media_device_register); +int __must_check media_device_register_entity_notify(struct media_device *mdev, + struct media_entity_notify *nptr) +{ + spin_lock(&mdev->lock); + list_add_tail(&nptr->list, &mdev->entity_notify); + spin_unlock(&mdev->lock); + return 0; +} +EXPORT_SYMBOL_GPL(media_device_register_entity_notify); + +/* + * Note: Should be called with mdev->lock held. + */ +static void __media_device_unregister_entity_notify(struct media_device *mdev, + struct media_entity_notify *nptr) +{ + list_del(&nptr->list); +} + +void media_device_unregister_entity_notify(struct media_device *mdev, + struct media_entity_notify *nptr) +{ + spin_lock(&mdev->lock); + __media_device_unregister_entity_notify(mdev, nptr); + spin_unlock(&mdev->lock); +} +EXPORT_SYMBOL_GPL(media_device_unregister_entity_notify); + void media_device_unregister(struct media_device *mdev) { struct media_entity *entity; struct media_entity *next; struct media_interface *intf, *tmp_intf; + struct media_entity_notify *notify, *nextp; if (mdev == NULL) return; @@ -710,6 +799,10 @@ void media_device_unregister(struct media_device *mdev) list_for_each_entry_safe(entity, next, &mdev->entities, graph_obj.list) __media_device_unregister_entity(entity); + /* Remove all entity_notify callbacks from the media device */ + list_for_each_entry_safe(notify, nextp, &mdev->entity_notify, list) + __media_device_unregister_entity_notify(mdev, notify); + /* Remove all interfaces from the media device */ list_for_each_entry_safe(intf, tmp_intf, &mdev->interfaces, graph_obj.list) { @@ -753,4 +846,58 @@ struct media_device *media_device_find_devres(struct device *dev) } EXPORT_SYMBOL_GPL(media_device_find_devres); +void media_device_pci_init(struct media_device *mdev, + struct pci_dev *pci_dev, + const char *name) +{ +#ifdef CONFIG_PCI + mdev->dev = &pci_dev->dev; + + if (name) + strlcpy(mdev->model, name, sizeof(mdev->model)); + else + strlcpy(mdev->model, pci_name(pci_dev), sizeof(mdev->model)); + + sprintf(mdev->bus_info, "PCI:%s", pci_name(pci_dev)); + + mdev->hw_revision = (pci_dev->subsystem_vendor << 16) + | pci_dev->subsystem_device; + + mdev->driver_version = LINUX_VERSION_CODE; + + media_device_init(mdev); +#endif +} +EXPORT_SYMBOL_GPL(media_device_pci_init); + +void __media_device_usb_init(struct media_device *mdev, + struct usb_device *udev, + const char *board_name, + const char *driver_name) +{ +#ifdef CONFIG_USB + mdev->dev = &udev->dev; + + if (driver_name) + strlcpy(mdev->driver_name, driver_name, + sizeof(mdev->driver_name)); + + if (board_name) + strlcpy(mdev->model, board_name, sizeof(mdev->model)); + else if (udev->product) + strlcpy(mdev->model, udev->product, sizeof(mdev->model)); + else + strlcpy(mdev->model, "unknown model", sizeof(mdev->model)); + if (udev->serial) + strlcpy(mdev->serial, udev->serial, sizeof(mdev->serial)); + usb_make_path(udev, mdev->bus_info, sizeof(mdev->bus_info)); + mdev->hw_revision = le16_to_cpu(udev->descriptor.bcdDevice); + mdev->driver_version = LINUX_VERSION_CODE; + + media_device_init(mdev); +#endif +} +EXPORT_SYMBOL_GPL(__media_device_usb_init); + + #endif /* CONFIG_MEDIA_CONTROLLER */ diff --git a/drivers/media/media-entity.c b/drivers/media/media-entity.c index f2e43603d6d2..e95070b3a3d4 100644 --- a/drivers/media/media-entity.c +++ b/drivers/media/media-entity.c @@ -46,25 +46,41 @@ static inline const char *intf_type(struct media_interface *intf) { switch (intf->type) { case MEDIA_INTF_T_DVB_FE: - return "frontend"; + return "dvb-frontend"; case MEDIA_INTF_T_DVB_DEMUX: - return "demux"; + return "dvb-demux"; case MEDIA_INTF_T_DVB_DVR: - return "DVR"; + return "dvb-dvr"; case MEDIA_INTF_T_DVB_CA: - return "CA"; + return "dvb-ca"; case MEDIA_INTF_T_DVB_NET: - return "dvbnet"; + return "dvb-net"; case MEDIA_INTF_T_V4L_VIDEO: - return "video"; + return "v4l-video"; case MEDIA_INTF_T_V4L_VBI: - return "vbi"; + return "v4l-vbi"; case MEDIA_INTF_T_V4L_RADIO: - return "radio"; + return "v4l-radio"; case MEDIA_INTF_T_V4L_SUBDEV: - return "v4l2-subdev"; + return "v4l-subdev"; case MEDIA_INTF_T_V4L_SWRADIO: - return "swradio"; + return "v4l-swradio"; + case MEDIA_INTF_T_ALSA_PCM_CAPTURE: + return "alsa-pcm-capture"; + case MEDIA_INTF_T_ALSA_PCM_PLAYBACK: + return "alsa-pcm-playback"; + case MEDIA_INTF_T_ALSA_CONTROL: + return "alsa-control"; + case MEDIA_INTF_T_ALSA_COMPRESS: + return "alsa-compress"; + case MEDIA_INTF_T_ALSA_RAWMIDI: + return "alsa-rawmidi"; + case MEDIA_INTF_T_ALSA_HWDEP: + return "alsa-hwdep"; + case MEDIA_INTF_T_ALSA_SEQUENCER: + return "alsa-sequencer"; + case MEDIA_INTF_T_ALSA_TIMER: + return "alsa-timer"; default: return "unknown-intf"; } @@ -73,8 +89,9 @@ static inline const char *intf_type(struct media_interface *intf) __must_check int __media_entity_enum_init(struct media_entity_enum *ent_enum, int idx_max) { - ent_enum->bmap = kcalloc(DIV_ROUND_UP(idx_max, BITS_PER_LONG), - sizeof(long), GFP_KERNEL); + idx_max = ALIGN(idx_max, BITS_PER_LONG); + ent_enum->bmap = kcalloc(idx_max / BITS_PER_LONG, sizeof(long), + GFP_KERNEL); if (!ent_enum->bmap) return -ENOMEM; @@ -349,8 +366,8 @@ EXPORT_SYMBOL_GPL(media_entity_graph_walk_next); * Pipeline management */ -__must_check int media_entity_pipeline_start(struct media_entity *entity, - struct media_pipeline *pipe) +__must_check int __media_entity_pipeline_start(struct media_entity *entity, + struct media_pipeline *pipe) { struct media_device *mdev = entity->graph_obj.mdev; struct media_entity_graph *graph = &pipe->graph; @@ -358,8 +375,6 @@ __must_check int media_entity_pipeline_start(struct media_entity *entity, struct media_link *link; int ret; - mutex_lock(&mdev->graph_mutex); - if (!pipe->streaming_count++) { ret = media_entity_graph_walk_init(&pipe->graph, mdev); if (ret) @@ -440,8 +455,6 @@ __must_check int media_entity_pipeline_start(struct media_entity *entity, } } - mutex_unlock(&mdev->graph_mutex); - return 0; error: @@ -471,19 +484,28 @@ error_graph_walk_start: if (!--pipe->streaming_count) media_entity_graph_walk_cleanup(graph); - mutex_unlock(&mdev->graph_mutex); + return ret; +} +EXPORT_SYMBOL_GPL(__media_entity_pipeline_start); +__must_check int media_entity_pipeline_start(struct media_entity *entity, + struct media_pipeline *pipe) +{ + struct media_device *mdev = entity->graph_obj.mdev; + int ret; + + mutex_lock(&mdev->graph_mutex); + ret = __media_entity_pipeline_start(entity, pipe); + mutex_unlock(&mdev->graph_mutex); return ret; } EXPORT_SYMBOL_GPL(media_entity_pipeline_start); -void media_entity_pipeline_stop(struct media_entity *entity) +void __media_entity_pipeline_stop(struct media_entity *entity) { - struct media_device *mdev = entity->graph_obj.mdev; struct media_entity_graph *graph = &entity->pipe->graph; struct media_pipeline *pipe = entity->pipe; - mutex_lock(&mdev->graph_mutex); WARN_ON(!pipe->streaming_count); media_entity_graph_walk_start(graph, entity); @@ -500,6 +522,15 @@ void media_entity_pipeline_stop(struct media_entity *entity) if (!--pipe->streaming_count) media_entity_graph_walk_cleanup(graph); +} +EXPORT_SYMBOL_GPL(__media_entity_pipeline_stop); + +void media_entity_pipeline_stop(struct media_entity *entity) +{ + struct media_device *mdev = entity->graph_obj.mdev; + + mutex_lock(&mdev->graph_mutex); + __media_entity_pipeline_stop(entity); mutex_unlock(&mdev->graph_mutex); } EXPORT_SYMBOL_GPL(media_entity_pipeline_stop); @@ -789,6 +820,7 @@ int __media_entity_setup_link(struct media_link *link, u32 flags) return ret; } +EXPORT_SYMBOL_GPL(__media_entity_setup_link); int media_entity_setup_link(struct media_link *link, u32 flags) { diff --git a/drivers/media/pci/bt8xx/bttv-driver.c b/drivers/media/pci/bt8xx/bttv-driver.c index 2c412377507b..df54e17ef864 100644 --- a/drivers/media/pci/bt8xx/bttv-driver.c +++ b/drivers/media/pci/bt8xx/bttv-driver.c @@ -2321,6 +2321,19 @@ static int bttv_g_fmt_vid_overlay(struct file *file, void *priv, return 0; } +static void bttv_get_width_mask_vid_cap(const struct bttv_format *fmt, + unsigned int *width_mask, + unsigned int *width_bias) +{ + if (fmt->flags & FORMAT_FLAGS_PLANAR) { + *width_mask = ~15; /* width must be a multiple of 16 pixels */ + *width_bias = 8; /* nearest */ + } else { + *width_mask = ~3; /* width must be a multiple of 4 pixels */ + *width_bias = 2; /* nearest */ + } +} + static int bttv_try_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { @@ -2330,6 +2343,7 @@ static int bttv_try_fmt_vid_cap(struct file *file, void *priv, enum v4l2_field field; __s32 width, height; __s32 height2; + unsigned int width_mask, width_bias; int rc; fmt = format_by_fourcc(f->fmt.pix.pixelformat); @@ -2362,9 +2376,9 @@ static int bttv_try_fmt_vid_cap(struct file *file, void *priv, width = f->fmt.pix.width; height = f->fmt.pix.height; + bttv_get_width_mask_vid_cap(fmt, &width_mask, &width_bias); rc = limit_scaled_size_lock(fh, &width, &height, field, - /* width_mask: 4 pixels */ ~3, - /* width_bias: nearest */ 2, + width_mask, width_bias, /* adjust_size */ 1, /* adjust_crop */ 0); if (0 != rc) @@ -2397,6 +2411,7 @@ static int bttv_s_fmt_vid_cap(struct file *file, void *priv, struct bttv_fh *fh = priv; struct bttv *btv = fh->btv; __s32 width, height; + unsigned int width_mask, width_bias; enum v4l2_field field; retval = bttv_switch_type(fh, f->type); @@ -2411,9 +2426,10 @@ static int bttv_s_fmt_vid_cap(struct file *file, void *priv, height = f->fmt.pix.height; field = f->fmt.pix.field; + fmt = format_by_fourcc(f->fmt.pix.pixelformat); + bttv_get_width_mask_vid_cap(fmt, &width_mask, &width_bias); retval = limit_scaled_size_lock(fh, &width, &height, f->fmt.pix.field, - /* width_mask: 4 pixels */ ~3, - /* width_bias: nearest */ 2, + width_mask, width_bias, /* adjust_size */ 1, /* adjust_crop */ 1); if (0 != retval) @@ -2421,8 +2437,6 @@ static int bttv_s_fmt_vid_cap(struct file *file, void *priv, f->fmt.pix.field = field; - fmt = format_by_fourcc(f->fmt.pix.pixelformat); - /* update our state informations */ fh->fmt = fmt; fh->cap.field = f->fmt.pix.field; diff --git a/drivers/media/pci/cx23885/cx23885-dvb.c b/drivers/media/pci/cx23885/cx23885-dvb.c index 5131c9f555fb..f041b6931ba8 100644 --- a/drivers/media/pci/cx23885/cx23885-dvb.c +++ b/drivers/media/pci/cx23885/cx23885-dvb.c @@ -1139,7 +1139,7 @@ static int dvb_register_ci_mac(struct cx23885_tsport *port) u8 eeprom[256]; /* 24C02 i2c eeprom */ struct sp2_config sp2_config; struct i2c_board_info info; - struct cx23885_i2c *i2c_bus2 = &dev->i2c_bus[1]; + struct cx23885_i2c *i2c_bus = &dev->i2c_bus[0]; /* attach CI */ memset(&sp2_config, 0, sizeof(sp2_config)); @@ -1151,7 +1151,7 @@ static int dvb_register_ci_mac(struct cx23885_tsport *port) info.addr = 0x40; info.platform_data = &sp2_config; request_module(info.type); - client_ci = i2c_new_device(&i2c_bus2->i2c_adap, &info); + client_ci = i2c_new_device(&i2c_bus->i2c_adap, &info); if (client_ci == NULL || client_ci->dev.driver == NULL) return -ENODEV; if (!try_module_get(client_ci->dev.driver->owner)) { @@ -1988,8 +1988,8 @@ static int dvb_register(struct cx23885_tsport *port) break; case CX23885_BOARD_DVBSKY_T980C: case CX23885_BOARD_TT_CT2_4500_CI: - i2c_bus = &dev->i2c_bus[1]; - i2c_bus2 = &dev->i2c_bus[0]; + i2c_bus = &dev->i2c_bus[0]; + i2c_bus2 = &dev->i2c_bus[1]; /* attach frontend */ memset(&si2168_config, 0, sizeof(si2168_config)); @@ -2001,7 +2001,7 @@ static int dvb_register(struct cx23885_tsport *port) info.addr = 0x64; info.platform_data = &si2168_config; request_module(info.type); - client_demod = i2c_new_device(&i2c_bus->i2c_adap, &info); + client_demod = i2c_new_device(&i2c_bus2->i2c_adap, &info); if (client_demod == NULL || client_demod->dev.driver == NULL) goto frontend_detach; if (!try_module_get(client_demod->dev.driver->owner)) { @@ -2030,13 +2030,13 @@ static int dvb_register(struct cx23885_tsport *port) port->i2c_client_tuner = client_tuner; break; case CX23885_BOARD_DVBSKY_S950C: - i2c_bus = &dev->i2c_bus[1]; - i2c_bus2 = &dev->i2c_bus[0]; + i2c_bus = &dev->i2c_bus[0]; + i2c_bus2 = &dev->i2c_bus[1]; /* attach frontend */ fe0->dvb.frontend = dvb_attach(m88ds3103_attach, &dvbsky_s950c_m88ds3103_config, - &i2c_bus->i2c_adap, &adapter); + &i2c_bus2->i2c_adap, &adapter); if (fe0->dvb.frontend == NULL) break; diff --git a/drivers/media/pci/ivtv/ivtv-queue.c b/drivers/media/pci/ivtv/ivtv-queue.c index 7fde36e6d227..2128c2a8d7fd 100644 --- a/drivers/media/pci/ivtv/ivtv-queue.c +++ b/drivers/media/pci/ivtv/ivtv-queue.c @@ -141,7 +141,7 @@ int ivtv_queue_move(struct ivtv_stream *s, struct ivtv_queue *from, struct ivtv_ spin_unlock_irqrestore(&s->qlock, flags); return -ENOMEM; } - while (bytes_available < needed_bytes) { + while (steal && bytes_available < needed_bytes) { struct ivtv_buffer *buf = list_entry(steal->list.prev, struct ivtv_buffer, list); u16 dma_xfer_cnt = buf->dma_xfer_cnt; diff --git a/drivers/media/pci/ivtv/ivtv-udma.c b/drivers/media/pci/ivtv/ivtv-udma.c index 24152accc66c..4769469fe842 100644 --- a/drivers/media/pci/ivtv/ivtv-udma.c +++ b/drivers/media/pci/ivtv/ivtv-udma.c @@ -124,8 +124,8 @@ int ivtv_udma_setup(struct ivtv *itv, unsigned long ivtv_dest_addr, } /* Get user pages for DMA Xfer */ - err = get_user_pages_unlocked(current, current->mm, - user_dma.uaddr, user_dma.page_count, 0, 1, dma->map); + err = get_user_pages_unlocked(user_dma.uaddr, user_dma.page_count, 0, + 1, dma->map); if (user_dma.page_count != err) { IVTV_DEBUG_WARN("failed to map user pages, returned %d instead of %d\n", diff --git a/drivers/media/pci/ivtv/ivtv-yuv.c b/drivers/media/pci/ivtv/ivtv-yuv.c index 2b8e7b2f2b86..b094054cda6e 100644 --- a/drivers/media/pci/ivtv/ivtv-yuv.c +++ b/drivers/media/pci/ivtv/ivtv-yuv.c @@ -75,14 +75,12 @@ static int ivtv_yuv_prep_user_dma(struct ivtv *itv, struct ivtv_user_dma *dma, ivtv_udma_get_page_info (&uv_dma, (unsigned long)args->uv_source, 360 * uv_decode_height); /* Get user pages for DMA Xfer */ - y_pages = get_user_pages_unlocked(current, current->mm, - y_dma.uaddr, y_dma.page_count, 0, 1, - &dma->map[0]); + y_pages = get_user_pages_unlocked(y_dma.uaddr, + y_dma.page_count, 0, 1, &dma->map[0]); uv_pages = 0; /* silence gcc. value is set and consumed only if: */ if (y_pages == y_dma.page_count) { - uv_pages = get_user_pages_unlocked(current, current->mm, - uv_dma.uaddr, uv_dma.page_count, 0, 1, - &dma->map[y_pages]); + uv_pages = get_user_pages_unlocked(uv_dma.uaddr, + uv_dma.page_count, 0, 1, &dma->map[y_pages]); } if (y_pages != y_dma.page_count || uv_pages != uv_dma.page_count) { diff --git a/drivers/media/pci/pt3/pt3.c b/drivers/media/pci/pt3/pt3.c index 0d2e2b217121..eff5e9f51ace 100644 --- a/drivers/media/pci/pt3/pt3.c +++ b/drivers/media/pci/pt3/pt3.c @@ -395,7 +395,8 @@ static int pt3_attach_fe(struct pt3_board *pt3, int i) if (!try_module_get(cl->dev.driver->owner)) goto err_demod_i2c_unregister_device; - if (!strncmp(cl->name, TC90522_I2C_DEV_SAT, sizeof(cl->name))) { + if (!strncmp(cl->name, TC90522_I2C_DEV_SAT, + strlen(TC90522_I2C_DEV_SAT))) { struct qm1d1c0042_config tcfg; tcfg = adap_conf[i].tuner_cfg.qm1d1c0042; diff --git a/drivers/media/pci/saa7134/saa7134-cards.c b/drivers/media/pci/saa7134/saa7134-cards.c index 9a2fdc78eb85..c480a7e87593 100644 --- a/drivers/media/pci/saa7134/saa7134-cards.c +++ b/drivers/media/pci/saa7134/saa7134-cards.c @@ -5733,7 +5733,36 @@ struct saa7134_board saa7134_boards[] = { .gpio = 0x08, }, }, - + [SAA7134_BOARD_SNAZIO_TVPVR_PRO] = { + .name = "SnaZio* TVPVR PRO", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_TDA8290, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .gpiomask = 1 << 21, + .inputs = { { + .type = SAA7134_INPUT_TV, + .vmux = 1, + .amux = TV, + .gpio = 0x0000000, + }, { + .type = SAA7134_INPUT_COMPOSITE1, + .vmux = 3, + .amux = LINE2, + .gpio = 0x0000000, + }, { + .type = SAA7134_INPUT_SVIDEO, + .vmux = 8, + .amux = LINE2, + .gpio = 0x0000000, + } }, + .radio = { + .type = SAA7134_INPUT_RADIO, + .amux = TV, + .gpio = 0x0200000, + }, + }, }; const unsigned int saa7134_bcount = ARRAY_SIZE(saa7134_boards); @@ -7004,6 +7033,12 @@ struct pci_device_id saa7134_pci_tbl[] = { .subdevice = 0x6f3a, .driver_data = SAA7134_BOARD_LEADTEK_WINFAST_TV2100_FM, }, { + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x1779, /* V One Multimedia PTE Ltd */ + .subdevice = 0x13cf, + .driver_data = SAA7134_BOARD_SNAZIO_TVPVR_PRO, + }, { /* --- boards without eeprom + subsystem ID --- */ .vendor = PCI_VENDOR_ID_PHILIPS, .device = PCI_DEVICE_ID_PHILIPS_SAA7134, @@ -7534,6 +7569,7 @@ int saa7134_board_init1(struct saa7134_dev *dev) case SAA7134_BOARD_BEHOLD_H7: case SAA7134_BOARD_BEHOLD_A7: case SAA7134_BOARD_KWORLD_PC150U: + case SAA7134_BOARD_SNAZIO_TVPVR_PRO: dev->has_remote = SAA7134_REMOTE_I2C; break; case SAA7134_BOARD_AVERMEDIA_A169_B: diff --git a/drivers/media/pci/saa7134/saa7134-core.c b/drivers/media/pci/saa7134/saa7134-core.c index 42bc4172febd..c0e1780ec831 100644 --- a/drivers/media/pci/saa7134/saa7134-core.c +++ b/drivers/media/pci/saa7134/saa7134-core.c @@ -829,18 +829,19 @@ static void saa7134_media_release(struct saa7134_dev *dev) #endif } +#if defined(CONFIG_MEDIA_CONTROLLER) static void saa7134_create_entities(struct saa7134_dev *dev) { -#if defined(CONFIG_MEDIA_CONTROLLER) int ret, i; struct media_entity *entity; struct media_entity *decoder = NULL; /* Check if it is using an external analog TV demod */ media_device_for_each_entity(entity, dev->media_dev) { - if (entity->function == MEDIA_ENT_F_ATV_DECODER) + if (entity->function == MEDIA_ENT_F_ATV_DECODER) { decoder = entity; break; + } } /* @@ -950,8 +951,8 @@ static void saa7134_create_entities(struct saa7134_dev *dev) if (ret < 0) pr_err("failed to register input entity %d!\n", i); } -#endif } +#endif static struct video_device *vdev_init(struct saa7134_dev *dev, struct video_device *template, @@ -1042,11 +1043,12 @@ static int saa7134_initdev(struct pci_dev *pci_dev, sprintf(dev->name, "saa%x[%d]", pci_dev->device, dev->nr); #ifdef CONFIG_MEDIA_CONTROLLER - dev->media_dev = v4l2_mc_pci_media_device_init(pci_dev, dev->name); + dev->media_dev = kzalloc(sizeof(*dev->media_dev), GFP_KERNEL); if (!dev->media_dev) { err = -ENOMEM; goto fail0; } + media_device_pci_init(dev->media_dev, pci_dev, dev->name); dev->v4l2_dev.mdev = dev->media_dev; #endif diff --git a/drivers/media/pci/saa7134/saa7134-input.c b/drivers/media/pci/saa7134/saa7134-input.c index 69d32d3fa32c..c8042c3888cd 100644 --- a/drivers/media/pci/saa7134/saa7134-input.c +++ b/drivers/media/pci/saa7134/saa7134-input.c @@ -975,6 +975,27 @@ void saa7134_probe_i2c_ir(struct saa7134_dev *dev) msg_msi.addr, dev->i2c_adap.name, (1 == rc) ? "yes" : "no"); break; + case SAA7134_BOARD_SNAZIO_TVPVR_PRO: + dev->init_data.name = "SnaZio* TVPVR PRO"; + dev->init_data.get_key = get_key_msi_tvanywhere_plus; + dev->init_data.ir_codes = RC_MAP_MSI_TVANYWHERE_PLUS; + /* + * MSI TV@nyware Plus requires more frequent polling + * otherwise it will miss some keypresses + */ + dev->init_data.polling_interval = 50; + info.addr = 0x30; + /* + * MSI TV@nywhere Plus controller doesn't seem to + * respond to probes unless we read something from + * an existing device. Weird... + * REVISIT: might no longer be needed + */ + rc = i2c_transfer(&dev->i2c_adap, &msg_msi, 1); + input_dbg("probe 0x%02x @ %s: %s\n", + msg_msi.addr, dev->i2c_adap.name, + (rc == 1) ? "yes" : "no"); + break; case SAA7134_BOARD_KWORLD_PC150U: /* copied and modified from MSI TV@nywhere Plus */ dev->init_data.name = "Kworld PC150-U"; diff --git a/drivers/media/pci/saa7134/saa7134.h b/drivers/media/pci/saa7134/saa7134.h index 8936568fab94..69a9bbf22d4d 100644 --- a/drivers/media/pci/saa7134/saa7134.h +++ b/drivers/media/pci/saa7134/saa7134.h @@ -343,6 +343,7 @@ struct saa7134_card_ir { #define SAA7134_BOARD_WIS_VOYAGER 193 #define SAA7134_BOARD_AVERMEDIA_505 194 #define SAA7134_BOARD_LEADTEK_WINFAST_TV2100_FM 195 +#define SAA7134_BOARD_SNAZIO_TVPVR_PRO 196 #define SAA7134_MAXBOARDS 32 #define SAA7134_INPUT_MAX 8 diff --git a/drivers/media/pci/ttpci/av7110.c b/drivers/media/pci/ttpci/av7110.c index 18d229fa65cf..382caf200ba1 100644 --- a/drivers/media/pci/ttpci/av7110.c +++ b/drivers/media/pci/ttpci/av7110.c @@ -2198,13 +2198,18 @@ static int frontend_init(struct av7110 *av7110) break; case 0x0001: // Hauppauge/TT Nexus-T premium rev1.X + { + struct dvb_frontend *fe; + // try ALPS TDLB7 first, then Grundig 29504-401 - av7110->fe = dvb_attach(sp8870_attach, &alps_tdlb7_config, &av7110->i2c_adap); - if (av7110->fe) { - av7110->fe->ops.tuner_ops.set_params = alps_tdlb7_tuner_set_params; + fe = dvb_attach(sp8870_attach, &alps_tdlb7_config, &av7110->i2c_adap); + if (fe) { + fe->ops.tuner_ops.set_params = alps_tdlb7_tuner_set_params; + av7110->fe = fe; break; } - /* fall-thru */ + } + /* fall-thru */ case 0x0008: // Hauppauge/TT DVB-T // Grundig 29504-401 diff --git a/drivers/media/pci/ttpci/budget.c b/drivers/media/pci/ttpci/budget.c index 9f48100227f1..fb8ede5a1531 100644 --- a/drivers/media/pci/ttpci/budget.c +++ b/drivers/media/pci/ttpci/budget.c @@ -615,33 +615,47 @@ static void frontend_init(struct budget *budget) break; case 0x1016: // Hauppauge/TT Nova-S SE (samsung s5h1420/????(tda8260)) - budget->dvb_frontend = dvb_attach(s5h1420_attach, &s5h1420_config, &budget->i2c_adap); - if (budget->dvb_frontend) { - budget->dvb_frontend->ops.tuner_ops.set_params = s5h1420_tuner_set_params; - if (dvb_attach(lnbp21_attach, budget->dvb_frontend, &budget->i2c_adap, 0, 0) == NULL) { + { + struct dvb_frontend *fe; + + fe = dvb_attach(s5h1420_attach, &s5h1420_config, &budget->i2c_adap); + if (fe) { + fe->ops.tuner_ops.set_params = s5h1420_tuner_set_params; + budget->dvb_frontend = fe; + if (dvb_attach(lnbp21_attach, fe, &budget->i2c_adap, + 0, 0) == NULL) { printk("%s: No LNBP21 found!\n", __func__); goto error_out; } break; } - + } + /* fall through */ case 0x1018: // TT Budget-S-1401 (philips tda10086/philips tda8262) + { + struct dvb_frontend *fe; + // gpio2 is connected to CLB - reset it + leave it high saa7146_setgpio(budget->dev, 2, SAA7146_GPIO_OUTLO); msleep(1); saa7146_setgpio(budget->dev, 2, SAA7146_GPIO_OUTHI); msleep(1); - budget->dvb_frontend = dvb_attach(tda10086_attach, &tda10086_config, &budget->i2c_adap); - if (budget->dvb_frontend) { - if (dvb_attach(tda826x_attach, budget->dvb_frontend, 0x60, &budget->i2c_adap, 0) == NULL) + fe = dvb_attach(tda10086_attach, &tda10086_config, &budget->i2c_adap); + if (fe) { + budget->dvb_frontend = fe; + if (dvb_attach(tda826x_attach, fe, 0x60, + &budget->i2c_adap, 0) == NULL) printk("%s: No tda826x found!\n", __func__); - if (dvb_attach(lnbp21_attach, budget->dvb_frontend, &budget->i2c_adap, 0, 0) == NULL) { + if (dvb_attach(lnbp21_attach, fe, + &budget->i2c_adap, 0, 0) == NULL) { printk("%s: No LNBP21 found!\n", __func__); goto error_out; } break; } + } + /* fall through */ case 0x101c: { /* TT S2-1600 */ const struct stv6110x_devctl *ctl; diff --git a/drivers/media/platform/coda/coda-bit.c b/drivers/media/platform/coda/coda-bit.c index 6efe9d002961..b6625047250d 100644 --- a/drivers/media/platform/coda/coda-bit.c +++ b/drivers/media/platform/coda/coda-bit.c @@ -1455,9 +1455,9 @@ static int coda_alloc_bitstream_buffer(struct coda_ctx *ctx, return 0; ctx->bitstream.size = roundup_pow_of_two(q_data->sizeimage * 2); - ctx->bitstream.vaddr = dma_alloc_writecombine( - &ctx->dev->plat_dev->dev, ctx->bitstream.size, - &ctx->bitstream.paddr, GFP_KERNEL); + ctx->bitstream.vaddr = dma_alloc_wc(&ctx->dev->plat_dev->dev, + ctx->bitstream.size, + &ctx->bitstream.paddr, GFP_KERNEL); if (!ctx->bitstream.vaddr) { v4l2_err(&ctx->dev->v4l2_dev, "failed to allocate bitstream ringbuffer"); @@ -1474,8 +1474,8 @@ static void coda_free_bitstream_buffer(struct coda_ctx *ctx) if (ctx->bitstream.vaddr == NULL) return; - dma_free_writecombine(&ctx->dev->plat_dev->dev, ctx->bitstream.size, - ctx->bitstream.vaddr, ctx->bitstream.paddr); + dma_free_wc(&ctx->dev->plat_dev->dev, ctx->bitstream.size, + ctx->bitstream.vaddr, ctx->bitstream.paddr); ctx->bitstream.vaddr = NULL; kfifo_init(&ctx->bitstream_fifo, NULL, 0); } diff --git a/drivers/media/platform/coda/coda-common.c b/drivers/media/platform/coda/coda-common.c index 2d782ce94a67..133ab9f70f85 100644 --- a/drivers/media/platform/coda/coda-common.c +++ b/drivers/media/platform/coda/coda-common.c @@ -1950,16 +1950,76 @@ static int coda_register_device(struct coda_dev *dev, int i) return video_register_device(vfd, VFL_TYPE_GRABBER, 0); } +static void coda_copy_firmware(struct coda_dev *dev, const u8 * const buf, + size_t size) +{ + u32 *src = (u32 *)buf; + + /* Check if the firmware has a 16-byte Freescale header, skip it */ + if (buf[0] == 'M' && buf[1] == 'X') + src += 4; + /* + * Check whether the firmware is in native order or pre-reordered for + * memory access. The first instruction opcode always is 0xe40e. + */ + if (__le16_to_cpup((__le16 *)src) == 0xe40e) { + u32 *dst = dev->codebuf.vaddr; + int i; + + /* Firmware in native order, reorder while copying */ + if (dev->devtype->product == CODA_DX6) { + for (i = 0; i < (size - 16) / 4; i++) + dst[i] = (src[i] << 16) | (src[i] >> 16); + } else { + for (i = 0; i < (size - 16) / 4; i += 2) { + dst[i] = (src[i + 1] << 16) | (src[i + 1] >> 16); + dst[i + 1] = (src[i] << 16) | (src[i] >> 16); + } + } + } else { + /* Copy the already reordered firmware image */ + memcpy(dev->codebuf.vaddr, src, size); + } +} + +static void coda_fw_callback(const struct firmware *fw, void *context); + +static int coda_firmware_request(struct coda_dev *dev) +{ + char *fw = dev->devtype->firmware[dev->firmware]; + + dev_dbg(&dev->plat_dev->dev, "requesting firmware '%s' for %s\n", fw, + coda_product_name(dev->devtype->product)); + + return request_firmware_nowait(THIS_MODULE, true, fw, + &dev->plat_dev->dev, GFP_KERNEL, dev, + coda_fw_callback); +} + static void coda_fw_callback(const struct firmware *fw, void *context) { struct coda_dev *dev = context; struct platform_device *pdev = dev->plat_dev; int i, ret; - if (!fw) { + if (!fw && dev->firmware == 1) { v4l2_err(&dev->v4l2_dev, "firmware request failed\n"); goto put_pm; } + if (!fw) { + dev->firmware = 1; + coda_firmware_request(dev); + return; + } + if (dev->firmware == 1) { + /* + * Since we can't suppress warnings for failed asynchronous + * firmware requests, report that the fallback firmware was + * found. + */ + dev_info(&pdev->dev, "Using fallback firmware %s\n", + dev->devtype->firmware[dev->firmware]); + } /* allocate auxiliary per-device code buffer for the BIT processor */ ret = coda_alloc_aux_buf(dev, &dev->codebuf, fw->size, "codebuf", @@ -1967,8 +2027,7 @@ static void coda_fw_callback(const struct firmware *fw, void *context) if (ret < 0) goto put_pm; - /* Copy the whole firmware image to the code buffer */ - memcpy(dev->codebuf.vaddr, fw->data, fw->size); + coda_copy_firmware(dev, fw->data, fw->size); release_firmware(fw); ret = coda_hw_init(dev); @@ -2019,17 +2078,6 @@ put_pm: pm_runtime_put_sync(&pdev->dev); } -static int coda_firmware_request(struct coda_dev *dev) -{ - char *fw = dev->devtype->firmware; - - dev_dbg(&dev->plat_dev->dev, "requesting firmware '%s' for %s\n", fw, - coda_product_name(dev->devtype->product)); - - return request_firmware_nowait(THIS_MODULE, true, - fw, &dev->plat_dev->dev, GFP_KERNEL, dev, coda_fw_callback); -} - enum coda_platform { CODA_IMX27, CODA_IMX53, @@ -2039,7 +2087,10 @@ enum coda_platform { static const struct coda_devtype coda_devdata[] = { [CODA_IMX27] = { - .firmware = "v4l-codadx6-imx27.bin", + .firmware = { + "vpu_fw_imx27_TO2.bin", + "v4l-codadx6-imx27.bin" + }, .product = CODA_DX6, .codecs = codadx6_codecs, .num_codecs = ARRAY_SIZE(codadx6_codecs), @@ -2049,7 +2100,10 @@ static const struct coda_devtype coda_devdata[] = { .iram_size = 0xb000, }, [CODA_IMX53] = { - .firmware = "v4l-coda7541-imx53.bin", + .firmware = { + "vpu_fw_imx53.bin", + "v4l-coda7541-imx53.bin" + }, .product = CODA_7541, .codecs = coda7_codecs, .num_codecs = ARRAY_SIZE(coda7_codecs), @@ -2060,7 +2114,10 @@ static const struct coda_devtype coda_devdata[] = { .iram_size = 0x14000, }, [CODA_IMX6Q] = { - .firmware = "v4l-coda960-imx6q.bin", + .firmware = { + "vpu_fw_imx6q.bin", + "v4l-coda960-imx6q.bin" + }, .product = CODA_960, .codecs = coda9_codecs, .num_codecs = ARRAY_SIZE(coda9_codecs), @@ -2071,7 +2128,10 @@ static const struct coda_devtype coda_devdata[] = { .iram_size = 0x21000, }, [CODA_IMX6DL] = { - .firmware = "v4l-coda960-imx6dl.bin", + .firmware = { + "vpu_fw_imx6d.bin", + "v4l-coda960-imx6dl.bin" + }, .product = CODA_960, .codecs = coda9_codecs, .num_codecs = ARRAY_SIZE(coda9_codecs), @@ -2118,14 +2178,12 @@ static int coda_probe(struct platform_device *pdev) pdev_id = of_id ? of_id->data : platform_get_device_id(pdev); - if (of_id) { + if (of_id) dev->devtype = of_id->data; - } else if (pdev_id) { + else if (pdev_id) dev->devtype = &coda_devdata[pdev_id->driver_data]; - } else { - ret = -EINVAL; - goto err_v4l2_register; - } + else + return -EINVAL; spin_lock_init(&dev->irqlock); INIT_LIST_HEAD(&dev->instances); diff --git a/drivers/media/platform/coda/coda.h b/drivers/media/platform/coda/coda.h index d08e9843e9f2..8f2c71e06966 100644 --- a/drivers/media/platform/coda/coda.h +++ b/drivers/media/platform/coda/coda.h @@ -50,7 +50,7 @@ enum coda_product { struct coda_video_device; struct coda_devtype { - char *firmware; + char *firmware[2]; enum coda_product product; const struct coda_codec *codecs; unsigned int num_codecs; @@ -74,6 +74,7 @@ struct coda_dev { struct video_device vfd[5]; struct platform_device *plat_dev; const struct coda_devtype *devtype; + int firmware; void __iomem *regs_base; struct clk *clk_per; diff --git a/drivers/media/platform/exynos4-is/fimc-lite.c b/drivers/media/platform/exynos4-is/fimc-lite.c index e85649147dc8..dc1b929f7a33 100644 --- a/drivers/media/platform/exynos4-is/fimc-lite.c +++ b/drivers/media/platform/exynos4-is/fimc-lite.c @@ -992,10 +992,6 @@ static int fimc_lite_link_setup(struct media_entity *entity, switch (local->index) { case FLITE_SD_PAD_SINK: - if (!is_media_entity_v4l2_subdev(remote->entity)) { - ret = -EINVAL; - break; - } if (flags & MEDIA_LNK_FL_ENABLED) { if (fimc->source_subdev_grp_id == 0) fimc->source_subdev_grp_id = sd->grp_id; @@ -1010,19 +1006,15 @@ static int fimc_lite_link_setup(struct media_entity *entity, case FLITE_SD_PAD_SOURCE_DMA: if (!(flags & MEDIA_LNK_FL_ENABLED)) atomic_set(&fimc->out_path, FIMC_IO_NONE); - else if (is_media_entity_v4l2_io(remote->entity)) - atomic_set(&fimc->out_path, FIMC_IO_DMA); else - ret = -EINVAL; + atomic_set(&fimc->out_path, FIMC_IO_DMA); break; case FLITE_SD_PAD_SOURCE_ISP: if (!(flags & MEDIA_LNK_FL_ENABLED)) atomic_set(&fimc->out_path, FIMC_IO_NONE); - else if (is_media_entity_v4l2_subdev(remote->entity)) - atomic_set(&fimc->out_path, FIMC_IO_ISP); else - ret = -EINVAL; + atomic_set(&fimc->out_path, FIMC_IO_ISP); break; default: diff --git a/drivers/media/platform/omap3isp/isp.c b/drivers/media/platform/omap3isp/isp.c index f9e5245f26ac..5d54e2c6c16b 100644 --- a/drivers/media/platform/omap3isp/isp.c +++ b/drivers/media/platform/omap3isp/isp.c @@ -64,6 +64,7 @@ #include <media/v4l2-common.h> #include <media/v4l2-device.h> +#include <media/v4l2-mc.h> #include <media/v4l2-of.h> #include "isp.h" @@ -657,216 +658,6 @@ static irqreturn_t isp_isr(int irq, void *_isp) } /* ----------------------------------------------------------------------------- - * Pipeline power management - * - * Entities must be powered up when part of a pipeline that contains at least - * one open video device node. - * - * To achieve this use the entity use_count field to track the number of users. - * For entities corresponding to video device nodes the use_count field stores - * the users count of the node. For entities corresponding to subdevs the - * use_count field stores the total number of users of all video device nodes - * in the pipeline. - * - * The omap3isp_pipeline_pm_use() function must be called in the open() and - * close() handlers of video device nodes. It increments or decrements the use - * count of all subdev entities in the pipeline. - * - * To react to link management on powered pipelines, the link setup notification - * callback updates the use count of all entities in the source and sink sides - * of the link. - */ - -/* - * isp_pipeline_pm_use_count - Count the number of users of a pipeline - * @entity: The entity - * - * Return the total number of users of all video device nodes in the pipeline. - */ -static int isp_pipeline_pm_use_count(struct media_entity *entity, - struct media_entity_graph *graph) -{ - int use = 0; - - media_entity_graph_walk_start(graph, entity); - - while ((entity = media_entity_graph_walk_next(graph))) { - if (is_media_entity_v4l2_io(entity)) - use += entity->use_count; - } - - return use; -} - -/* - * isp_pipeline_pm_power_one - Apply power change to an entity - * @entity: The entity - * @change: Use count change - * - * Change the entity use count by @change. If the entity is a subdev update its - * power state by calling the core::s_power operation when the use count goes - * from 0 to != 0 or from != 0 to 0. - * - * Return 0 on success or a negative error code on failure. - */ -static int isp_pipeline_pm_power_one(struct media_entity *entity, int change) -{ - struct v4l2_subdev *subdev; - int ret; - - subdev = is_media_entity_v4l2_subdev(entity) - ? media_entity_to_v4l2_subdev(entity) : NULL; - - if (entity->use_count == 0 && change > 0 && subdev != NULL) { - ret = v4l2_subdev_call(subdev, core, s_power, 1); - if (ret < 0 && ret != -ENOIOCTLCMD) - return ret; - } - - entity->use_count += change; - WARN_ON(entity->use_count < 0); - - if (entity->use_count == 0 && change < 0 && subdev != NULL) - v4l2_subdev_call(subdev, core, s_power, 0); - - return 0; -} - -/* - * isp_pipeline_pm_power - Apply power change to all entities in a pipeline - * @entity: The entity - * @change: Use count change - * - * Walk the pipeline to update the use count and the power state of all non-node - * entities. - * - * Return 0 on success or a negative error code on failure. - */ -static int isp_pipeline_pm_power(struct media_entity *entity, int change, - struct media_entity_graph *graph) -{ - struct media_entity *first = entity; - int ret = 0; - - if (!change) - return 0; - - media_entity_graph_walk_start(graph, entity); - - while (!ret && (entity = media_entity_graph_walk_next(graph))) - if (is_media_entity_v4l2_subdev(entity)) - ret = isp_pipeline_pm_power_one(entity, change); - - if (!ret) - return ret; - - media_entity_graph_walk_start(graph, first); - - while ((first = media_entity_graph_walk_next(graph)) - && first != entity) - if (is_media_entity_v4l2_subdev(first)) - isp_pipeline_pm_power_one(first, -change); - - return ret; -} - -/* - * omap3isp_pipeline_pm_use - Update the use count of an entity - * @entity: The entity - * @use: Use (1) or stop using (0) the entity - * - * Update the use count of all entities in the pipeline and power entities on or - * off accordingly. - * - * Return 0 on success or a negative error code on failure. Powering entities - * off is assumed to never fail. No failure can occur when the use parameter is - * set to 0. - */ -int omap3isp_pipeline_pm_use(struct media_entity *entity, int use, - struct media_entity_graph *graph) -{ - int change = use ? 1 : -1; - int ret; - - mutex_lock(&entity->graph_obj.mdev->graph_mutex); - - /* Apply use count to node. */ - entity->use_count += change; - WARN_ON(entity->use_count < 0); - - /* Apply power change to connected non-nodes. */ - ret = isp_pipeline_pm_power(entity, change, graph); - if (ret < 0) - entity->use_count -= change; - - mutex_unlock(&entity->graph_obj.mdev->graph_mutex); - - return ret; -} - -/* - * isp_pipeline_link_notify - Link management notification callback - * @link: The link - * @flags: New link flags that will be applied - * @notification: The link's state change notification type (MEDIA_DEV_NOTIFY_*) - * - * React to link management on powered pipelines by updating the use count of - * all entities in the source and sink sides of the link. Entities are powered - * on or off accordingly. - * - * Return 0 on success or a negative error code on failure. Powering entities - * off is assumed to never fail. This function will not fail for disconnection - * events. - */ -static int isp_pipeline_link_notify(struct media_link *link, u32 flags, - unsigned int notification) -{ - struct media_entity_graph *graph = - &container_of(link->graph_obj.mdev, struct isp_device, - media_dev)->pm_count_graph; - struct media_entity *source = link->source->entity; - struct media_entity *sink = link->sink->entity; - int source_use; - int sink_use; - int ret = 0; - - if (notification == MEDIA_DEV_NOTIFY_PRE_LINK_CH) { - ret = media_entity_graph_walk_init(graph, - link->graph_obj.mdev); - if (ret) - return ret; - } - - source_use = isp_pipeline_pm_use_count(source, graph); - sink_use = isp_pipeline_pm_use_count(sink, graph); - - if (notification == MEDIA_DEV_NOTIFY_POST_LINK_CH && - !(flags & MEDIA_LNK_FL_ENABLED)) { - /* Powering off entities is assumed to never fail. */ - isp_pipeline_pm_power(source, -sink_use, graph); - isp_pipeline_pm_power(sink, -source_use, graph); - return 0; - } - - if (notification == MEDIA_DEV_NOTIFY_PRE_LINK_CH && - (flags & MEDIA_LNK_FL_ENABLED)) { - - ret = isp_pipeline_pm_power(source, sink_use, graph); - if (ret < 0) - return ret; - - ret = isp_pipeline_pm_power(sink, source_use, graph); - if (ret < 0) - isp_pipeline_pm_power(source, -sink_use, graph); - } - - if (notification == MEDIA_DEV_NOTIFY_POST_LINK_CH) - media_entity_graph_walk_cleanup(graph); - - return ret; -} - -/* ----------------------------------------------------------------------------- * Pipeline stream management */ @@ -1889,7 +1680,7 @@ static int isp_register_entities(struct isp_device *isp) strlcpy(isp->media_dev.model, "TI OMAP3 ISP", sizeof(isp->media_dev.model)); isp->media_dev.hw_revision = isp->revision; - isp->media_dev.link_notify = isp_pipeline_link_notify; + isp->media_dev.link_notify = v4l2_pipeline_link_notify; media_device_init(&isp->media_dev); isp->v4l2_dev.mdev = &isp->media_dev; diff --git a/drivers/media/platform/omap3isp/isp.h b/drivers/media/platform/omap3isp/isp.h index 49b7f71ac968..7e6f6638433b 100644 --- a/drivers/media/platform/omap3isp/isp.h +++ b/drivers/media/platform/omap3isp/isp.h @@ -177,7 +177,6 @@ struct isp_device { struct v4l2_device v4l2_dev; struct v4l2_async_notifier notifier; struct media_device media_dev; - struct media_entity_graph pm_count_graph; struct device *dev; u32 revision; @@ -267,9 +266,6 @@ void omap3isp_subclk_enable(struct isp_device *isp, void omap3isp_subclk_disable(struct isp_device *isp, enum isp_subclk_resource res); -int omap3isp_pipeline_pm_use(struct media_entity *entity, int use, - struct media_entity_graph *graph); - int omap3isp_register_entities(struct platform_device *pdev, struct v4l2_device *v4l2_dev); void omap3isp_unregister_entities(struct platform_device *pdev); diff --git a/drivers/media/platform/omap3isp/ispvideo.c b/drivers/media/platform/omap3isp/ispvideo.c index 2aff755ff77c..ac76d2901501 100644 --- a/drivers/media/platform/omap3isp/ispvideo.c +++ b/drivers/media/platform/omap3isp/ispvideo.c @@ -22,8 +22,10 @@ #include <linux/sched.h> #include <linux/slab.h> #include <linux/vmalloc.h> + #include <media/v4l2-dev.h> #include <media/v4l2-ioctl.h> +#include <media/v4l2-mc.h> #include <media/videobuf2-dma-contig.h> #include "ispvideo.h" @@ -1292,12 +1294,7 @@ static int isp_video_open(struct file *file) goto done; } - ret = media_entity_graph_walk_init(&handle->graph, - &video->isp->media_dev); - if (ret) - goto done; - - ret = omap3isp_pipeline_pm_use(&video->video.entity, 1, &handle->graph); + ret = v4l2_pipeline_pm_use(&video->video.entity, 1); if (ret < 0) { omap3isp_put(video->isp); goto done; @@ -1328,7 +1325,6 @@ static int isp_video_open(struct file *file) done: if (ret < 0) { v4l2_fh_del(&handle->vfh); - media_entity_graph_walk_cleanup(&handle->graph); kfree(handle); } @@ -1348,8 +1344,7 @@ static int isp_video_release(struct file *file) vb2_queue_release(&handle->queue); mutex_unlock(&video->queue_lock); - omap3isp_pipeline_pm_use(&video->video.entity, 0, &handle->graph); - media_entity_graph_walk_cleanup(&handle->graph); + v4l2_pipeline_pm_use(&video->video.entity, 0); /* Release the file handle. */ v4l2_fh_del(vfh); diff --git a/drivers/media/platform/omap3isp/ispvideo.h b/drivers/media/platform/omap3isp/ispvideo.h index 156429878d64..6a48d5879c56 100644 --- a/drivers/media/platform/omap3isp/ispvideo.h +++ b/drivers/media/platform/omap3isp/ispvideo.h @@ -189,7 +189,6 @@ struct isp_video_fh { struct vb2_queue queue; struct v4l2_format format; struct v4l2_fract timeperframe; - struct media_entity_graph graph; }; #define to_isp_video_fh(fh) container_of(fh, struct isp_video_fh, vfh) diff --git a/drivers/media/platform/soc_camera/Kconfig b/drivers/media/platform/soc_camera/Kconfig index f2776cd415ca..355298989dd8 100644 --- a/drivers/media/platform/soc_camera/Kconfig +++ b/drivers/media/platform/soc_camera/Kconfig @@ -17,19 +17,11 @@ config SOC_CAMERA_PLATFORM help This is a generic SoC camera platform driver, useful for testing -config VIDEO_MX3 - tristate "i.MX3x Camera Sensor Interface driver" - depends on VIDEO_DEV && MX3_IPU && SOC_CAMERA - depends on MX3_IPU || COMPILE_TEST - depends on HAS_DMA - select VIDEOBUF2_DMA_CONTIG - ---help--- - This is a v4l2 driver for the i.MX3x Camera Sensor Interface - config VIDEO_PXA27x tristate "PXA27x Quick Capture Interface driver" depends on VIDEO_DEV && PXA27x && SOC_CAMERA select VIDEOBUF_DMA_SG + select SG_SPLIT ---help--- This is a v4l2 driver for the PXA27x Quick Capture Interface @@ -60,25 +52,6 @@ config VIDEO_SH_MOBILE_CEU ---help--- This is a v4l2 driver for the SuperH Mobile CEU Interface -config VIDEO_OMAP1 - tristate "OMAP1 Camera Interface driver" - depends on VIDEO_DEV && SOC_CAMERA - depends on ARCH_OMAP1 - depends on HAS_DMA - select VIDEOBUF_DMA_CONTIG - select VIDEOBUF_DMA_SG - ---help--- - This is a v4l2 driver for the TI OMAP1 camera interface - -config VIDEO_MX2 - tristate "i.MX27 Camera Sensor Interface driver" - depends on VIDEO_DEV && SOC_CAMERA - depends on SOC_IMX27 || COMPILE_TEST - depends on HAS_DMA - select VIDEOBUF2_DMA_CONTIG - ---help--- - This is a v4l2 driver for the i.MX27 Camera Sensor Interface - config VIDEO_ATMEL_ISI tristate "ATMEL Image Sensor Interface (ISI) support" depends on VIDEO_DEV && SOC_CAMERA diff --git a/drivers/media/platform/soc_camera/Makefile b/drivers/media/platform/soc_camera/Makefile index 2826382dc9f8..7ee71ae231c7 100644 --- a/drivers/media/platform/soc_camera/Makefile +++ b/drivers/media/platform/soc_camera/Makefile @@ -7,9 +7,6 @@ obj-$(CONFIG_SOC_CAMERA_PLATFORM) += soc_camera_platform.o # soc-camera host drivers have to be linked after camera drivers obj-$(CONFIG_VIDEO_ATMEL_ISI) += atmel-isi.o -obj-$(CONFIG_VIDEO_MX2) += mx2_camera.o -obj-$(CONFIG_VIDEO_MX3) += mx3_camera.o -obj-$(CONFIG_VIDEO_OMAP1) += omap1_camera.o obj-$(CONFIG_VIDEO_PXA27x) += pxa_camera.o obj-$(CONFIG_VIDEO_SH_MOBILE_CEU) += sh_mobile_ceu_camera.o obj-$(CONFIG_VIDEO_SH_MOBILE_CSI2) += sh_mobile_csi2.o diff --git a/drivers/media/platform/soc_camera/atmel-isi.c b/drivers/media/platform/soc_camera/atmel-isi.c index 1af779ee3c74..ab2d9b9b1f5d 100644 --- a/drivers/media/platform/soc_camera/atmel-isi.c +++ b/drivers/media/platform/soc_camera/atmel-isi.c @@ -1026,7 +1026,7 @@ static int atmel_isi_parse_dt(struct atmel_isi *isi, static int atmel_isi_probe(struct platform_device *pdev) { - unsigned int irq; + int irq; struct atmel_isi *isi; struct resource *regs; int ret, i; @@ -1086,7 +1086,7 @@ static int atmel_isi_probe(struct platform_device *pdev) isi->width_flags |= 1 << 9; irq = platform_get_irq(pdev, 0); - if (IS_ERR_VALUE(irq)) { + if (irq < 0) { ret = irq; goto err_req_irq; } diff --git a/drivers/media/platform/soc_camera/mx2_camera.c b/drivers/media/platform/soc_camera/mx2_camera.c deleted file mode 100644 index 48dd5b7851b5..000000000000 --- a/drivers/media/platform/soc_camera/mx2_camera.c +++ /dev/null @@ -1,1636 +0,0 @@ -/* - * V4L2 Driver for i.MX27 camera host - * - * Copyright (C) 2008, Sascha Hauer, Pengutronix - * Copyright (C) 2010, Baruch Siach, Orex Computed Radiography - * Copyright (C) 2012, Javier Martin, Vista Silicon S.L. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - */ - -#include <linux/init.h> -#include <linux/module.h> -#include <linux/io.h> -#include <linux/delay.h> -#include <linux/slab.h> -#include <linux/dma-mapping.h> -#include <linux/errno.h> -#include <linux/fs.h> -#include <linux/gcd.h> -#include <linux/interrupt.h> -#include <linux/kernel.h> -#include <linux/math64.h> -#include <linux/mm.h> -#include <linux/moduleparam.h> -#include <linux/time.h> -#include <linux/device.h> -#include <linux/platform_device.h> -#include <linux/clk.h> - -#include <media/v4l2-common.h> -#include <media/v4l2-dev.h> -#include <media/videobuf2-v4l2.h> -#include <media/videobuf2-dma-contig.h> -#include <media/soc_camera.h> -#include <media/drv-intf/soc_mediabus.h> - -#include <linux/videodev2.h> - -#include <linux/platform_data/media/camera-mx2.h> - -#include <asm/dma.h> - -#define MX2_CAM_DRV_NAME "mx2-camera" -#define MX2_CAM_VERSION "0.0.6" -#define MX2_CAM_DRIVER_DESCRIPTION "i.MX2x_Camera" - -/* reset values */ -#define CSICR1_RESET_VAL 0x40000800 -#define CSICR2_RESET_VAL 0x0 -#define CSICR3_RESET_VAL 0x0 - -/* csi control reg 1 */ -#define CSICR1_SWAP16_EN (1 << 31) -#define CSICR1_EXT_VSYNC (1 << 30) -#define CSICR1_EOF_INTEN (1 << 29) -#define CSICR1_PRP_IF_EN (1 << 28) -#define CSICR1_CCIR_MODE (1 << 27) -#define CSICR1_COF_INTEN (1 << 26) -#define CSICR1_SF_OR_INTEN (1 << 25) -#define CSICR1_RF_OR_INTEN (1 << 24) -#define CSICR1_STATFF_LEVEL (3 << 22) -#define CSICR1_STATFF_INTEN (1 << 21) -#define CSICR1_RXFF_LEVEL(l) (((l) & 3) << 19) -#define CSICR1_RXFF_INTEN (1 << 18) -#define CSICR1_SOF_POL (1 << 17) -#define CSICR1_SOF_INTEN (1 << 16) -#define CSICR1_MCLKDIV(d) (((d) & 0xF) << 12) -#define CSICR1_HSYNC_POL (1 << 11) -#define CSICR1_CCIR_EN (1 << 10) -#define CSICR1_MCLKEN (1 << 9) -#define CSICR1_FCC (1 << 8) -#define CSICR1_PACK_DIR (1 << 7) -#define CSICR1_CLR_STATFIFO (1 << 6) -#define CSICR1_CLR_RXFIFO (1 << 5) -#define CSICR1_GCLK_MODE (1 << 4) -#define CSICR1_INV_DATA (1 << 3) -#define CSICR1_INV_PCLK (1 << 2) -#define CSICR1_REDGE (1 << 1) -#define CSICR1_FMT_MASK (CSICR1_PACK_DIR | CSICR1_SWAP16_EN) - -#define SHIFT_STATFF_LEVEL 22 -#define SHIFT_RXFF_LEVEL 19 -#define SHIFT_MCLKDIV 12 - -#define SHIFT_FRMCNT 16 - -#define CSICR1 0x00 -#define CSICR2 0x04 -#define CSISR 0x08 -#define CSISTATFIFO 0x0c -#define CSIRFIFO 0x10 -#define CSIRXCNT 0x14 -#define CSICR3 0x1c -#define CSIDMASA_STATFIFO 0x20 -#define CSIDMATA_STATFIFO 0x24 -#define CSIDMASA_FB1 0x28 -#define CSIDMASA_FB2 0x2c -#define CSIFBUF_PARA 0x30 -#define CSIIMAG_PARA 0x34 - -/* EMMA PrP */ -#define PRP_CNTL 0x00 -#define PRP_INTR_CNTL 0x04 -#define PRP_INTRSTATUS 0x08 -#define PRP_SOURCE_Y_PTR 0x0c -#define PRP_SOURCE_CB_PTR 0x10 -#define PRP_SOURCE_CR_PTR 0x14 -#define PRP_DEST_RGB1_PTR 0x18 -#define PRP_DEST_RGB2_PTR 0x1c -#define PRP_DEST_Y_PTR 0x20 -#define PRP_DEST_CB_PTR 0x24 -#define PRP_DEST_CR_PTR 0x28 -#define PRP_SRC_FRAME_SIZE 0x2c -#define PRP_DEST_CH1_LINE_STRIDE 0x30 -#define PRP_SRC_PIXEL_FORMAT_CNTL 0x34 -#define PRP_CH1_PIXEL_FORMAT_CNTL 0x38 -#define PRP_CH1_OUT_IMAGE_SIZE 0x3c -#define PRP_CH2_OUT_IMAGE_SIZE 0x40 -#define PRP_SRC_LINE_STRIDE 0x44 -#define PRP_CSC_COEF_012 0x48 -#define PRP_CSC_COEF_345 0x4c -#define PRP_CSC_COEF_678 0x50 -#define PRP_CH1_RZ_HORI_COEF1 0x54 -#define PRP_CH1_RZ_HORI_COEF2 0x58 -#define PRP_CH1_RZ_HORI_VALID 0x5c -#define PRP_CH1_RZ_VERT_COEF1 0x60 -#define PRP_CH1_RZ_VERT_COEF2 0x64 -#define PRP_CH1_RZ_VERT_VALID 0x68 -#define PRP_CH2_RZ_HORI_COEF1 0x6c -#define PRP_CH2_RZ_HORI_COEF2 0x70 -#define PRP_CH2_RZ_HORI_VALID 0x74 -#define PRP_CH2_RZ_VERT_COEF1 0x78 -#define PRP_CH2_RZ_VERT_COEF2 0x7c -#define PRP_CH2_RZ_VERT_VALID 0x80 - -#define PRP_CNTL_CH1EN (1 << 0) -#define PRP_CNTL_CH2EN (1 << 1) -#define PRP_CNTL_CSIEN (1 << 2) -#define PRP_CNTL_DATA_IN_YUV420 (0 << 3) -#define PRP_CNTL_DATA_IN_YUV422 (1 << 3) -#define PRP_CNTL_DATA_IN_RGB16 (2 << 3) -#define PRP_CNTL_DATA_IN_RGB32 (3 << 3) -#define PRP_CNTL_CH1_OUT_RGB8 (0 << 5) -#define PRP_CNTL_CH1_OUT_RGB16 (1 << 5) -#define PRP_CNTL_CH1_OUT_RGB32 (2 << 5) -#define PRP_CNTL_CH1_OUT_YUV422 (3 << 5) -#define PRP_CNTL_CH2_OUT_YUV420 (0 << 7) -#define PRP_CNTL_CH2_OUT_YUV422 (1 << 7) -#define PRP_CNTL_CH2_OUT_YUV444 (2 << 7) -#define PRP_CNTL_CH1_LEN (1 << 9) -#define PRP_CNTL_CH2_LEN (1 << 10) -#define PRP_CNTL_SKIP_FRAME (1 << 11) -#define PRP_CNTL_SWRST (1 << 12) -#define PRP_CNTL_CLKEN (1 << 13) -#define PRP_CNTL_WEN (1 << 14) -#define PRP_CNTL_CH1BYP (1 << 15) -#define PRP_CNTL_IN_TSKIP(x) ((x) << 16) -#define PRP_CNTL_CH1_TSKIP(x) ((x) << 19) -#define PRP_CNTL_CH2_TSKIP(x) ((x) << 22) -#define PRP_CNTL_INPUT_FIFO_LEVEL(x) ((x) << 25) -#define PRP_CNTL_RZ_FIFO_LEVEL(x) ((x) << 27) -#define PRP_CNTL_CH2B1EN (1 << 29) -#define PRP_CNTL_CH2B2EN (1 << 30) -#define PRP_CNTL_CH2FEN (1 << 31) - -/* IRQ Enable and status register */ -#define PRP_INTR_RDERR (1 << 0) -#define PRP_INTR_CH1WERR (1 << 1) -#define PRP_INTR_CH2WERR (1 << 2) -#define PRP_INTR_CH1FC (1 << 3) -#define PRP_INTR_CH2FC (1 << 5) -#define PRP_INTR_LBOVF (1 << 7) -#define PRP_INTR_CH2OVF (1 << 8) - -/* Resizing registers */ -#define PRP_RZ_VALID_TBL_LEN(x) ((x) << 24) -#define PRP_RZ_VALID_BILINEAR (1 << 31) - -#define MAX_VIDEO_MEM 16 - -#define RESIZE_NUM_MIN 1 -#define RESIZE_NUM_MAX 20 -#define BC_COEF 3 -#define SZ_COEF (1 << BC_COEF) - -#define RESIZE_DIR_H 0 -#define RESIZE_DIR_V 1 - -#define RESIZE_ALGO_BILINEAR 0 -#define RESIZE_ALGO_AVERAGING 1 - -struct mx2_prp_cfg { - int channel; - u32 in_fmt; - u32 out_fmt; - u32 src_pixel; - u32 ch1_pixel; - u32 irq_flags; - u32 csicr1; -}; - -/* prp resizing parameters */ -struct emma_prp_resize { - int algo; /* type of algorithm used */ - int len; /* number of coefficients */ - unsigned char s[RESIZE_NUM_MAX]; /* table of coefficients */ -}; - -/* prp configuration for a client-host fmt pair */ -struct mx2_fmt_cfg { - u32 in_fmt; - u32 out_fmt; - struct mx2_prp_cfg cfg; -}; - -struct mx2_buf_internal { - struct list_head queue; - int bufnum; - bool discard; -}; - -/* buffer for one video frame */ -struct mx2_buffer { - /* common v4l buffer stuff -- must be first */ - struct vb2_v4l2_buffer vb; - struct mx2_buf_internal internal; -}; - -enum mx2_camera_type { - IMX27_CAMERA, -}; - -struct mx2_camera_dev { - struct device *dev; - struct soc_camera_host soc_host; - struct clk *clk_emma_ahb, *clk_emma_ipg; - struct clk *clk_csi_ahb, *clk_csi_per; - - void __iomem *base_csi, *base_emma; - - struct mx2_camera_platform_data *pdata; - unsigned long platform_flags; - - struct list_head capture; - struct list_head active_bufs; - struct list_head discard; - - spinlock_t lock; - - int dma; - struct mx2_buffer *active; - struct mx2_buffer *fb1_active; - struct mx2_buffer *fb2_active; - - u32 csicr1; - enum mx2_camera_type devtype; - - struct mx2_buf_internal buf_discard[2]; - void *discard_buffer; - dma_addr_t discard_buffer_dma; - size_t discard_size; - struct mx2_fmt_cfg *emma_prp; - struct emma_prp_resize resizing[2]; - unsigned int s_width, s_height; - u32 frame_count; - struct vb2_alloc_ctx *alloc_ctx; -}; - -static struct platform_device_id mx2_camera_devtype[] = { - { - .name = "imx27-camera", - .driver_data = IMX27_CAMERA, - }, { - /* sentinel */ - } -}; -MODULE_DEVICE_TABLE(platform, mx2_camera_devtype); - -static struct mx2_buffer *mx2_ibuf_to_buf(struct mx2_buf_internal *int_buf) -{ - return container_of(int_buf, struct mx2_buffer, internal); -} - -static struct mx2_fmt_cfg mx27_emma_prp_table[] = { - /* - * This is a generic configuration which is valid for most - * prp input-output format combinations. - * We set the incoming and outgoing pixelformat to a - * 16 Bit wide format and adjust the bytesperline - * accordingly. With this configuration the inputdata - * will not be changed by the emma and could be any type - * of 16 Bit Pixelformat. - */ - { - .in_fmt = 0, - .out_fmt = 0, - .cfg = { - .channel = 1, - .in_fmt = PRP_CNTL_DATA_IN_RGB16, - .out_fmt = PRP_CNTL_CH1_OUT_RGB16, - .src_pixel = 0x2ca00565, /* RGB565 */ - .ch1_pixel = 0x2ca00565, /* RGB565 */ - .irq_flags = PRP_INTR_RDERR | PRP_INTR_CH1WERR | - PRP_INTR_CH1FC | PRP_INTR_LBOVF, - .csicr1 = 0, - } - }, - { - .in_fmt = MEDIA_BUS_FMT_UYVY8_2X8, - .out_fmt = V4L2_PIX_FMT_YUYV, - .cfg = { - .channel = 1, - .in_fmt = PRP_CNTL_DATA_IN_YUV422, - .out_fmt = PRP_CNTL_CH1_OUT_YUV422, - .src_pixel = 0x22000888, /* YUV422 (YUYV) */ - .ch1_pixel = 0x62000888, /* YUV422 (YUYV) */ - .irq_flags = PRP_INTR_RDERR | PRP_INTR_CH1WERR | - PRP_INTR_CH1FC | PRP_INTR_LBOVF, - .csicr1 = CSICR1_SWAP16_EN, - } - }, - { - .in_fmt = MEDIA_BUS_FMT_YUYV8_2X8, - .out_fmt = V4L2_PIX_FMT_YUYV, - .cfg = { - .channel = 1, - .in_fmt = PRP_CNTL_DATA_IN_YUV422, - .out_fmt = PRP_CNTL_CH1_OUT_YUV422, - .src_pixel = 0x22000888, /* YUV422 (YUYV) */ - .ch1_pixel = 0x62000888, /* YUV422 (YUYV) */ - .irq_flags = PRP_INTR_RDERR | PRP_INTR_CH1WERR | - PRP_INTR_CH1FC | PRP_INTR_LBOVF, - .csicr1 = CSICR1_PACK_DIR, - } - }, - { - .in_fmt = MEDIA_BUS_FMT_YUYV8_2X8, - .out_fmt = V4L2_PIX_FMT_YUV420, - .cfg = { - .channel = 2, - .in_fmt = PRP_CNTL_DATA_IN_YUV422, - .out_fmt = PRP_CNTL_CH2_OUT_YUV420, - .src_pixel = 0x22000888, /* YUV422 (YUYV) */ - .irq_flags = PRP_INTR_RDERR | PRP_INTR_CH2WERR | - PRP_INTR_CH2FC | PRP_INTR_LBOVF | - PRP_INTR_CH2OVF, - .csicr1 = CSICR1_PACK_DIR, - } - }, - { - .in_fmt = MEDIA_BUS_FMT_UYVY8_2X8, - .out_fmt = V4L2_PIX_FMT_YUV420, - .cfg = { - .channel = 2, - .in_fmt = PRP_CNTL_DATA_IN_YUV422, - .out_fmt = PRP_CNTL_CH2_OUT_YUV420, - .src_pixel = 0x22000888, /* YUV422 (YUYV) */ - .irq_flags = PRP_INTR_RDERR | PRP_INTR_CH2WERR | - PRP_INTR_CH2FC | PRP_INTR_LBOVF | - PRP_INTR_CH2OVF, - .csicr1 = CSICR1_SWAP16_EN, - } - }, -}; - -static struct mx2_fmt_cfg *mx27_emma_prp_get_format(u32 in_fmt, u32 out_fmt) -{ - int i; - - for (i = 1; i < ARRAY_SIZE(mx27_emma_prp_table); i++) - if ((mx27_emma_prp_table[i].in_fmt == in_fmt) && - (mx27_emma_prp_table[i].out_fmt == out_fmt)) { - return &mx27_emma_prp_table[i]; - } - /* If no match return the most generic configuration */ - return &mx27_emma_prp_table[0]; -}; - -static void mx27_update_emma_buf(struct mx2_camera_dev *pcdev, - unsigned long phys, int bufnum) -{ - struct mx2_fmt_cfg *prp = pcdev->emma_prp; - - if (prp->cfg.channel == 1) { - writel(phys, pcdev->base_emma + - PRP_DEST_RGB1_PTR + 4 * bufnum); - } else { - writel(phys, pcdev->base_emma + - PRP_DEST_Y_PTR - 0x14 * bufnum); - if (prp->out_fmt == V4L2_PIX_FMT_YUV420) { - u32 imgsize = pcdev->soc_host.icd->user_height * - pcdev->soc_host.icd->user_width; - - writel(phys + imgsize, pcdev->base_emma + - PRP_DEST_CB_PTR - 0x14 * bufnum); - writel(phys + ((5 * imgsize) / 4), pcdev->base_emma + - PRP_DEST_CR_PTR - 0x14 * bufnum); - } - } -} - -static void mx2_camera_deactivate(struct mx2_camera_dev *pcdev) -{ - clk_disable_unprepare(pcdev->clk_csi_ahb); - clk_disable_unprepare(pcdev->clk_csi_per); - writel(0, pcdev->base_csi + CSICR1); - writel(0, pcdev->base_emma + PRP_CNTL); -} - -static int mx2_camera_add_device(struct soc_camera_device *icd) -{ - dev_info(icd->parent, "Camera driver attached to camera %d\n", - icd->devnum); - - return 0; -} - -static void mx2_camera_remove_device(struct soc_camera_device *icd) -{ - dev_info(icd->parent, "Camera driver detached from camera %d\n", - icd->devnum); -} - -/* - * The following two functions absolutely depend on the fact, that - * there can be only one camera on mx2 camera sensor interface - */ -static int mx2_camera_clock_start(struct soc_camera_host *ici) -{ - struct mx2_camera_dev *pcdev = ici->priv; - int ret; - u32 csicr1; - - ret = clk_prepare_enable(pcdev->clk_csi_ahb); - if (ret < 0) - return ret; - - ret = clk_prepare_enable(pcdev->clk_csi_per); - if (ret < 0) - goto exit_csi_ahb; - - csicr1 = CSICR1_MCLKEN | CSICR1_PRP_IF_EN | CSICR1_FCC | - CSICR1_RXFF_LEVEL(0); - - pcdev->csicr1 = csicr1; - writel(pcdev->csicr1, pcdev->base_csi + CSICR1); - - pcdev->frame_count = 0; - - return 0; - -exit_csi_ahb: - clk_disable_unprepare(pcdev->clk_csi_ahb); - - return ret; -} - -static void mx2_camera_clock_stop(struct soc_camera_host *ici) -{ - struct mx2_camera_dev *pcdev = ici->priv; - - mx2_camera_deactivate(pcdev); -} - -/* - * Videobuf operations - */ -static int mx2_videobuf_setup(struct vb2_queue *vq, - unsigned int *count, unsigned int *num_planes, - unsigned int sizes[], void *alloc_ctxs[]) -{ - struct soc_camera_device *icd = soc_camera_from_vb2q(vq); - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - struct mx2_camera_dev *pcdev = ici->priv; - - dev_dbg(icd->parent, "count=%d, size=%d\n", *count, sizes[0]); - - alloc_ctxs[0] = pcdev->alloc_ctx; - - sizes[0] = icd->sizeimage; - - if (0 == *count) - *count = 32; - if (!*num_planes && - sizes[0] * *count > MAX_VIDEO_MEM * 1024 * 1024) - *count = (MAX_VIDEO_MEM * 1024 * 1024) / sizes[0]; - - *num_planes = 1; - - return 0; -} - -static int mx2_videobuf_prepare(struct vb2_buffer *vb) -{ - struct soc_camera_device *icd = soc_camera_from_vb2q(vb->vb2_queue); - int ret = 0; - - dev_dbg(icd->parent, "%s (vb=0x%p) 0x%p %lu\n", __func__, - vb, vb2_plane_vaddr(vb, 0), vb2_get_plane_payload(vb, 0)); - -#ifdef DEBUG - /* - * This can be useful if you want to see if we actually fill - * the buffer with something - */ - memset((void *)vb2_plane_vaddr(vb, 0), - 0xaa, vb2_get_plane_payload(vb, 0)); -#endif - - vb2_set_plane_payload(vb, 0, icd->sizeimage); - if (vb2_plane_vaddr(vb, 0) && - vb2_get_plane_payload(vb, 0) > vb2_plane_size(vb, 0)) { - ret = -EINVAL; - goto out; - } - - return 0; - -out: - return ret; -} - -static void mx2_videobuf_queue(struct vb2_buffer *vb) -{ - struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); - struct soc_camera_device *icd = soc_camera_from_vb2q(vb->vb2_queue); - struct soc_camera_host *ici = - to_soc_camera_host(icd->parent); - struct mx2_camera_dev *pcdev = ici->priv; - struct mx2_buffer *buf = container_of(vbuf, struct mx2_buffer, vb); - unsigned long flags; - - dev_dbg(icd->parent, "%s (vb=0x%p) 0x%p %lu\n", __func__, - vb, vb2_plane_vaddr(vb, 0), vb2_get_plane_payload(vb, 0)); - - spin_lock_irqsave(&pcdev->lock, flags); - - list_add_tail(&buf->internal.queue, &pcdev->capture); - - spin_unlock_irqrestore(&pcdev->lock, flags); -} - -static void mx27_camera_emma_buf_init(struct soc_camera_device *icd, - int bytesperline) -{ - struct soc_camera_host *ici = - to_soc_camera_host(icd->parent); - struct mx2_camera_dev *pcdev = ici->priv; - struct mx2_fmt_cfg *prp = pcdev->emma_prp; - - writel((pcdev->s_width << 16) | pcdev->s_height, - pcdev->base_emma + PRP_SRC_FRAME_SIZE); - writel(prp->cfg.src_pixel, - pcdev->base_emma + PRP_SRC_PIXEL_FORMAT_CNTL); - if (prp->cfg.channel == 1) { - writel((icd->user_width << 16) | icd->user_height, - pcdev->base_emma + PRP_CH1_OUT_IMAGE_SIZE); - writel(bytesperline, - pcdev->base_emma + PRP_DEST_CH1_LINE_STRIDE); - writel(prp->cfg.ch1_pixel, - pcdev->base_emma + PRP_CH1_PIXEL_FORMAT_CNTL); - } else { /* channel 2 */ - writel((icd->user_width << 16) | icd->user_height, - pcdev->base_emma + PRP_CH2_OUT_IMAGE_SIZE); - } - - /* Enable interrupts */ - writel(prp->cfg.irq_flags, pcdev->base_emma + PRP_INTR_CNTL); -} - -static void mx2_prp_resize_commit(struct mx2_camera_dev *pcdev) -{ - int dir; - - for (dir = RESIZE_DIR_H; dir <= RESIZE_DIR_V; dir++) { - unsigned char *s = pcdev->resizing[dir].s; - int len = pcdev->resizing[dir].len; - unsigned int coeff[2] = {0, 0}; - unsigned int valid = 0; - int i; - - if (len == 0) - continue; - - for (i = RESIZE_NUM_MAX - 1; i >= 0; i--) { - int j; - - j = i > 9 ? 1 : 0; - coeff[j] = (coeff[j] << BC_COEF) | - (s[i] & (SZ_COEF - 1)); - - if (i == 5 || i == 15) - coeff[j] <<= 1; - - valid = (valid << 1) | (s[i] >> BC_COEF); - } - - valid |= PRP_RZ_VALID_TBL_LEN(len); - - if (pcdev->resizing[dir].algo == RESIZE_ALGO_BILINEAR) - valid |= PRP_RZ_VALID_BILINEAR; - - if (pcdev->emma_prp->cfg.channel == 1) { - if (dir == RESIZE_DIR_H) { - writel(coeff[0], pcdev->base_emma + - PRP_CH1_RZ_HORI_COEF1); - writel(coeff[1], pcdev->base_emma + - PRP_CH1_RZ_HORI_COEF2); - writel(valid, pcdev->base_emma + - PRP_CH1_RZ_HORI_VALID); - } else { - writel(coeff[0], pcdev->base_emma + - PRP_CH1_RZ_VERT_COEF1); - writel(coeff[1], pcdev->base_emma + - PRP_CH1_RZ_VERT_COEF2); - writel(valid, pcdev->base_emma + - PRP_CH1_RZ_VERT_VALID); - } - } else { - if (dir == RESIZE_DIR_H) { - writel(coeff[0], pcdev->base_emma + - PRP_CH2_RZ_HORI_COEF1); - writel(coeff[1], pcdev->base_emma + - PRP_CH2_RZ_HORI_COEF2); - writel(valid, pcdev->base_emma + - PRP_CH2_RZ_HORI_VALID); - } else { - writel(coeff[0], pcdev->base_emma + - PRP_CH2_RZ_VERT_COEF1); - writel(coeff[1], pcdev->base_emma + - PRP_CH2_RZ_VERT_COEF2); - writel(valid, pcdev->base_emma + - PRP_CH2_RZ_VERT_VALID); - } - } - } -} - -static int mx2_start_streaming(struct vb2_queue *q, unsigned int count) -{ - struct soc_camera_device *icd = soc_camera_from_vb2q(q); - struct soc_camera_host *ici = - to_soc_camera_host(icd->parent); - struct mx2_camera_dev *pcdev = ici->priv; - struct mx2_fmt_cfg *prp = pcdev->emma_prp; - struct vb2_buffer *vb; - struct mx2_buffer *buf; - unsigned long phys; - int bytesperline; - unsigned long flags; - - if (count < 2) - return -ENOBUFS; - - spin_lock_irqsave(&pcdev->lock, flags); - - buf = list_first_entry(&pcdev->capture, struct mx2_buffer, - internal.queue); - buf->internal.bufnum = 0; - vb = &buf->vb.vb2_buf; - - phys = vb2_dma_contig_plane_dma_addr(vb, 0); - mx27_update_emma_buf(pcdev, phys, buf->internal.bufnum); - list_move_tail(pcdev->capture.next, &pcdev->active_bufs); - - buf = list_first_entry(&pcdev->capture, struct mx2_buffer, - internal.queue); - buf->internal.bufnum = 1; - vb = &buf->vb.vb2_buf; - - phys = vb2_dma_contig_plane_dma_addr(vb, 0); - mx27_update_emma_buf(pcdev, phys, buf->internal.bufnum); - list_move_tail(pcdev->capture.next, &pcdev->active_bufs); - - bytesperline = soc_mbus_bytes_per_line(icd->user_width, - icd->current_fmt->host_fmt); - if (bytesperline < 0) { - spin_unlock_irqrestore(&pcdev->lock, flags); - return bytesperline; - } - - /* - * I didn't manage to properly enable/disable the prp - * on a per frame basis during running transfers, - * thus we allocate a buffer here and use it to - * discard frames when no buffer is available. - * Feel free to work on this ;) - */ - pcdev->discard_size = icd->user_height * bytesperline; - pcdev->discard_buffer = dma_alloc_coherent(ici->v4l2_dev.dev, - pcdev->discard_size, - &pcdev->discard_buffer_dma, GFP_ATOMIC); - if (!pcdev->discard_buffer) { - spin_unlock_irqrestore(&pcdev->lock, flags); - return -ENOMEM; - } - - pcdev->buf_discard[0].discard = true; - list_add_tail(&pcdev->buf_discard[0].queue, - &pcdev->discard); - - pcdev->buf_discard[1].discard = true; - list_add_tail(&pcdev->buf_discard[1].queue, - &pcdev->discard); - - mx2_prp_resize_commit(pcdev); - - mx27_camera_emma_buf_init(icd, bytesperline); - - if (prp->cfg.channel == 1) { - writel(PRP_CNTL_CH1EN | - PRP_CNTL_CSIEN | - prp->cfg.in_fmt | - prp->cfg.out_fmt | - PRP_CNTL_CH1_LEN | - PRP_CNTL_CH1BYP | - PRP_CNTL_CH1_TSKIP(0) | - PRP_CNTL_IN_TSKIP(0), - pcdev->base_emma + PRP_CNTL); - } else { - writel(PRP_CNTL_CH2EN | - PRP_CNTL_CSIEN | - prp->cfg.in_fmt | - prp->cfg.out_fmt | - PRP_CNTL_CH2_LEN | - PRP_CNTL_CH2_TSKIP(0) | - PRP_CNTL_IN_TSKIP(0), - pcdev->base_emma + PRP_CNTL); - } - spin_unlock_irqrestore(&pcdev->lock, flags); - - return 0; -} - -static void mx2_stop_streaming(struct vb2_queue *q) -{ - struct soc_camera_device *icd = soc_camera_from_vb2q(q); - struct soc_camera_host *ici = - to_soc_camera_host(icd->parent); - struct mx2_camera_dev *pcdev = ici->priv; - struct mx2_fmt_cfg *prp = pcdev->emma_prp; - unsigned long flags; - void *b; - u32 cntl; - - spin_lock_irqsave(&pcdev->lock, flags); - - cntl = readl(pcdev->base_emma + PRP_CNTL); - if (prp->cfg.channel == 1) { - writel(cntl & ~PRP_CNTL_CH1EN, - pcdev->base_emma + PRP_CNTL); - } else { - writel(cntl & ~PRP_CNTL_CH2EN, - pcdev->base_emma + PRP_CNTL); - } - INIT_LIST_HEAD(&pcdev->capture); - INIT_LIST_HEAD(&pcdev->active_bufs); - INIT_LIST_HEAD(&pcdev->discard); - - b = pcdev->discard_buffer; - pcdev->discard_buffer = NULL; - - spin_unlock_irqrestore(&pcdev->lock, flags); - - dma_free_coherent(ici->v4l2_dev.dev, - pcdev->discard_size, b, pcdev->discard_buffer_dma); -} - -static struct vb2_ops mx2_videobuf_ops = { - .queue_setup = mx2_videobuf_setup, - .buf_prepare = mx2_videobuf_prepare, - .buf_queue = mx2_videobuf_queue, - .start_streaming = mx2_start_streaming, - .stop_streaming = mx2_stop_streaming, -}; - -static int mx2_camera_init_videobuf(struct vb2_queue *q, - struct soc_camera_device *icd) -{ - q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - q->io_modes = VB2_MMAP | VB2_USERPTR; - q->drv_priv = icd; - q->ops = &mx2_videobuf_ops; - q->mem_ops = &vb2_dma_contig_memops; - q->buf_struct_size = sizeof(struct mx2_buffer); - q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; - - return vb2_queue_init(q); -} - -#define MX2_BUS_FLAGS (V4L2_MBUS_MASTER | \ - V4L2_MBUS_VSYNC_ACTIVE_HIGH | \ - V4L2_MBUS_VSYNC_ACTIVE_LOW | \ - V4L2_MBUS_HSYNC_ACTIVE_HIGH | \ - V4L2_MBUS_HSYNC_ACTIVE_LOW | \ - V4L2_MBUS_PCLK_SAMPLE_RISING | \ - V4L2_MBUS_PCLK_SAMPLE_FALLING | \ - V4L2_MBUS_DATA_ACTIVE_HIGH | \ - V4L2_MBUS_DATA_ACTIVE_LOW) - -static int mx27_camera_emma_prp_reset(struct mx2_camera_dev *pcdev) -{ - int count = 0; - - readl(pcdev->base_emma + PRP_CNTL); - writel(PRP_CNTL_SWRST, pcdev->base_emma + PRP_CNTL); - while (count++ < 100) { - if (!(readl(pcdev->base_emma + PRP_CNTL) & PRP_CNTL_SWRST)) - return 0; - barrier(); - udelay(1); - } - - return -ETIMEDOUT; -} - -static int mx2_camera_set_bus_param(struct soc_camera_device *icd) -{ - struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - struct mx2_camera_dev *pcdev = ici->priv; - struct v4l2_mbus_config cfg = {.type = V4L2_MBUS_PARALLEL,}; - unsigned long common_flags; - int ret; - int bytesperline; - u32 csicr1 = pcdev->csicr1; - - ret = v4l2_subdev_call(sd, video, g_mbus_config, &cfg); - if (!ret) { - common_flags = soc_mbus_config_compatible(&cfg, MX2_BUS_FLAGS); - if (!common_flags) { - dev_warn(icd->parent, - "Flags incompatible: camera 0x%x, host 0x%x\n", - cfg.flags, MX2_BUS_FLAGS); - return -EINVAL; - } - } else if (ret != -ENOIOCTLCMD) { - return ret; - } else { - common_flags = MX2_BUS_FLAGS; - } - - if ((common_flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH) && - (common_flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)) { - if (pcdev->platform_flags & MX2_CAMERA_HSYNC_HIGH) - common_flags &= ~V4L2_MBUS_HSYNC_ACTIVE_LOW; - else - common_flags &= ~V4L2_MBUS_HSYNC_ACTIVE_HIGH; - } - - if ((common_flags & V4L2_MBUS_PCLK_SAMPLE_RISING) && - (common_flags & V4L2_MBUS_PCLK_SAMPLE_FALLING)) { - if (pcdev->platform_flags & MX2_CAMERA_PCLK_SAMPLE_RISING) - common_flags &= ~V4L2_MBUS_PCLK_SAMPLE_FALLING; - else - common_flags &= ~V4L2_MBUS_PCLK_SAMPLE_RISING; - } - - cfg.flags = common_flags; - ret = v4l2_subdev_call(sd, video, s_mbus_config, &cfg); - if (ret < 0 && ret != -ENOIOCTLCMD) { - dev_dbg(icd->parent, "camera s_mbus_config(0x%lx) returned %d\n", - common_flags, ret); - return ret; - } - - csicr1 = (csicr1 & ~CSICR1_FMT_MASK) | pcdev->emma_prp->cfg.csicr1; - - if (common_flags & V4L2_MBUS_PCLK_SAMPLE_RISING) - csicr1 |= CSICR1_REDGE; - if (common_flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH) - csicr1 |= CSICR1_SOF_POL; - if (common_flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH) - csicr1 |= CSICR1_HSYNC_POL; - if (pcdev->platform_flags & MX2_CAMERA_EXT_VSYNC) - csicr1 |= CSICR1_EXT_VSYNC; - if (pcdev->platform_flags & MX2_CAMERA_CCIR) - csicr1 |= CSICR1_CCIR_EN; - if (pcdev->platform_flags & MX2_CAMERA_CCIR_INTERLACE) - csicr1 |= CSICR1_CCIR_MODE; - if (pcdev->platform_flags & MX2_CAMERA_GATED_CLOCK) - csicr1 |= CSICR1_GCLK_MODE; - if (pcdev->platform_flags & MX2_CAMERA_INV_DATA) - csicr1 |= CSICR1_INV_DATA; - - pcdev->csicr1 = csicr1; - - bytesperline = soc_mbus_bytes_per_line(icd->user_width, - icd->current_fmt->host_fmt); - if (bytesperline < 0) - return bytesperline; - - ret = mx27_camera_emma_prp_reset(pcdev); - if (ret) - return ret; - - writel(pcdev->csicr1, pcdev->base_csi + CSICR1); - - return 0; -} - -static int mx2_camera_set_crop(struct soc_camera_device *icd, - const struct v4l2_crop *a) -{ - struct v4l2_crop a_writable = *a; - struct v4l2_rect *rect = &a_writable.c; - struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - struct v4l2_subdev_format fmt = { - .which = V4L2_SUBDEV_FORMAT_ACTIVE, - }; - struct v4l2_mbus_framefmt *mf = &fmt.format; - int ret; - - soc_camera_limit_side(&rect->left, &rect->width, 0, 2, 4096); - soc_camera_limit_side(&rect->top, &rect->height, 0, 2, 4096); - - ret = v4l2_subdev_call(sd, video, s_crop, a); - if (ret < 0) - return ret; - - /* The capture device might have changed its output */ - ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &fmt); - if (ret < 0) - return ret; - - dev_dbg(icd->parent, "Sensor cropped %dx%d\n", - mf->width, mf->height); - - icd->user_width = mf->width; - icd->user_height = mf->height; - - return ret; -} - -static int mx2_camera_get_formats(struct soc_camera_device *icd, - unsigned int idx, - struct soc_camera_format_xlate *xlate) -{ - struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - const struct soc_mbus_pixelfmt *fmt; - struct device *dev = icd->parent; - struct v4l2_subdev_mbus_code_enum code = { - .which = V4L2_SUBDEV_FORMAT_ACTIVE, - .index = idx, - }; - int ret, formats = 0; - - ret = v4l2_subdev_call(sd, pad, enum_mbus_code, NULL, &code); - if (ret < 0) - /* no more formats */ - return 0; - - fmt = soc_mbus_get_fmtdesc(code.code); - if (!fmt) { - dev_err(dev, "Invalid format code #%u: %d\n", idx, code.code); - return 0; - } - - if (code.code == MEDIA_BUS_FMT_YUYV8_2X8 || - code.code == MEDIA_BUS_FMT_UYVY8_2X8) { - formats++; - if (xlate) { - /* - * CH2 can output YUV420 which is a standard format in - * soc_mediabus.c - */ - xlate->host_fmt = - soc_mbus_get_fmtdesc(MEDIA_BUS_FMT_YUYV8_1_5X8); - xlate->code = code.code; - dev_dbg(dev, "Providing host format %s for sensor code %d\n", - xlate->host_fmt->name, code.code); - xlate++; - } - } - - if (code.code == MEDIA_BUS_FMT_UYVY8_2X8) { - formats++; - if (xlate) { - xlate->host_fmt = - soc_mbus_get_fmtdesc(MEDIA_BUS_FMT_YUYV8_2X8); - xlate->code = code.code; - dev_dbg(dev, "Providing host format %s for sensor code %d\n", - xlate->host_fmt->name, code.code); - xlate++; - } - } - - /* Generic pass-trough */ - formats++; - if (xlate) { - xlate->host_fmt = fmt; - xlate->code = code.code; - xlate++; - } - return formats; -} - -static int mx2_emmaprp_resize(struct mx2_camera_dev *pcdev, - struct v4l2_mbus_framefmt *mf_in, - struct v4l2_pix_format *pix_out, bool apply) -{ - unsigned int num, den; - unsigned long m; - int i, dir; - - for (dir = RESIZE_DIR_H; dir <= RESIZE_DIR_V; dir++) { - struct emma_prp_resize tmprsz; - unsigned char *s = tmprsz.s; - int len = 0; - int in, out; - - if (dir == RESIZE_DIR_H) { - in = mf_in->width; - out = pix_out->width; - } else { - in = mf_in->height; - out = pix_out->height; - } - - if (in < out) - return -EINVAL; - else if (in == out) - continue; - - /* Calculate ratio */ - m = gcd(in, out); - num = in / m; - den = out / m; - if (num > RESIZE_NUM_MAX) - return -EINVAL; - - if ((num >= 2 * den) && (den == 1) && - (num < 9) && (!(num & 0x01))) { - int sum = 0; - int j; - - /* Average scaling for >= 2:1 ratios */ - /* Support can be added for num >=9 and odd values */ - - tmprsz.algo = RESIZE_ALGO_AVERAGING; - len = num; - - for (i = 0; i < (len / 2); i++) - s[i] = 8; - - do { - for (i = 0; i < (len / 2); i++) { - s[i] = s[i] >> 1; - sum = 0; - for (j = 0; j < (len / 2); j++) - sum += s[j]; - if (sum == 4) - break; - } - } while (sum != 4); - - for (i = (len / 2); i < len; i++) - s[i] = s[len - i - 1]; - - s[len - 1] |= SZ_COEF; - } else { - /* bilinear scaling for < 2:1 ratios */ - int v; /* overflow counter */ - int coeff, nxt; /* table output */ - int in_pos_inc = 2 * den; - int out_pos = num; - int out_pos_inc = 2 * num; - int init_carry = num - den; - int carry = init_carry; - - tmprsz.algo = RESIZE_ALGO_BILINEAR; - v = den + in_pos_inc; - do { - coeff = v - out_pos; - out_pos += out_pos_inc; - carry += out_pos_inc; - for (nxt = 0; v < out_pos; nxt++) { - v += in_pos_inc; - carry -= in_pos_inc; - } - - if (len > RESIZE_NUM_MAX) - return -EINVAL; - - coeff = ((coeff << BC_COEF) + - (in_pos_inc >> 1)) / in_pos_inc; - - if (coeff >= (SZ_COEF - 1)) - coeff--; - - coeff |= SZ_COEF; - s[len] = (unsigned char)coeff; - len++; - - for (i = 1; i < nxt; i++) { - if (len >= RESIZE_NUM_MAX) - return -EINVAL; - s[len] = 0; - len++; - } - } while (carry != init_carry); - } - tmprsz.len = len; - if (dir == RESIZE_DIR_H) - mf_in->width = pix_out->width; - else - mf_in->height = pix_out->height; - - if (apply) - memcpy(&pcdev->resizing[dir], &tmprsz, sizeof(tmprsz)); - } - return 0; -} - -static int mx2_camera_set_fmt(struct soc_camera_device *icd, - struct v4l2_format *f) -{ - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - struct mx2_camera_dev *pcdev = ici->priv; - struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - const struct soc_camera_format_xlate *xlate; - struct v4l2_pix_format *pix = &f->fmt.pix; - struct v4l2_subdev_format format = { - .which = V4L2_SUBDEV_FORMAT_ACTIVE, - }; - struct v4l2_mbus_framefmt *mf = &format.format; - int ret; - - dev_dbg(icd->parent, "%s: requested params: width = %d, height = %d\n", - __func__, pix->width, pix->height); - - xlate = soc_camera_xlate_by_fourcc(icd, pix->pixelformat); - if (!xlate) { - dev_warn(icd->parent, "Format %x not found\n", - pix->pixelformat); - return -EINVAL; - } - - mf->width = pix->width; - mf->height = pix->height; - mf->field = pix->field; - mf->colorspace = pix->colorspace; - mf->code = xlate->code; - - ret = v4l2_subdev_call(sd, pad, set_fmt, NULL, &format); - if (ret < 0 && ret != -ENOIOCTLCMD) - return ret; - - /* Store width and height returned by the sensor for resizing */ - pcdev->s_width = mf->width; - pcdev->s_height = mf->height; - dev_dbg(icd->parent, "%s: sensor params: width = %d, height = %d\n", - __func__, pcdev->s_width, pcdev->s_height); - - pcdev->emma_prp = mx27_emma_prp_get_format(xlate->code, - xlate->host_fmt->fourcc); - - memset(pcdev->resizing, 0, sizeof(pcdev->resizing)); - if ((mf->width != pix->width || mf->height != pix->height) && - pcdev->emma_prp->cfg.in_fmt == PRP_CNTL_DATA_IN_YUV422) { - if (mx2_emmaprp_resize(pcdev, mf, pix, true) < 0) - dev_dbg(icd->parent, "%s: can't resize\n", __func__); - } - - if (mf->code != xlate->code) - return -EINVAL; - - pix->width = mf->width; - pix->height = mf->height; - pix->field = mf->field; - pix->colorspace = mf->colorspace; - icd->current_fmt = xlate; - - dev_dbg(icd->parent, "%s: returned params: width = %d, height = %d\n", - __func__, pix->width, pix->height); - - return 0; -} - -static int mx2_camera_try_fmt(struct soc_camera_device *icd, - struct v4l2_format *f) -{ - struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - const struct soc_camera_format_xlate *xlate; - struct v4l2_pix_format *pix = &f->fmt.pix; - struct v4l2_subdev_pad_config pad_cfg; - struct v4l2_subdev_format format = { - .which = V4L2_SUBDEV_FORMAT_TRY, - }; - struct v4l2_mbus_framefmt *mf = &format.format; - __u32 pixfmt = pix->pixelformat; - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - struct mx2_camera_dev *pcdev = ici->priv; - struct mx2_fmt_cfg *emma_prp; - int ret; - - dev_dbg(icd->parent, "%s: requested params: width = %d, height = %d\n", - __func__, pix->width, pix->height); - - xlate = soc_camera_xlate_by_fourcc(icd, pixfmt); - if (pixfmt && !xlate) { - dev_warn(icd->parent, "Format %x not found\n", pixfmt); - return -EINVAL; - } - - /* - * limit to MX27 hardware capabilities: width must be a multiple of 8 as - * requested by the CSI. (Table 39-2 in the i.MX27 Reference Manual). - */ - pix->width &= ~0x7; - - /* limit to sensor capabilities */ - mf->width = pix->width; - mf->height = pix->height; - mf->field = pix->field; - mf->colorspace = pix->colorspace; - mf->code = xlate->code; - - ret = v4l2_subdev_call(sd, pad, set_fmt, &pad_cfg, &format); - if (ret < 0) - return ret; - - dev_dbg(icd->parent, "%s: sensor params: width = %d, height = %d\n", - __func__, pcdev->s_width, pcdev->s_height); - - /* If the sensor does not support image size try PrP resizing */ - emma_prp = mx27_emma_prp_get_format(xlate->code, - xlate->host_fmt->fourcc); - - if ((mf->width != pix->width || mf->height != pix->height) && - emma_prp->cfg.in_fmt == PRP_CNTL_DATA_IN_YUV422) { - if (mx2_emmaprp_resize(pcdev, mf, pix, false) < 0) - dev_dbg(icd->parent, "%s: can't resize\n", __func__); - } - - if (mf->field == V4L2_FIELD_ANY) - mf->field = V4L2_FIELD_NONE; - /* - * Driver supports interlaced images provided they have - * both fields so that they can be processed as if they - * were progressive. - */ - if (mf->field != V4L2_FIELD_NONE && !V4L2_FIELD_HAS_BOTH(mf->field)) { - dev_err(icd->parent, "Field type %d unsupported.\n", - mf->field); - return -EINVAL; - } - - pix->width = mf->width; - pix->height = mf->height; - pix->field = mf->field; - pix->colorspace = mf->colorspace; - - dev_dbg(icd->parent, "%s: returned params: width = %d, height = %d\n", - __func__, pix->width, pix->height); - - return 0; -} - -static int mx2_camera_querycap(struct soc_camera_host *ici, - struct v4l2_capability *cap) -{ - /* cap->name is set by the friendly caller:-> */ - strlcpy(cap->card, MX2_CAM_DRIVER_DESCRIPTION, sizeof(cap->card)); - cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; - cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; - - return 0; -} - -static unsigned int mx2_camera_poll(struct file *file, poll_table *pt) -{ - struct soc_camera_device *icd = file->private_data; - - return vb2_poll(&icd->vb2_vidq, file, pt); -} - -static struct soc_camera_host_ops mx2_soc_camera_host_ops = { - .owner = THIS_MODULE, - .add = mx2_camera_add_device, - .remove = mx2_camera_remove_device, - .clock_start = mx2_camera_clock_start, - .clock_stop = mx2_camera_clock_stop, - .set_fmt = mx2_camera_set_fmt, - .set_crop = mx2_camera_set_crop, - .get_formats = mx2_camera_get_formats, - .try_fmt = mx2_camera_try_fmt, - .init_videobuf2 = mx2_camera_init_videobuf, - .poll = mx2_camera_poll, - .querycap = mx2_camera_querycap, - .set_bus_param = mx2_camera_set_bus_param, -}; - -static void mx27_camera_frame_done_emma(struct mx2_camera_dev *pcdev, - int bufnum, bool err) -{ -#ifdef DEBUG - struct mx2_fmt_cfg *prp = pcdev->emma_prp; -#endif - struct mx2_buf_internal *ibuf; - struct mx2_buffer *buf; - struct vb2_buffer *vb; - struct vb2_v4l2_buffer *vbuf; - unsigned long phys; - - ibuf = list_first_entry(&pcdev->active_bufs, struct mx2_buf_internal, - queue); - - BUG_ON(ibuf->bufnum != bufnum); - - if (ibuf->discard) { - /* - * Discard buffer must not be returned to user space. - * Just return it to the discard queue. - */ - list_move_tail(pcdev->active_bufs.next, &pcdev->discard); - } else { - buf = mx2_ibuf_to_buf(ibuf); - - vb = &buf->vb.vb2_buf; - vbuf = to_vb2_v4l2_buffer(vb); -#ifdef DEBUG - phys = vb2_dma_contig_plane_dma_addr(vb, 0); - if (prp->cfg.channel == 1) { - if (readl(pcdev->base_emma + PRP_DEST_RGB1_PTR + - 4 * bufnum) != phys) { - dev_err(pcdev->dev, "%lx != %x\n", phys, - readl(pcdev->base_emma + - PRP_DEST_RGB1_PTR + 4 * bufnum)); - } - } else { - if (readl(pcdev->base_emma + PRP_DEST_Y_PTR - - 0x14 * bufnum) != phys) { - dev_err(pcdev->dev, "%lx != %x\n", phys, - readl(pcdev->base_emma + - PRP_DEST_Y_PTR - 0x14 * bufnum)); - } - } -#endif - dev_dbg(pcdev->dev, "%s (vb=0x%p) 0x%p %lu\n", __func__, vb, - vb2_plane_vaddr(vb, 0), - vb2_get_plane_payload(vb, 0)); - - list_del_init(&buf->internal.queue); - vb->timestamp = ktime_get_ns(); - vbuf->sequence = pcdev->frame_count; - if (err) - vb2_buffer_done(vb, VB2_BUF_STATE_ERROR); - else - vb2_buffer_done(vb, VB2_BUF_STATE_DONE); - } - - pcdev->frame_count++; - - if (list_empty(&pcdev->capture)) { - if (list_empty(&pcdev->discard)) { - dev_warn(pcdev->dev, "%s: trying to access empty discard list\n", - __func__); - return; - } - - ibuf = list_first_entry(&pcdev->discard, - struct mx2_buf_internal, queue); - ibuf->bufnum = bufnum; - - list_move_tail(pcdev->discard.next, &pcdev->active_bufs); - mx27_update_emma_buf(pcdev, pcdev->discard_buffer_dma, bufnum); - return; - } - - buf = list_first_entry(&pcdev->capture, struct mx2_buffer, - internal.queue); - - buf->internal.bufnum = bufnum; - - list_move_tail(pcdev->capture.next, &pcdev->active_bufs); - - vb = &buf->vb.vb2_buf; - - phys = vb2_dma_contig_plane_dma_addr(vb, 0); - mx27_update_emma_buf(pcdev, phys, bufnum); -} - -static irqreturn_t mx27_camera_emma_irq(int irq_emma, void *data) -{ - struct mx2_camera_dev *pcdev = data; - unsigned int status = readl(pcdev->base_emma + PRP_INTRSTATUS); - struct mx2_buf_internal *ibuf; - - spin_lock(&pcdev->lock); - - if (list_empty(&pcdev->active_bufs)) { - dev_warn(pcdev->dev, "%s: called while active list is empty\n", - __func__); - - if (!status) { - spin_unlock(&pcdev->lock); - return IRQ_NONE; - } - } - - if (status & (1 << 7)) { /* overflow */ - u32 cntl = readl(pcdev->base_emma + PRP_CNTL); - writel(cntl & ~(PRP_CNTL_CH1EN | PRP_CNTL_CH2EN), - pcdev->base_emma + PRP_CNTL); - writel(cntl, pcdev->base_emma + PRP_CNTL); - - ibuf = list_first_entry(&pcdev->active_bufs, - struct mx2_buf_internal, queue); - mx27_camera_frame_done_emma(pcdev, - ibuf->bufnum, true); - - status &= ~(1 << 7); - } else if (((status & (3 << 5)) == (3 << 5)) || - ((status & (3 << 3)) == (3 << 3))) { - /* - * Both buffers have triggered, process the one we're expecting - * to first - */ - ibuf = list_first_entry(&pcdev->active_bufs, - struct mx2_buf_internal, queue); - mx27_camera_frame_done_emma(pcdev, ibuf->bufnum, false); - status &= ~(1 << (6 - ibuf->bufnum)); /* mark processed */ - } else if ((status & (1 << 6)) || (status & (1 << 4))) { - mx27_camera_frame_done_emma(pcdev, 0, false); - } else if ((status & (1 << 5)) || (status & (1 << 3))) { - mx27_camera_frame_done_emma(pcdev, 1, false); - } - - spin_unlock(&pcdev->lock); - writel(status, pcdev->base_emma + PRP_INTRSTATUS); - - return IRQ_HANDLED; -} - -static int mx27_camera_emma_init(struct platform_device *pdev) -{ - struct mx2_camera_dev *pcdev = platform_get_drvdata(pdev); - struct resource *res_emma; - int irq_emma; - int err = 0; - - res_emma = platform_get_resource(pdev, IORESOURCE_MEM, 1); - irq_emma = platform_get_irq(pdev, 1); - if (!res_emma || !irq_emma) { - dev_err(pcdev->dev, "no EMMA resources\n"); - err = -ENODEV; - goto out; - } - - pcdev->base_emma = devm_ioremap_resource(pcdev->dev, res_emma); - if (IS_ERR(pcdev->base_emma)) { - err = PTR_ERR(pcdev->base_emma); - goto out; - } - - err = devm_request_irq(pcdev->dev, irq_emma, mx27_camera_emma_irq, 0, - MX2_CAM_DRV_NAME, pcdev); - if (err) { - dev_err(pcdev->dev, "Camera EMMA interrupt register failed\n"); - goto out; - } - - pcdev->clk_emma_ipg = devm_clk_get(pcdev->dev, "emma-ipg"); - if (IS_ERR(pcdev->clk_emma_ipg)) { - err = PTR_ERR(pcdev->clk_emma_ipg); - goto out; - } - - clk_prepare_enable(pcdev->clk_emma_ipg); - - pcdev->clk_emma_ahb = devm_clk_get(pcdev->dev, "emma-ahb"); - if (IS_ERR(pcdev->clk_emma_ahb)) { - err = PTR_ERR(pcdev->clk_emma_ahb); - goto exit_clk_emma_ipg; - } - - clk_prepare_enable(pcdev->clk_emma_ahb); - - err = mx27_camera_emma_prp_reset(pcdev); - if (err) - goto exit_clk_emma_ahb; - - return err; - -exit_clk_emma_ahb: - clk_disable_unprepare(pcdev->clk_emma_ahb); -exit_clk_emma_ipg: - clk_disable_unprepare(pcdev->clk_emma_ipg); -out: - return err; -} - -static int mx2_camera_probe(struct platform_device *pdev) -{ - struct mx2_camera_dev *pcdev; - struct resource *res_csi; - int irq_csi; - int err = 0; - - dev_dbg(&pdev->dev, "initialising\n"); - - res_csi = platform_get_resource(pdev, IORESOURCE_MEM, 0); - irq_csi = platform_get_irq(pdev, 0); - if (res_csi == NULL || irq_csi < 0) { - dev_err(&pdev->dev, "Missing platform resources data\n"); - err = -ENODEV; - goto exit; - } - - pcdev = devm_kzalloc(&pdev->dev, sizeof(*pcdev), GFP_KERNEL); - if (!pcdev) { - dev_err(&pdev->dev, "Could not allocate pcdev\n"); - err = -ENOMEM; - goto exit; - } - - pcdev->clk_csi_ahb = devm_clk_get(&pdev->dev, "ahb"); - if (IS_ERR(pcdev->clk_csi_ahb)) { - dev_err(&pdev->dev, "Could not get csi ahb clock\n"); - err = PTR_ERR(pcdev->clk_csi_ahb); - goto exit; - } - - pcdev->clk_csi_per = devm_clk_get(&pdev->dev, "per"); - if (IS_ERR(pcdev->clk_csi_per)) { - dev_err(&pdev->dev, "Could not get csi per clock\n"); - err = PTR_ERR(pcdev->clk_csi_per); - goto exit; - } - - pcdev->pdata = pdev->dev.platform_data; - if (pcdev->pdata) { - long rate; - - pcdev->platform_flags = pcdev->pdata->flags; - - rate = clk_round_rate(pcdev->clk_csi_per, - pcdev->pdata->clk * 2); - if (rate <= 0) { - err = -ENODEV; - goto exit; - } - err = clk_set_rate(pcdev->clk_csi_per, rate); - if (err < 0) - goto exit; - } - - INIT_LIST_HEAD(&pcdev->capture); - INIT_LIST_HEAD(&pcdev->active_bufs); - INIT_LIST_HEAD(&pcdev->discard); - spin_lock_init(&pcdev->lock); - - pcdev->base_csi = devm_ioremap_resource(&pdev->dev, res_csi); - if (IS_ERR(pcdev->base_csi)) { - err = PTR_ERR(pcdev->base_csi); - goto exit; - } - - pcdev->dev = &pdev->dev; - platform_set_drvdata(pdev, pcdev); - - err = mx27_camera_emma_init(pdev); - if (err) - goto exit; - - /* - * We're done with drvdata here. Clear the pointer so that - * v4l2 core can start using drvdata on its purpose. - */ - platform_set_drvdata(pdev, NULL); - - pcdev->soc_host.drv_name = MX2_CAM_DRV_NAME, - pcdev->soc_host.ops = &mx2_soc_camera_host_ops, - pcdev->soc_host.priv = pcdev; - pcdev->soc_host.v4l2_dev.dev = &pdev->dev; - pcdev->soc_host.nr = pdev->id; - - pcdev->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev); - if (IS_ERR(pcdev->alloc_ctx)) { - err = PTR_ERR(pcdev->alloc_ctx); - goto eallocctx; - } - err = soc_camera_host_register(&pcdev->soc_host); - if (err) - goto exit_free_emma; - - dev_info(&pdev->dev, "MX2 Camera (CSI) driver probed, clock frequency: %ld\n", - clk_get_rate(pcdev->clk_csi_per)); - - return 0; - -exit_free_emma: - vb2_dma_contig_cleanup_ctx(pcdev->alloc_ctx); -eallocctx: - clk_disable_unprepare(pcdev->clk_emma_ipg); - clk_disable_unprepare(pcdev->clk_emma_ahb); -exit: - return err; -} - -static int mx2_camera_remove(struct platform_device *pdev) -{ - struct soc_camera_host *soc_host = to_soc_camera_host(&pdev->dev); - struct mx2_camera_dev *pcdev = container_of(soc_host, - struct mx2_camera_dev, soc_host); - - soc_camera_host_unregister(&pcdev->soc_host); - - vb2_dma_contig_cleanup_ctx(pcdev->alloc_ctx); - - clk_disable_unprepare(pcdev->clk_emma_ipg); - clk_disable_unprepare(pcdev->clk_emma_ahb); - - dev_info(&pdev->dev, "MX2 Camera driver unloaded\n"); - - return 0; -} - -static struct platform_driver mx2_camera_driver = { - .driver = { - .name = MX2_CAM_DRV_NAME, - }, - .id_table = mx2_camera_devtype, - .remove = mx2_camera_remove, -}; - -module_platform_driver_probe(mx2_camera_driver, mx2_camera_probe); - -MODULE_DESCRIPTION("i.MX27 SoC Camera Host driver"); -MODULE_AUTHOR("Sascha Hauer <sha@pengutronix.de>"); -MODULE_LICENSE("GPL"); -MODULE_VERSION(MX2_CAM_VERSION); diff --git a/drivers/media/platform/soc_camera/mx3_camera.c b/drivers/media/platform/soc_camera/mx3_camera.c deleted file mode 100644 index 169ed1150226..000000000000 --- a/drivers/media/platform/soc_camera/mx3_camera.c +++ /dev/null @@ -1,1264 +0,0 @@ -/* - * V4L2 Driver for i.MX3x camera host - * - * Copyright (C) 2008 - * Guennadi Liakhovetski, DENX Software Engineering, <lg@denx.de> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include <linux/init.h> -#include <linux/module.h> -#include <linux/videodev2.h> -#include <linux/platform_device.h> -#include <linux/clk.h> -#include <linux/vmalloc.h> -#include <linux/interrupt.h> -#include <linux/sched.h> -#include <linux/dma/ipu-dma.h> - -#include <media/v4l2-common.h> -#include <media/v4l2-dev.h> -#include <media/videobuf2-dma-contig.h> -#include <media/soc_camera.h> -#include <media/drv-intf/soc_mediabus.h> - -#include <linux/platform_data/media/camera-mx3.h> -#include <linux/platform_data/dma-imx.h> - -#define MX3_CAM_DRV_NAME "mx3-camera" - -/* CMOS Sensor Interface Registers */ -#define CSI_REG_START 0x60 - -#define CSI_SENS_CONF (0x60 - CSI_REG_START) -#define CSI_SENS_FRM_SIZE (0x64 - CSI_REG_START) -#define CSI_ACT_FRM_SIZE (0x68 - CSI_REG_START) -#define CSI_OUT_FRM_CTRL (0x6C - CSI_REG_START) -#define CSI_TST_CTRL (0x70 - CSI_REG_START) -#define CSI_CCIR_CODE_1 (0x74 - CSI_REG_START) -#define CSI_CCIR_CODE_2 (0x78 - CSI_REG_START) -#define CSI_CCIR_CODE_3 (0x7C - CSI_REG_START) -#define CSI_FLASH_STROBE_1 (0x80 - CSI_REG_START) -#define CSI_FLASH_STROBE_2 (0x84 - CSI_REG_START) - -#define CSI_SENS_CONF_VSYNC_POL_SHIFT 0 -#define CSI_SENS_CONF_HSYNC_POL_SHIFT 1 -#define CSI_SENS_CONF_DATA_POL_SHIFT 2 -#define CSI_SENS_CONF_PIX_CLK_POL_SHIFT 3 -#define CSI_SENS_CONF_SENS_PRTCL_SHIFT 4 -#define CSI_SENS_CONF_SENS_CLKSRC_SHIFT 7 -#define CSI_SENS_CONF_DATA_FMT_SHIFT 8 -#define CSI_SENS_CONF_DATA_WIDTH_SHIFT 10 -#define CSI_SENS_CONF_EXT_VSYNC_SHIFT 15 -#define CSI_SENS_CONF_DIVRATIO_SHIFT 16 - -#define CSI_SENS_CONF_DATA_FMT_RGB_YUV444 (0UL << CSI_SENS_CONF_DATA_FMT_SHIFT) -#define CSI_SENS_CONF_DATA_FMT_YUV422 (2UL << CSI_SENS_CONF_DATA_FMT_SHIFT) -#define CSI_SENS_CONF_DATA_FMT_BAYER (3UL << CSI_SENS_CONF_DATA_FMT_SHIFT) - -#define MAX_VIDEO_MEM 16 - -struct mx3_camera_buffer { - /* common v4l buffer stuff -- must be first */ - struct vb2_v4l2_buffer vb; - struct list_head queue; - - /* One descriptot per scatterlist (per frame) */ - struct dma_async_tx_descriptor *txd; - - /* We have to "build" a scatterlist ourselves - one element per frame */ - struct scatterlist sg; -}; - -/** - * struct mx3_camera_dev - i.MX3x camera (CSI) object - * @dev: camera device, to which the coherent buffer is attached - * @icd: currently attached camera sensor - * @clk: pointer to clock - * @base: remapped register base address - * @pdata: platform data - * @platform_flags: platform flags - * @mclk: master clock frequency in Hz - * @capture: list of capture videobuffers - * @lock: protects video buffer lists - * @active: active video buffer - * @idmac_channel: array of pointers to IPU DMAC DMA channels - * @soc_host: embedded soc_host object - */ -struct mx3_camera_dev { - /* - * i.MX3x is only supposed to handle one camera on its Camera Sensor - * Interface. If anyone ever builds hardware to enable more than one - * camera _simultaneously_, they will have to modify this driver too - */ - struct clk *clk; - - void __iomem *base; - - struct mx3_camera_pdata *pdata; - - unsigned long platform_flags; - unsigned long mclk; - u16 width_flags; /* max 15 bits */ - - struct list_head capture; - spinlock_t lock; /* Protects video buffer lists */ - struct mx3_camera_buffer *active; - size_t buf_total; - struct vb2_alloc_ctx *alloc_ctx; - enum v4l2_field field; - int sequence; - - /* IDMAC / dmaengine interface */ - struct idmac_channel *idmac_channel[1]; /* We need one channel */ - - struct soc_camera_host soc_host; -}; - -struct dma_chan_request { - struct mx3_camera_dev *mx3_cam; - enum ipu_channel id; -}; - -static u32 csi_reg_read(struct mx3_camera_dev *mx3, off_t reg) -{ - return __raw_readl(mx3->base + reg); -} - -static void csi_reg_write(struct mx3_camera_dev *mx3, u32 value, off_t reg) -{ - __raw_writel(value, mx3->base + reg); -} - -static struct mx3_camera_buffer *to_mx3_vb(struct vb2_v4l2_buffer *vb) -{ - return container_of(vb, struct mx3_camera_buffer, vb); -} - -/* Called from the IPU IDMAC ISR */ -static void mx3_cam_dma_done(void *arg) -{ - struct idmac_tx_desc *desc = to_tx_desc(arg); - struct dma_chan *chan = desc->txd.chan; - struct idmac_channel *ichannel = to_idmac_chan(chan); - struct mx3_camera_dev *mx3_cam = ichannel->client; - - dev_dbg(chan->device->dev, "callback cookie %d, active DMA 0x%08x\n", - desc->txd.cookie, mx3_cam->active ? sg_dma_address(&mx3_cam->active->sg) : 0); - - spin_lock(&mx3_cam->lock); - if (mx3_cam->active) { - struct vb2_v4l2_buffer *vb = &mx3_cam->active->vb; - struct mx3_camera_buffer *buf = to_mx3_vb(vb); - - list_del_init(&buf->queue); - vb->vb2_buf.timestamp = ktime_get_ns(); - vb->field = mx3_cam->field; - vb->sequence = mx3_cam->sequence++; - vb2_buffer_done(&vb->vb2_buf, VB2_BUF_STATE_DONE); - } - - if (list_empty(&mx3_cam->capture)) { - mx3_cam->active = NULL; - spin_unlock(&mx3_cam->lock); - - /* - * stop capture - without further buffers IPU_CHA_BUF0_RDY will - * not get updated - */ - return; - } - - mx3_cam->active = list_entry(mx3_cam->capture.next, - struct mx3_camera_buffer, queue); - spin_unlock(&mx3_cam->lock); -} - -/* - * Videobuf operations - */ - -/* - * Calculate the __buffer__ (not data) size and number of buffers. - */ -static int mx3_videobuf_setup(struct vb2_queue *vq, - unsigned int *count, unsigned int *num_planes, - unsigned int sizes[], void *alloc_ctxs[]) -{ - struct soc_camera_device *icd = soc_camera_from_vb2q(vq); - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - struct mx3_camera_dev *mx3_cam = ici->priv; - - if (!mx3_cam->idmac_channel[0]) - return -EINVAL; - - alloc_ctxs[0] = mx3_cam->alloc_ctx; - - if (!vq->num_buffers) - mx3_cam->sequence = 0; - - if (!*count) - *count = 2; - - /* Called from VIDIOC_REQBUFS or in compatibility mode */ - if (!*num_planes) - sizes[0] = icd->sizeimage; - else if (sizes[0] < icd->sizeimage) - return -EINVAL; - - /* If *num_planes != 0, we have already verified *count. */ - if (sizes[0] * *count + mx3_cam->buf_total > MAX_VIDEO_MEM * 1024 * 1024) - *count = (MAX_VIDEO_MEM * 1024 * 1024 - mx3_cam->buf_total) / - sizes[0]; - - *num_planes = 1; - - return 0; -} - -static enum pixel_fmt fourcc_to_ipu_pix(__u32 fourcc) -{ - /* Add more formats as need arises and test possibilities appear... */ - switch (fourcc) { - case V4L2_PIX_FMT_RGB24: - return IPU_PIX_FMT_RGB24; - case V4L2_PIX_FMT_UYVY: - case V4L2_PIX_FMT_RGB565: - default: - return IPU_PIX_FMT_GENERIC; - } -} - -static void mx3_videobuf_queue(struct vb2_buffer *vb) -{ - struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); - struct soc_camera_device *icd = soc_camera_from_vb2q(vb->vb2_queue); - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - struct mx3_camera_dev *mx3_cam = ici->priv; - struct mx3_camera_buffer *buf = to_mx3_vb(vbuf); - struct scatterlist *sg = &buf->sg; - struct dma_async_tx_descriptor *txd; - struct idmac_channel *ichan = mx3_cam->idmac_channel[0]; - struct idmac_video_param *video = &ichan->params.video; - const struct soc_mbus_pixelfmt *host_fmt = icd->current_fmt->host_fmt; - dma_cookie_t cookie; - size_t new_size; - - new_size = icd->sizeimage; - - if (vb2_plane_size(vb, 0) < new_size) { - dev_err(icd->parent, "Buffer #%d too small (%lu < %zu)\n", - vbuf->vb2_buf.index, vb2_plane_size(vb, 0), new_size); - goto error; - } - - if (!buf->txd) { - sg_dma_address(sg) = vb2_dma_contig_plane_dma_addr(vb, 0); - sg_dma_len(sg) = new_size; - - txd = dmaengine_prep_slave_sg( - &ichan->dma_chan, sg, 1, DMA_DEV_TO_MEM, - DMA_PREP_INTERRUPT); - if (!txd) - goto error; - - txd->callback_param = txd; - txd->callback = mx3_cam_dma_done; - - buf->txd = txd; - } else { - txd = buf->txd; - } - - vb2_set_plane_payload(vb, 0, new_size); - - /* This is the configuration of one sg-element */ - video->out_pixel_fmt = fourcc_to_ipu_pix(host_fmt->fourcc); - - if (video->out_pixel_fmt == IPU_PIX_FMT_GENERIC) { - /* - * If the IPU DMA channel is configured to transfer generic - * 8-bit data, we have to set up the geometry parameters - * correctly, according to the current pixel format. The DMA - * horizontal parameters in this case are expressed in bytes, - * not in pixels. - */ - video->out_width = icd->bytesperline; - video->out_height = icd->user_height; - video->out_stride = icd->bytesperline; - } else { - /* - * For IPU known formats the pixel unit will be managed - * successfully by the IPU code - */ - video->out_width = icd->user_width; - video->out_height = icd->user_height; - video->out_stride = icd->user_width; - } - -#ifdef DEBUG - /* helps to see what DMA actually has written */ - if (vb2_plane_vaddr(vb, 0)) - memset(vb2_plane_vaddr(vb, 0), 0xaa, vb2_get_plane_payload(vb, 0)); -#endif - - spin_lock_irq(&mx3_cam->lock); - list_add_tail(&buf->queue, &mx3_cam->capture); - - if (!mx3_cam->active) - mx3_cam->active = buf; - - spin_unlock_irq(&mx3_cam->lock); - - cookie = txd->tx_submit(txd); - dev_dbg(icd->parent, "Submitted cookie %d DMA 0x%08x\n", - cookie, sg_dma_address(&buf->sg)); - - if (cookie >= 0) - return; - - spin_lock_irq(&mx3_cam->lock); - - /* Submit error */ - list_del_init(&buf->queue); - - if (mx3_cam->active == buf) - mx3_cam->active = NULL; - - spin_unlock_irq(&mx3_cam->lock); -error: - vb2_buffer_done(vb, VB2_BUF_STATE_ERROR); -} - -static void mx3_videobuf_release(struct vb2_buffer *vb) -{ - struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); - struct soc_camera_device *icd = soc_camera_from_vb2q(vb->vb2_queue); - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - struct mx3_camera_dev *mx3_cam = ici->priv; - struct mx3_camera_buffer *buf = to_mx3_vb(vbuf); - struct dma_async_tx_descriptor *txd = buf->txd; - unsigned long flags; - - dev_dbg(icd->parent, - "Release%s DMA 0x%08x, queue %sempty\n", - mx3_cam->active == buf ? " active" : "", sg_dma_address(&buf->sg), - list_empty(&buf->queue) ? "" : "not "); - - spin_lock_irqsave(&mx3_cam->lock, flags); - - if (mx3_cam->active == buf) - mx3_cam->active = NULL; - - /* Doesn't hurt also if the list is empty */ - list_del_init(&buf->queue); - - if (txd) { - buf->txd = NULL; - if (mx3_cam->idmac_channel[0]) - async_tx_ack(txd); - } - - spin_unlock_irqrestore(&mx3_cam->lock, flags); - - mx3_cam->buf_total -= vb2_plane_size(vb, 0); -} - -static int mx3_videobuf_init(struct vb2_buffer *vb) -{ - struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); - struct soc_camera_device *icd = soc_camera_from_vb2q(vb->vb2_queue); - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - struct mx3_camera_dev *mx3_cam = ici->priv; - struct mx3_camera_buffer *buf = to_mx3_vb(vbuf); - - if (!buf->txd) { - /* This is for locking debugging only */ - INIT_LIST_HEAD(&buf->queue); - sg_init_table(&buf->sg, 1); - - mx3_cam->buf_total += vb2_plane_size(vb, 0); - } - - return 0; -} - -static void mx3_stop_streaming(struct vb2_queue *q) -{ - struct soc_camera_device *icd = soc_camera_from_vb2q(q); - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - struct mx3_camera_dev *mx3_cam = ici->priv; - struct idmac_channel *ichan = mx3_cam->idmac_channel[0]; - struct mx3_camera_buffer *buf, *tmp; - unsigned long flags; - - if (ichan) - dmaengine_pause(&ichan->dma_chan); - - spin_lock_irqsave(&mx3_cam->lock, flags); - - mx3_cam->active = NULL; - - list_for_each_entry_safe(buf, tmp, &mx3_cam->capture, queue) { - list_del_init(&buf->queue); - vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); - } - - spin_unlock_irqrestore(&mx3_cam->lock, flags); -} - -static struct vb2_ops mx3_videobuf_ops = { - .queue_setup = mx3_videobuf_setup, - .buf_queue = mx3_videobuf_queue, - .buf_cleanup = mx3_videobuf_release, - .buf_init = mx3_videobuf_init, - .wait_prepare = vb2_ops_wait_prepare, - .wait_finish = vb2_ops_wait_finish, - .stop_streaming = mx3_stop_streaming, -}; - -static int mx3_camera_init_videobuf(struct vb2_queue *q, - struct soc_camera_device *icd) -{ - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - - q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - q->io_modes = VB2_MMAP | VB2_USERPTR; - q->drv_priv = icd; - q->ops = &mx3_videobuf_ops; - q->mem_ops = &vb2_dma_contig_memops; - q->buf_struct_size = sizeof(struct mx3_camera_buffer); - q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; - q->lock = &ici->host_lock; - - return vb2_queue_init(q); -} - -/* First part of ipu_csi_init_interface() */ -static void mx3_camera_activate(struct mx3_camera_dev *mx3_cam) -{ - u32 conf; - long rate; - - /* Set default size: ipu_csi_set_window_size() */ - csi_reg_write(mx3_cam, (640 - 1) | ((480 - 1) << 16), CSI_ACT_FRM_SIZE); - /* ...and position to 0:0: ipu_csi_set_window_pos() */ - conf = csi_reg_read(mx3_cam, CSI_OUT_FRM_CTRL) & 0xffff0000; - csi_reg_write(mx3_cam, conf, CSI_OUT_FRM_CTRL); - - /* We use only gated clock synchronisation mode so far */ - conf = 0 << CSI_SENS_CONF_SENS_PRTCL_SHIFT; - - /* Set generic data, platform-biggest bus-width */ - conf |= CSI_SENS_CONF_DATA_FMT_BAYER; - - if (mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_15) - conf |= 3 << CSI_SENS_CONF_DATA_WIDTH_SHIFT; - else if (mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_10) - conf |= 2 << CSI_SENS_CONF_DATA_WIDTH_SHIFT; - else if (mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_8) - conf |= 1 << CSI_SENS_CONF_DATA_WIDTH_SHIFT; - else/* if (mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_4)*/ - conf |= 0 << CSI_SENS_CONF_DATA_WIDTH_SHIFT; - - if (mx3_cam->platform_flags & MX3_CAMERA_CLK_SRC) - conf |= 1 << CSI_SENS_CONF_SENS_CLKSRC_SHIFT; - if (mx3_cam->platform_flags & MX3_CAMERA_EXT_VSYNC) - conf |= 1 << CSI_SENS_CONF_EXT_VSYNC_SHIFT; - if (mx3_cam->platform_flags & MX3_CAMERA_DP) - conf |= 1 << CSI_SENS_CONF_DATA_POL_SHIFT; - if (mx3_cam->platform_flags & MX3_CAMERA_PCP) - conf |= 1 << CSI_SENS_CONF_PIX_CLK_POL_SHIFT; - if (mx3_cam->platform_flags & MX3_CAMERA_HSP) - conf |= 1 << CSI_SENS_CONF_HSYNC_POL_SHIFT; - if (mx3_cam->platform_flags & MX3_CAMERA_VSP) - conf |= 1 << CSI_SENS_CONF_VSYNC_POL_SHIFT; - - /* ipu_csi_init_interface() */ - csi_reg_write(mx3_cam, conf, CSI_SENS_CONF); - - clk_prepare_enable(mx3_cam->clk); - rate = clk_round_rate(mx3_cam->clk, mx3_cam->mclk); - dev_dbg(mx3_cam->soc_host.v4l2_dev.dev, "Set SENS_CONF to %x, rate %ld\n", conf, rate); - if (rate) - clk_set_rate(mx3_cam->clk, rate); -} - -static int mx3_camera_add_device(struct soc_camera_device *icd) -{ - dev_info(icd->parent, "MX3 Camera driver attached to camera %d\n", - icd->devnum); - - return 0; -} - -static void mx3_camera_remove_device(struct soc_camera_device *icd) -{ - dev_info(icd->parent, "MX3 Camera driver detached from camera %d\n", - icd->devnum); -} - -/* Called with .host_lock held */ -static int mx3_camera_clock_start(struct soc_camera_host *ici) -{ - struct mx3_camera_dev *mx3_cam = ici->priv; - - mx3_camera_activate(mx3_cam); - - mx3_cam->buf_total = 0; - - return 0; -} - -/* Called with .host_lock held */ -static void mx3_camera_clock_stop(struct soc_camera_host *ici) -{ - struct mx3_camera_dev *mx3_cam = ici->priv; - struct idmac_channel **ichan = &mx3_cam->idmac_channel[0]; - - if (*ichan) { - dma_release_channel(&(*ichan)->dma_chan); - *ichan = NULL; - } - - clk_disable_unprepare(mx3_cam->clk); -} - -static int test_platform_param(struct mx3_camera_dev *mx3_cam, - unsigned char buswidth, unsigned long *flags) -{ - /* - * If requested data width is supported by the platform, use it or any - * possible lower value - i.MX31 is smart enough to shift bits - */ - if (buswidth > fls(mx3_cam->width_flags)) - return -EINVAL; - - /* - * Platform specified synchronization and pixel clock polarities are - * only a recommendation and are only used during probing. MX3x - * camera interface only works in master mode, i.e., uses HSYNC and - * VSYNC signals from the sensor - */ - *flags = V4L2_MBUS_MASTER | - V4L2_MBUS_HSYNC_ACTIVE_HIGH | - V4L2_MBUS_HSYNC_ACTIVE_LOW | - V4L2_MBUS_VSYNC_ACTIVE_HIGH | - V4L2_MBUS_VSYNC_ACTIVE_LOW | - V4L2_MBUS_PCLK_SAMPLE_RISING | - V4L2_MBUS_PCLK_SAMPLE_FALLING | - V4L2_MBUS_DATA_ACTIVE_HIGH | - V4L2_MBUS_DATA_ACTIVE_LOW; - - return 0; -} - -static int mx3_camera_try_bus_param(struct soc_camera_device *icd, - const unsigned int depth) -{ - struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - struct mx3_camera_dev *mx3_cam = ici->priv; - struct v4l2_mbus_config cfg = {.type = V4L2_MBUS_PARALLEL,}; - unsigned long bus_flags, common_flags; - int ret = test_platform_param(mx3_cam, depth, &bus_flags); - - dev_dbg(icd->parent, "request bus width %d bit: %d\n", depth, ret); - - if (ret < 0) - return ret; - - ret = v4l2_subdev_call(sd, video, g_mbus_config, &cfg); - if (!ret) { - common_flags = soc_mbus_config_compatible(&cfg, - bus_flags); - if (!common_flags) { - dev_warn(icd->parent, - "Flags incompatible: camera 0x%x, host 0x%lx\n", - cfg.flags, bus_flags); - return -EINVAL; - } - } else if (ret != -ENOIOCTLCMD) { - return ret; - } - - return 0; -} - -static bool chan_filter(struct dma_chan *chan, void *arg) -{ - struct dma_chan_request *rq = arg; - struct mx3_camera_pdata *pdata; - - if (!imx_dma_is_ipu(chan)) - return false; - - if (!rq) - return false; - - pdata = rq->mx3_cam->soc_host.v4l2_dev.dev->platform_data; - - return rq->id == chan->chan_id && - pdata->dma_dev == chan->device->dev; -} - -static const struct soc_mbus_pixelfmt mx3_camera_formats[] = { - { - .fourcc = V4L2_PIX_FMT_SBGGR8, - .name = "Bayer BGGR (sRGB) 8 bit", - .bits_per_sample = 8, - .packing = SOC_MBUS_PACKING_NONE, - .order = SOC_MBUS_ORDER_LE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, { - .fourcc = V4L2_PIX_FMT_GREY, - .name = "Monochrome 8 bit", - .bits_per_sample = 8, - .packing = SOC_MBUS_PACKING_NONE, - .order = SOC_MBUS_ORDER_LE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, -}; - -/* This will be corrected as we get more formats */ -static bool mx3_camera_packing_supported(const struct soc_mbus_pixelfmt *fmt) -{ - return fmt->packing == SOC_MBUS_PACKING_NONE || - (fmt->bits_per_sample == 8 && - fmt->packing == SOC_MBUS_PACKING_2X8_PADHI) || - (fmt->bits_per_sample > 8 && - fmt->packing == SOC_MBUS_PACKING_EXTEND16); -} - -static int mx3_camera_get_formats(struct soc_camera_device *icd, unsigned int idx, - struct soc_camera_format_xlate *xlate) -{ - struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - struct device *dev = icd->parent; - int formats = 0, ret; - struct v4l2_subdev_mbus_code_enum code = { - .which = V4L2_SUBDEV_FORMAT_ACTIVE, - .index = idx, - }; - const struct soc_mbus_pixelfmt *fmt; - - ret = v4l2_subdev_call(sd, pad, enum_mbus_code, NULL, &code); - if (ret < 0) - /* No more formats */ - return 0; - - fmt = soc_mbus_get_fmtdesc(code.code); - if (!fmt) { - dev_warn(icd->parent, - "Unsupported format code #%u: 0x%x\n", idx, code.code); - return 0; - } - - /* This also checks support for the requested bits-per-sample */ - ret = mx3_camera_try_bus_param(icd, fmt->bits_per_sample); - if (ret < 0) - return 0; - - switch (code.code) { - case MEDIA_BUS_FMT_SBGGR10_1X10: - formats++; - if (xlate) { - xlate->host_fmt = &mx3_camera_formats[0]; - xlate->code = code.code; - xlate++; - dev_dbg(dev, "Providing format %s using code 0x%x\n", - mx3_camera_formats[0].name, code.code); - } - break; - case MEDIA_BUS_FMT_Y10_1X10: - formats++; - if (xlate) { - xlate->host_fmt = &mx3_camera_formats[1]; - xlate->code = code.code; - xlate++; - dev_dbg(dev, "Providing format %s using code 0x%x\n", - mx3_camera_formats[1].name, code.code); - } - break; - default: - if (!mx3_camera_packing_supported(fmt)) - return 0; - } - - /* Generic pass-through */ - formats++; - if (xlate) { - xlate->host_fmt = fmt; - xlate->code = code.code; - dev_dbg(dev, "Providing format %c%c%c%c in pass-through mode\n", - (fmt->fourcc >> (0*8)) & 0xFF, - (fmt->fourcc >> (1*8)) & 0xFF, - (fmt->fourcc >> (2*8)) & 0xFF, - (fmt->fourcc >> (3*8)) & 0xFF); - xlate++; - } - - return formats; -} - -static void configure_geometry(struct mx3_camera_dev *mx3_cam, - unsigned int width, unsigned int height, - const struct soc_mbus_pixelfmt *fmt) -{ - u32 ctrl, width_field, height_field; - - if (fourcc_to_ipu_pix(fmt->fourcc) == IPU_PIX_FMT_GENERIC) { - /* - * As the CSI will be configured to output BAYER, here - * the width parameter count the number of samples to - * capture to complete the whole image width. - */ - unsigned int num, den; - int ret = soc_mbus_samples_per_pixel(fmt, &num, &den); - BUG_ON(ret < 0); - width = width * num / den; - } - - /* Setup frame size - this cannot be changed on-the-fly... */ - width_field = width - 1; - height_field = height - 1; - csi_reg_write(mx3_cam, width_field | (height_field << 16), CSI_SENS_FRM_SIZE); - - csi_reg_write(mx3_cam, width_field << 16, CSI_FLASH_STROBE_1); - csi_reg_write(mx3_cam, (height_field << 16) | 0x22, CSI_FLASH_STROBE_2); - - csi_reg_write(mx3_cam, width_field | (height_field << 16), CSI_ACT_FRM_SIZE); - - /* ...and position */ - ctrl = csi_reg_read(mx3_cam, CSI_OUT_FRM_CTRL) & 0xffff0000; - /* Sensor does the cropping */ - csi_reg_write(mx3_cam, ctrl | 0 | (0 << 8), CSI_OUT_FRM_CTRL); -} - -static int acquire_dma_channel(struct mx3_camera_dev *mx3_cam) -{ - dma_cap_mask_t mask; - struct dma_chan *chan; - struct idmac_channel **ichan = &mx3_cam->idmac_channel[0]; - /* We have to use IDMAC_IC_7 for Bayer / generic data */ - struct dma_chan_request rq = {.mx3_cam = mx3_cam, - .id = IDMAC_IC_7}; - - dma_cap_zero(mask); - dma_cap_set(DMA_SLAVE, mask); - dma_cap_set(DMA_PRIVATE, mask); - chan = dma_request_channel(mask, chan_filter, &rq); - if (!chan) - return -EBUSY; - - *ichan = to_idmac_chan(chan); - (*ichan)->client = mx3_cam; - - return 0; -} - -/* - * FIXME: learn to use stride != width, then we can keep stride properly aligned - * and support arbitrary (even) widths. - */ -static inline void stride_align(__u32 *width) -{ - if (ALIGN(*width, 8) < 4096) - *width = ALIGN(*width, 8); - else - *width = *width & ~7; -} - -/* - * As long as we don't implement host-side cropping and scaling, we can use - * default g_crop and cropcap from soc_camera.c - */ -static int mx3_camera_set_crop(struct soc_camera_device *icd, - const struct v4l2_crop *a) -{ - struct v4l2_crop a_writable = *a; - struct v4l2_rect *rect = &a_writable.c; - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - struct mx3_camera_dev *mx3_cam = ici->priv; - struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - struct v4l2_subdev_format fmt = { - .which = V4L2_SUBDEV_FORMAT_ACTIVE, - }; - struct v4l2_mbus_framefmt *mf = &fmt.format; - int ret; - - soc_camera_limit_side(&rect->left, &rect->width, 0, 2, 4096); - soc_camera_limit_side(&rect->top, &rect->height, 0, 2, 4096); - - ret = v4l2_subdev_call(sd, video, s_crop, a); - if (ret < 0) - return ret; - - /* The capture device might have changed its output sizes */ - ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &fmt); - if (ret < 0) - return ret; - - if (mf->code != icd->current_fmt->code) - return -EINVAL; - - if (mf->width & 7) { - /* Ouch! We can only handle 8-byte aligned width... */ - stride_align(&mf->width); - ret = v4l2_subdev_call(sd, pad, set_fmt, NULL, &fmt); - if (ret < 0) - return ret; - } - - if (mf->width != icd->user_width || mf->height != icd->user_height) - configure_geometry(mx3_cam, mf->width, mf->height, - icd->current_fmt->host_fmt); - - dev_dbg(icd->parent, "Sensor cropped %dx%d\n", - mf->width, mf->height); - - icd->user_width = mf->width; - icd->user_height = mf->height; - - return ret; -} - -static int mx3_camera_set_fmt(struct soc_camera_device *icd, - struct v4l2_format *f) -{ - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - struct mx3_camera_dev *mx3_cam = ici->priv; - struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - const struct soc_camera_format_xlate *xlate; - struct v4l2_pix_format *pix = &f->fmt.pix; - struct v4l2_subdev_format format = { - .which = V4L2_SUBDEV_FORMAT_ACTIVE, - }; - struct v4l2_mbus_framefmt *mf = &format.format; - int ret; - - xlate = soc_camera_xlate_by_fourcc(icd, pix->pixelformat); - if (!xlate) { - dev_warn(icd->parent, "Format %x not found\n", - pix->pixelformat); - return -EINVAL; - } - - stride_align(&pix->width); - dev_dbg(icd->parent, "Set format %dx%d\n", pix->width, pix->height); - - /* - * Might have to perform a complete interface initialisation like in - * ipu_csi_init_interface() in mxc_v4l2_s_param(). Also consider - * mxc_v4l2_s_fmt() - */ - - configure_geometry(mx3_cam, pix->width, pix->height, xlate->host_fmt); - - mf->width = pix->width; - mf->height = pix->height; - mf->field = pix->field; - mf->colorspace = pix->colorspace; - mf->code = xlate->code; - - ret = v4l2_subdev_call(sd, pad, set_fmt, NULL, &format); - if (ret < 0) - return ret; - - if (mf->code != xlate->code) - return -EINVAL; - - if (!mx3_cam->idmac_channel[0]) { - ret = acquire_dma_channel(mx3_cam); - if (ret < 0) - return ret; - } - - pix->width = mf->width; - pix->height = mf->height; - pix->field = mf->field; - mx3_cam->field = mf->field; - pix->colorspace = mf->colorspace; - icd->current_fmt = xlate; - - dev_dbg(icd->parent, "Sensor set %dx%d\n", pix->width, pix->height); - - return ret; -} - -static int mx3_camera_try_fmt(struct soc_camera_device *icd, - struct v4l2_format *f) -{ - struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - const struct soc_camera_format_xlate *xlate; - struct v4l2_pix_format *pix = &f->fmt.pix; - struct v4l2_subdev_pad_config pad_cfg; - struct v4l2_subdev_format format = { - .which = V4L2_SUBDEV_FORMAT_TRY, - }; - struct v4l2_mbus_framefmt *mf = &format.format; - __u32 pixfmt = pix->pixelformat; - int ret; - - xlate = soc_camera_xlate_by_fourcc(icd, pixfmt); - if (pixfmt && !xlate) { - dev_warn(icd->parent, "Format %x not found\n", pixfmt); - return -EINVAL; - } - - /* limit to MX3 hardware capabilities */ - if (pix->height > 4096) - pix->height = 4096; - if (pix->width > 4096) - pix->width = 4096; - - /* limit to sensor capabilities */ - mf->width = pix->width; - mf->height = pix->height; - mf->field = pix->field; - mf->colorspace = pix->colorspace; - mf->code = xlate->code; - - ret = v4l2_subdev_call(sd, pad, set_fmt, &pad_cfg, &format); - if (ret < 0) - return ret; - - pix->width = mf->width; - pix->height = mf->height; - pix->colorspace = mf->colorspace; - - switch (mf->field) { - case V4L2_FIELD_ANY: - pix->field = V4L2_FIELD_NONE; - break; - case V4L2_FIELD_NONE: - break; - default: - dev_err(icd->parent, "Field type %d unsupported.\n", - mf->field); - ret = -EINVAL; - } - - return ret; -} - -static int mx3_camera_reqbufs(struct soc_camera_device *icd, - struct v4l2_requestbuffers *p) -{ - return 0; -} - -static unsigned int mx3_camera_poll(struct file *file, poll_table *pt) -{ - struct soc_camera_device *icd = file->private_data; - - return vb2_poll(&icd->vb2_vidq, file, pt); -} - -static int mx3_camera_querycap(struct soc_camera_host *ici, - struct v4l2_capability *cap) -{ - /* cap->name is set by the firendly caller:-> */ - strlcpy(cap->card, "i.MX3x Camera", sizeof(cap->card)); - cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; - cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; - - return 0; -} - -static int mx3_camera_set_bus_param(struct soc_camera_device *icd) -{ - struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - struct mx3_camera_dev *mx3_cam = ici->priv; - struct v4l2_mbus_config cfg = {.type = V4L2_MBUS_PARALLEL,}; - u32 pixfmt = icd->current_fmt->host_fmt->fourcc; - unsigned long bus_flags, common_flags; - u32 dw, sens_conf; - const struct soc_mbus_pixelfmt *fmt; - int buswidth; - int ret; - const struct soc_camera_format_xlate *xlate; - struct device *dev = icd->parent; - - fmt = soc_mbus_get_fmtdesc(icd->current_fmt->code); - if (!fmt) - return -EINVAL; - - xlate = soc_camera_xlate_by_fourcc(icd, pixfmt); - if (!xlate) { - dev_warn(dev, "Format %x not found\n", pixfmt); - return -EINVAL; - } - - buswidth = fmt->bits_per_sample; - ret = test_platform_param(mx3_cam, buswidth, &bus_flags); - - dev_dbg(dev, "requested bus width %d bit: %d\n", buswidth, ret); - - if (ret < 0) - return ret; - - ret = v4l2_subdev_call(sd, video, g_mbus_config, &cfg); - if (!ret) { - common_flags = soc_mbus_config_compatible(&cfg, - bus_flags); - if (!common_flags) { - dev_warn(icd->parent, - "Flags incompatible: camera 0x%x, host 0x%lx\n", - cfg.flags, bus_flags); - return -EINVAL; - } - } else if (ret != -ENOIOCTLCMD) { - return ret; - } else { - common_flags = bus_flags; - } - - dev_dbg(dev, "Flags cam: 0x%x host: 0x%lx common: 0x%lx\n", - cfg.flags, bus_flags, common_flags); - - /* Make choices, based on platform preferences */ - if ((common_flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH) && - (common_flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)) { - if (mx3_cam->platform_flags & MX3_CAMERA_HSP) - common_flags &= ~V4L2_MBUS_HSYNC_ACTIVE_HIGH; - else - common_flags &= ~V4L2_MBUS_HSYNC_ACTIVE_LOW; - } - - if ((common_flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH) && - (common_flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)) { - if (mx3_cam->platform_flags & MX3_CAMERA_VSP) - common_flags &= ~V4L2_MBUS_VSYNC_ACTIVE_HIGH; - else - common_flags &= ~V4L2_MBUS_VSYNC_ACTIVE_LOW; - } - - if ((common_flags & V4L2_MBUS_DATA_ACTIVE_HIGH) && - (common_flags & V4L2_MBUS_DATA_ACTIVE_LOW)) { - if (mx3_cam->platform_flags & MX3_CAMERA_DP) - common_flags &= ~V4L2_MBUS_DATA_ACTIVE_HIGH; - else - common_flags &= ~V4L2_MBUS_DATA_ACTIVE_LOW; - } - - if ((common_flags & V4L2_MBUS_PCLK_SAMPLE_RISING) && - (common_flags & V4L2_MBUS_PCLK_SAMPLE_FALLING)) { - if (mx3_cam->platform_flags & MX3_CAMERA_PCP) - common_flags &= ~V4L2_MBUS_PCLK_SAMPLE_RISING; - else - common_flags &= ~V4L2_MBUS_PCLK_SAMPLE_FALLING; - } - - cfg.flags = common_flags; - ret = v4l2_subdev_call(sd, video, s_mbus_config, &cfg); - if (ret < 0 && ret != -ENOIOCTLCMD) { - dev_dbg(dev, "camera s_mbus_config(0x%lx) returned %d\n", - common_flags, ret); - return ret; - } - - /* - * So far only gated clock mode is supported. Add a line - * (3 << CSI_SENS_CONF_SENS_PRTCL_SHIFT) | - * below and select the required mode when supporting other - * synchronisation protocols. - */ - sens_conf = csi_reg_read(mx3_cam, CSI_SENS_CONF) & - ~((1 << CSI_SENS_CONF_VSYNC_POL_SHIFT) | - (1 << CSI_SENS_CONF_HSYNC_POL_SHIFT) | - (1 << CSI_SENS_CONF_DATA_POL_SHIFT) | - (1 << CSI_SENS_CONF_PIX_CLK_POL_SHIFT) | - (3 << CSI_SENS_CONF_DATA_FMT_SHIFT) | - (3 << CSI_SENS_CONF_DATA_WIDTH_SHIFT)); - - /* TODO: Support RGB and YUV formats */ - - /* This has been set in mx3_camera_activate(), but we clear it above */ - sens_conf |= CSI_SENS_CONF_DATA_FMT_BAYER; - - if (common_flags & V4L2_MBUS_PCLK_SAMPLE_FALLING) - sens_conf |= 1 << CSI_SENS_CONF_PIX_CLK_POL_SHIFT; - if (common_flags & V4L2_MBUS_HSYNC_ACTIVE_LOW) - sens_conf |= 1 << CSI_SENS_CONF_HSYNC_POL_SHIFT; - if (common_flags & V4L2_MBUS_VSYNC_ACTIVE_LOW) - sens_conf |= 1 << CSI_SENS_CONF_VSYNC_POL_SHIFT; - if (common_flags & V4L2_MBUS_DATA_ACTIVE_LOW) - sens_conf |= 1 << CSI_SENS_CONF_DATA_POL_SHIFT; - - /* Just do what we're asked to do */ - switch (xlate->host_fmt->bits_per_sample) { - case 4: - dw = 0 << CSI_SENS_CONF_DATA_WIDTH_SHIFT; - break; - case 8: - dw = 1 << CSI_SENS_CONF_DATA_WIDTH_SHIFT; - break; - case 10: - dw = 2 << CSI_SENS_CONF_DATA_WIDTH_SHIFT; - break; - default: - /* - * Actually it can only be 15 now, default is just to silence - * compiler warnings - */ - case 15: - dw = 3 << CSI_SENS_CONF_DATA_WIDTH_SHIFT; - } - - csi_reg_write(mx3_cam, sens_conf | dw, CSI_SENS_CONF); - - dev_dbg(dev, "Set SENS_CONF to %x\n", sens_conf | dw); - - return 0; -} - -static struct soc_camera_host_ops mx3_soc_camera_host_ops = { - .owner = THIS_MODULE, - .add = mx3_camera_add_device, - .remove = mx3_camera_remove_device, - .clock_start = mx3_camera_clock_start, - .clock_stop = mx3_camera_clock_stop, - .set_crop = mx3_camera_set_crop, - .set_fmt = mx3_camera_set_fmt, - .try_fmt = mx3_camera_try_fmt, - .get_formats = mx3_camera_get_formats, - .init_videobuf2 = mx3_camera_init_videobuf, - .reqbufs = mx3_camera_reqbufs, - .poll = mx3_camera_poll, - .querycap = mx3_camera_querycap, - .set_bus_param = mx3_camera_set_bus_param, -}; - -static int mx3_camera_probe(struct platform_device *pdev) -{ - struct mx3_camera_pdata *pdata = pdev->dev.platform_data; - struct mx3_camera_dev *mx3_cam; - struct resource *res; - void __iomem *base; - int err = 0; - struct soc_camera_host *soc_host; - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - base = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(base)) - return PTR_ERR(base); - - if (!pdata) - return -EINVAL; - - mx3_cam = devm_kzalloc(&pdev->dev, sizeof(*mx3_cam), GFP_KERNEL); - if (!mx3_cam) { - dev_err(&pdev->dev, "Could not allocate mx3 camera object\n"); - return -ENOMEM; - } - - mx3_cam->clk = devm_clk_get(&pdev->dev, NULL); - if (IS_ERR(mx3_cam->clk)) - return PTR_ERR(mx3_cam->clk); - - mx3_cam->pdata = pdata; - mx3_cam->platform_flags = pdata->flags; - if (!(mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_MASK)) { - /* - * Platform hasn't set available data widths. This is bad. - * Warn and use a default. - */ - dev_warn(&pdev->dev, "WARNING! Platform hasn't set available " - "data widths, using default 8 bit\n"); - mx3_cam->platform_flags |= MX3_CAMERA_DATAWIDTH_8; - } - if (mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_4) - mx3_cam->width_flags = 1 << 3; - if (mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_8) - mx3_cam->width_flags |= 1 << 7; - if (mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_10) - mx3_cam->width_flags |= 1 << 9; - if (mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_15) - mx3_cam->width_flags |= 1 << 14; - - mx3_cam->mclk = pdata->mclk_10khz * 10000; - if (!mx3_cam->mclk) { - dev_warn(&pdev->dev, - "mclk_10khz == 0! Please, fix your platform data. " - "Using default 20MHz\n"); - mx3_cam->mclk = 20000000; - } - - /* list of video-buffers */ - INIT_LIST_HEAD(&mx3_cam->capture); - spin_lock_init(&mx3_cam->lock); - - mx3_cam->base = base; - - soc_host = &mx3_cam->soc_host; - soc_host->drv_name = MX3_CAM_DRV_NAME; - soc_host->ops = &mx3_soc_camera_host_ops; - soc_host->priv = mx3_cam; - soc_host->v4l2_dev.dev = &pdev->dev; - soc_host->nr = pdev->id; - - mx3_cam->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev); - if (IS_ERR(mx3_cam->alloc_ctx)) - return PTR_ERR(mx3_cam->alloc_ctx); - - if (pdata->asd_sizes) { - soc_host->asd = pdata->asd; - soc_host->asd_sizes = pdata->asd_sizes; - } - - err = soc_camera_host_register(soc_host); - if (err) - goto ecamhostreg; - - /* IDMAC interface */ - dmaengine_get(); - - return 0; - -ecamhostreg: - vb2_dma_contig_cleanup_ctx(mx3_cam->alloc_ctx); - return err; -} - -static int mx3_camera_remove(struct platform_device *pdev) -{ - struct soc_camera_host *soc_host = to_soc_camera_host(&pdev->dev); - struct mx3_camera_dev *mx3_cam = container_of(soc_host, - struct mx3_camera_dev, soc_host); - - soc_camera_host_unregister(soc_host); - - /* - * The channel has either not been allocated, - * or should have been released - */ - if (WARN_ON(mx3_cam->idmac_channel[0])) - dma_release_channel(&mx3_cam->idmac_channel[0]->dma_chan); - - vb2_dma_contig_cleanup_ctx(mx3_cam->alloc_ctx); - - dmaengine_put(); - - return 0; -} - -static struct platform_driver mx3_camera_driver = { - .driver = { - .name = MX3_CAM_DRV_NAME, - }, - .probe = mx3_camera_probe, - .remove = mx3_camera_remove, -}; - -module_platform_driver(mx3_camera_driver); - -MODULE_DESCRIPTION("i.MX3x SoC Camera Host driver"); -MODULE_AUTHOR("Guennadi Liakhovetski <lg@denx.de>"); -MODULE_LICENSE("GPL v2"); -MODULE_VERSION("0.2.3"); -MODULE_ALIAS("platform:" MX3_CAM_DRV_NAME); diff --git a/drivers/media/platform/soc_camera/omap1_camera.c b/drivers/media/platform/soc_camera/omap1_camera.c deleted file mode 100644 index bd721e35474a..000000000000 --- a/drivers/media/platform/soc_camera/omap1_camera.c +++ /dev/null @@ -1,1738 +0,0 @@ -/* - * V4L2 SoC Camera driver for OMAP1 Camera Interface - * - * Copyright (C) 2010, Janusz Krzysztofik <jkrzyszt@tis.icnet.pl> - * - * Based on V4L2 Driver for i.MXL/i.MXL camera (CSI) host - * Copyright (C) 2008, Paulius Zaleckas <paulius.zaleckas@teltonika.lt> - * Copyright (C) 2009, Darius Augulis <augulis.darius@gmail.com> - * - * Based on PXA SoC camera driver - * Copyright (C) 2006, Sascha Hauer, Pengutronix - * Copyright (C) 2008, Guennadi Liakhovetski <kernel@pengutronix.de> - * - * Hardware specific bits initialy based on former work by Matt Callow - * drivers/media/platform/omap/omap1510cam.c - * Copyright (C) 2006 Matt Callow - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - - -#include <linux/clk.h> -#include <linux/dma-mapping.h> -#include <linux/interrupt.h> -#include <linux/module.h> -#include <linux/platform_device.h> -#include <linux/slab.h> - -#include <linux/platform_data/media/omap1_camera.h> -#include <media/soc_camera.h> -#include <media/drv-intf/soc_mediabus.h> -#include <media/videobuf-dma-contig.h> -#include <media/videobuf-dma-sg.h> - -#include <linux/omap-dma.h> - - -#define DRIVER_NAME "omap1-camera" -#define DRIVER_VERSION "0.0.2" - -#define OMAP_DMA_CAMERA_IF_RX 20 - -/* - * --------------------------------------------------------------------------- - * OMAP1 Camera Interface registers - * --------------------------------------------------------------------------- - */ - -#define REG_CTRLCLOCK 0x00 -#define REG_IT_STATUS 0x04 -#define REG_MODE 0x08 -#define REG_STATUS 0x0C -#define REG_CAMDATA 0x10 -#define REG_GPIO 0x14 -#define REG_PEAK_COUNTER 0x18 - -/* CTRLCLOCK bit shifts */ -#define LCLK_EN BIT(7) -#define DPLL_EN BIT(6) -#define MCLK_EN BIT(5) -#define CAMEXCLK_EN BIT(4) -#define POLCLK BIT(3) -#define FOSCMOD_SHIFT 0 -#define FOSCMOD_MASK (0x7 << FOSCMOD_SHIFT) -#define FOSCMOD_12MHz 0x0 -#define FOSCMOD_6MHz 0x2 -#define FOSCMOD_9_6MHz 0x4 -#define FOSCMOD_24MHz 0x5 -#define FOSCMOD_8MHz 0x6 - -/* IT_STATUS bit shifts */ -#define DATA_TRANSFER BIT(5) -#define FIFO_FULL BIT(4) -#define H_DOWN BIT(3) -#define H_UP BIT(2) -#define V_DOWN BIT(1) -#define V_UP BIT(0) - -/* MODE bit shifts */ -#define RAZ_FIFO BIT(18) -#define EN_FIFO_FULL BIT(17) -#define EN_NIRQ BIT(16) -#define THRESHOLD_SHIFT 9 -#define THRESHOLD_MASK (0x7f << THRESHOLD_SHIFT) -#define DMA BIT(8) -#define EN_H_DOWN BIT(7) -#define EN_H_UP BIT(6) -#define EN_V_DOWN BIT(5) -#define EN_V_UP BIT(4) -#define ORDERCAMD BIT(3) - -#define IRQ_MASK (EN_V_UP | EN_V_DOWN | EN_H_UP | EN_H_DOWN | \ - EN_NIRQ | EN_FIFO_FULL) - -/* STATUS bit shifts */ -#define HSTATUS BIT(1) -#define VSTATUS BIT(0) - -/* GPIO bit shifts */ -#define CAM_RST BIT(0) - -/* end of OMAP1 Camera Interface registers */ - - -#define SOCAM_BUS_FLAGS (V4L2_MBUS_MASTER | \ - V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_HIGH | \ - V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_PCLK_SAMPLE_FALLING | \ - V4L2_MBUS_DATA_ACTIVE_HIGH) - - -#define FIFO_SIZE ((THRESHOLD_MASK >> THRESHOLD_SHIFT) + 1) -#define FIFO_SHIFT __fls(FIFO_SIZE) - -#define DMA_BURST_SHIFT (1 + OMAP_DMA_DATA_BURST_4) -#define DMA_BURST_SIZE (1 << DMA_BURST_SHIFT) - -#define DMA_ELEMENT_SHIFT OMAP_DMA_DATA_TYPE_S32 -#define DMA_ELEMENT_SIZE (1 << DMA_ELEMENT_SHIFT) - -#define DMA_FRAME_SHIFT_CONTIG (FIFO_SHIFT - 1) -#define DMA_FRAME_SHIFT_SG DMA_BURST_SHIFT - -#define DMA_FRAME_SHIFT(x) ((x) == OMAP1_CAM_DMA_CONTIG ? \ - DMA_FRAME_SHIFT_CONTIG : \ - DMA_FRAME_SHIFT_SG) -#define DMA_FRAME_SIZE(x) (1 << DMA_FRAME_SHIFT(x)) -#define DMA_SYNC OMAP_DMA_SYNC_FRAME -#define THRESHOLD_LEVEL DMA_FRAME_SIZE - - -#define MAX_VIDEO_MEM 4 /* arbitrary video memory limit in MB */ - - -/* - * Structures - */ - -/* buffer for one video frame */ -struct omap1_cam_buf { - struct videobuf_buffer vb; - u32 code; - int inwork; - struct scatterlist *sgbuf; - int sgcount; - int bytes_left; - enum videobuf_state result; -}; - -struct omap1_cam_dev { - struct soc_camera_host soc_host; - struct clk *clk; - - unsigned int irq; - void __iomem *base; - - int dma_ch; - - struct omap1_cam_platform_data *pdata; - struct resource *res; - unsigned long pflags; - unsigned long camexclk; - - struct list_head capture; - - /* lock used to protect videobuf */ - spinlock_t lock; - - /* Pointers to DMA buffers */ - struct omap1_cam_buf *active; - struct omap1_cam_buf *ready; - - enum omap1_cam_vb_mode vb_mode; - int (*mmap_mapper)(struct videobuf_queue *q, - struct videobuf_buffer *buf, - struct vm_area_struct *vma); - - u32 reg_cache[0]; -}; - - -static void cam_write(struct omap1_cam_dev *pcdev, u16 reg, u32 val) -{ - pcdev->reg_cache[reg / sizeof(u32)] = val; - __raw_writel(val, pcdev->base + reg); -} - -static u32 cam_read(struct omap1_cam_dev *pcdev, u16 reg, bool from_cache) -{ - return !from_cache ? __raw_readl(pcdev->base + reg) : - pcdev->reg_cache[reg / sizeof(u32)]; -} - -#define CAM_READ(pcdev, reg) \ - cam_read(pcdev, REG_##reg, false) -#define CAM_WRITE(pcdev, reg, val) \ - cam_write(pcdev, REG_##reg, val) -#define CAM_READ_CACHE(pcdev, reg) \ - cam_read(pcdev, REG_##reg, true) - -/* - * Videobuf operations - */ -static int omap1_videobuf_setup(struct videobuf_queue *vq, unsigned int *count, - unsigned int *size) -{ - struct soc_camera_device *icd = vq->priv_data; - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - struct omap1_cam_dev *pcdev = ici->priv; - - *size = icd->sizeimage; - - if (!*count || *count < OMAP1_CAMERA_MIN_BUF_COUNT(pcdev->vb_mode)) - *count = OMAP1_CAMERA_MIN_BUF_COUNT(pcdev->vb_mode); - - if (*size * *count > MAX_VIDEO_MEM * 1024 * 1024) - *count = (MAX_VIDEO_MEM * 1024 * 1024) / *size; - - dev_dbg(icd->parent, - "%s: count=%d, size=%d\n", __func__, *count, *size); - - return 0; -} - -static void free_buffer(struct videobuf_queue *vq, struct omap1_cam_buf *buf, - enum omap1_cam_vb_mode vb_mode) -{ - struct videobuf_buffer *vb = &buf->vb; - - BUG_ON(in_interrupt()); - - videobuf_waiton(vq, vb, 0, 0); - - if (vb_mode == OMAP1_CAM_DMA_CONTIG) { - videobuf_dma_contig_free(vq, vb); - } else { - struct soc_camera_device *icd = vq->priv_data; - struct device *dev = icd->parent; - struct videobuf_dmabuf *dma = videobuf_to_dma(vb); - - videobuf_dma_unmap(dev, dma); - videobuf_dma_free(dma); - } - - vb->state = VIDEOBUF_NEEDS_INIT; -} - -static int omap1_videobuf_prepare(struct videobuf_queue *vq, - struct videobuf_buffer *vb, enum v4l2_field field) -{ - struct soc_camera_device *icd = vq->priv_data; - struct omap1_cam_buf *buf = container_of(vb, struct omap1_cam_buf, vb); - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - struct omap1_cam_dev *pcdev = ici->priv; - int ret; - - WARN_ON(!list_empty(&vb->queue)); - - BUG_ON(NULL == icd->current_fmt); - - buf->inwork = 1; - - if (buf->code != icd->current_fmt->code || vb->field != field || - vb->width != icd->user_width || - vb->height != icd->user_height) { - buf->code = icd->current_fmt->code; - vb->width = icd->user_width; - vb->height = icd->user_height; - vb->field = field; - vb->state = VIDEOBUF_NEEDS_INIT; - } - - vb->size = icd->sizeimage; - - if (vb->baddr && vb->bsize < vb->size) { - ret = -EINVAL; - goto out; - } - - if (vb->state == VIDEOBUF_NEEDS_INIT) { - ret = videobuf_iolock(vq, vb, NULL); - if (ret) - goto fail; - - vb->state = VIDEOBUF_PREPARED; - } - buf->inwork = 0; - - return 0; -fail: - free_buffer(vq, buf, pcdev->vb_mode); -out: - buf->inwork = 0; - return ret; -} - -static void set_dma_dest_params(int dma_ch, struct omap1_cam_buf *buf, - enum omap1_cam_vb_mode vb_mode) -{ - dma_addr_t dma_addr; - unsigned int block_size; - - if (vb_mode == OMAP1_CAM_DMA_CONTIG) { - dma_addr = videobuf_to_dma_contig(&buf->vb); - block_size = buf->vb.size; - } else { - if (WARN_ON(!buf->sgbuf)) { - buf->result = VIDEOBUF_ERROR; - return; - } - dma_addr = sg_dma_address(buf->sgbuf); - if (WARN_ON(!dma_addr)) { - buf->sgbuf = NULL; - buf->result = VIDEOBUF_ERROR; - return; - } - block_size = sg_dma_len(buf->sgbuf); - if (WARN_ON(!block_size)) { - buf->sgbuf = NULL; - buf->result = VIDEOBUF_ERROR; - return; - } - if (unlikely(buf->bytes_left < block_size)) - block_size = buf->bytes_left; - if (WARN_ON(dma_addr & (DMA_FRAME_SIZE(vb_mode) * - DMA_ELEMENT_SIZE - 1))) { - dma_addr = ALIGN(dma_addr, DMA_FRAME_SIZE(vb_mode) * - DMA_ELEMENT_SIZE); - block_size &= ~(DMA_FRAME_SIZE(vb_mode) * - DMA_ELEMENT_SIZE - 1); - } - buf->bytes_left -= block_size; - buf->sgcount++; - } - - omap_set_dma_dest_params(dma_ch, - OMAP_DMA_PORT_EMIFF, OMAP_DMA_AMODE_POST_INC, dma_addr, 0, 0); - omap_set_dma_transfer_params(dma_ch, - OMAP_DMA_DATA_TYPE_S32, DMA_FRAME_SIZE(vb_mode), - block_size >> (DMA_FRAME_SHIFT(vb_mode) + DMA_ELEMENT_SHIFT), - DMA_SYNC, 0, 0); -} - -static struct omap1_cam_buf *prepare_next_vb(struct omap1_cam_dev *pcdev) -{ - struct omap1_cam_buf *buf; - - /* - * If there is already a buffer pointed out by the pcdev->ready, - * (re)use it, otherwise try to fetch and configure a new one. - */ - buf = pcdev->ready; - if (!buf) { - if (list_empty(&pcdev->capture)) - return buf; - buf = list_entry(pcdev->capture.next, - struct omap1_cam_buf, vb.queue); - buf->vb.state = VIDEOBUF_ACTIVE; - pcdev->ready = buf; - list_del_init(&buf->vb.queue); - } - - if (pcdev->vb_mode == OMAP1_CAM_DMA_CONTIG) { - /* - * In CONTIG mode, we can safely enter next buffer parameters - * into the DMA programming register set after the DMA - * has already been activated on the previous buffer - */ - set_dma_dest_params(pcdev->dma_ch, buf, pcdev->vb_mode); - } else { - /* - * In SG mode, the above is not safe since there are probably - * a bunch of sgbufs from previous sglist still pending. - * Instead, mark the sglist fresh for the upcoming - * try_next_sgbuf(). - */ - buf->sgbuf = NULL; - } - - return buf; -} - -static struct scatterlist *try_next_sgbuf(int dma_ch, struct omap1_cam_buf *buf) -{ - struct scatterlist *sgbuf; - - if (likely(buf->sgbuf)) { - /* current sglist is active */ - if (unlikely(!buf->bytes_left)) { - /* indicate sglist complete */ - sgbuf = NULL; - } else { - /* process next sgbuf */ - sgbuf = sg_next(buf->sgbuf); - if (WARN_ON(!sgbuf)) { - buf->result = VIDEOBUF_ERROR; - } else if (WARN_ON(!sg_dma_len(sgbuf))) { - sgbuf = NULL; - buf->result = VIDEOBUF_ERROR; - } - } - buf->sgbuf = sgbuf; - } else { - /* sglist is fresh, initialize it before using */ - struct videobuf_dmabuf *dma = videobuf_to_dma(&buf->vb); - - sgbuf = dma->sglist; - if (!(WARN_ON(!sgbuf))) { - buf->sgbuf = sgbuf; - buf->sgcount = 0; - buf->bytes_left = buf->vb.size; - buf->result = VIDEOBUF_DONE; - } - } - if (sgbuf) - /* - * Put our next sgbuf parameters (address, size) - * into the DMA programming register set. - */ - set_dma_dest_params(dma_ch, buf, OMAP1_CAM_DMA_SG); - - return sgbuf; -} - -static void start_capture(struct omap1_cam_dev *pcdev) -{ - struct omap1_cam_buf *buf = pcdev->active; - u32 ctrlclock = CAM_READ_CACHE(pcdev, CTRLCLOCK); - u32 mode = CAM_READ_CACHE(pcdev, MODE) & ~EN_V_DOWN; - - if (WARN_ON(!buf)) - return; - - /* - * Enable start of frame interrupt, which we will use for activating - * our end of frame watchdog when capture actually starts. - */ - mode |= EN_V_UP; - - if (unlikely(ctrlclock & LCLK_EN)) - /* stop pixel clock before FIFO reset */ - CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock & ~LCLK_EN); - /* reset FIFO */ - CAM_WRITE(pcdev, MODE, mode | RAZ_FIFO); - - omap_start_dma(pcdev->dma_ch); - - if (pcdev->vb_mode == OMAP1_CAM_DMA_SG) { - /* - * In SG mode, it's a good moment for fetching next sgbuf - * from the current sglist and, if available, already putting - * its parameters into the DMA programming register set. - */ - try_next_sgbuf(pcdev->dma_ch, buf); - } - - /* (re)enable pixel clock */ - CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock | LCLK_EN); - /* release FIFO reset */ - CAM_WRITE(pcdev, MODE, mode); -} - -static void suspend_capture(struct omap1_cam_dev *pcdev) -{ - u32 ctrlclock = CAM_READ_CACHE(pcdev, CTRLCLOCK); - - CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock & ~LCLK_EN); - omap_stop_dma(pcdev->dma_ch); -} - -static void disable_capture(struct omap1_cam_dev *pcdev) -{ - u32 mode = CAM_READ_CACHE(pcdev, MODE); - - CAM_WRITE(pcdev, MODE, mode & ~(IRQ_MASK | DMA)); -} - -static void omap1_videobuf_queue(struct videobuf_queue *vq, - struct videobuf_buffer *vb) -{ - struct soc_camera_device *icd = vq->priv_data; - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - struct omap1_cam_dev *pcdev = ici->priv; - struct omap1_cam_buf *buf; - u32 mode; - - list_add_tail(&vb->queue, &pcdev->capture); - vb->state = VIDEOBUF_QUEUED; - - if (pcdev->active) { - /* - * Capture in progress, so don't touch pcdev->ready even if - * empty. Since the transfer of the DMA programming register set - * content to the DMA working register set is done automatically - * by the DMA hardware, this can pretty well happen while we - * are keeping the lock here. Leave fetching it from the queue - * to be done when a next DMA interrupt occures instead. - */ - return; - } - - WARN_ON(pcdev->ready); - - buf = prepare_next_vb(pcdev); - if (WARN_ON(!buf)) - return; - - pcdev->active = buf; - pcdev->ready = NULL; - - dev_dbg(icd->parent, - "%s: capture not active, setup FIFO, start DMA\n", __func__); - mode = CAM_READ_CACHE(pcdev, MODE) & ~THRESHOLD_MASK; - mode |= THRESHOLD_LEVEL(pcdev->vb_mode) << THRESHOLD_SHIFT; - CAM_WRITE(pcdev, MODE, mode | EN_FIFO_FULL | DMA); - - if (pcdev->vb_mode == OMAP1_CAM_DMA_SG) { - /* - * In SG mode, the above prepare_next_vb() didn't actually - * put anything into the DMA programming register set, - * so we have to do it now, before activating DMA. - */ - try_next_sgbuf(pcdev->dma_ch, buf); - } - - start_capture(pcdev); -} - -static void omap1_videobuf_release(struct videobuf_queue *vq, - struct videobuf_buffer *vb) -{ - struct omap1_cam_buf *buf = - container_of(vb, struct omap1_cam_buf, vb); - struct soc_camera_device *icd = vq->priv_data; - struct device *dev = icd->parent; - struct soc_camera_host *ici = to_soc_camera_host(dev); - struct omap1_cam_dev *pcdev = ici->priv; - - switch (vb->state) { - case VIDEOBUF_DONE: - dev_dbg(dev, "%s (done)\n", __func__); - break; - case VIDEOBUF_ACTIVE: - dev_dbg(dev, "%s (active)\n", __func__); - break; - case VIDEOBUF_QUEUED: - dev_dbg(dev, "%s (queued)\n", __func__); - break; - case VIDEOBUF_PREPARED: - dev_dbg(dev, "%s (prepared)\n", __func__); - break; - default: - dev_dbg(dev, "%s (unknown %d)\n", __func__, vb->state); - break; - } - - free_buffer(vq, buf, pcdev->vb_mode); -} - -static void videobuf_done(struct omap1_cam_dev *pcdev, - enum videobuf_state result) -{ - struct omap1_cam_buf *buf = pcdev->active; - struct videobuf_buffer *vb; - struct device *dev = pcdev->soc_host.icd->parent; - - if (WARN_ON(!buf)) { - suspend_capture(pcdev); - disable_capture(pcdev); - return; - } - - if (result == VIDEOBUF_ERROR) - suspend_capture(pcdev); - - vb = &buf->vb; - if (waitqueue_active(&vb->done)) { - if (!pcdev->ready && result != VIDEOBUF_ERROR) { - /* - * No next buffer has been entered into the DMA - * programming register set on time (could be done only - * while the previous DMA interurpt was processed, not - * later), so the last DMA block, be it a whole buffer - * if in CONTIG or its last sgbuf if in SG mode, is - * about to be reused by the just autoreinitialized DMA - * engine, and overwritten with next frame data. Best we - * can do is stopping the capture as soon as possible, - * hopefully before the next frame start. - */ - suspend_capture(pcdev); - } - vb->state = result; - v4l2_get_timestamp(&vb->ts); - if (result != VIDEOBUF_ERROR) - vb->field_count++; - wake_up(&vb->done); - - /* shift in next buffer */ - buf = pcdev->ready; - pcdev->active = buf; - pcdev->ready = NULL; - - if (!buf) { - /* - * No next buffer was ready on time (see above), so - * indicate error condition to force capture restart or - * stop, depending on next buffer already queued or not. - */ - result = VIDEOBUF_ERROR; - prepare_next_vb(pcdev); - - buf = pcdev->ready; - pcdev->active = buf; - pcdev->ready = NULL; - } - } else if (pcdev->ready) { - /* - * In both CONTIG and SG mode, the DMA engine has possibly - * been already autoreinitialized with the preprogrammed - * pcdev->ready buffer. We can either accept this fact - * and just swap the buffers, or provoke an error condition - * and restart capture. The former seems less intrusive. - */ - dev_dbg(dev, "%s: nobody waiting on videobuf, swap with next\n", - __func__); - pcdev->active = pcdev->ready; - - if (pcdev->vb_mode == OMAP1_CAM_DMA_SG) { - /* - * In SG mode, we have to make sure that the buffer we - * are putting back into the pcdev->ready is marked - * fresh. - */ - buf->sgbuf = NULL; - } - pcdev->ready = buf; - - buf = pcdev->active; - } else { - /* - * No next buffer has been entered into - * the DMA programming register set on time. - */ - if (pcdev->vb_mode == OMAP1_CAM_DMA_CONTIG) { - /* - * In CONTIG mode, the DMA engine has already been - * reinitialized with the current buffer. Best we can do - * is not touching it. - */ - dev_dbg(dev, - "%s: nobody waiting on videobuf, reuse it\n", - __func__); - } else { - /* - * In SG mode, the DMA engine has just been - * autoreinitialized with the last sgbuf from the - * current list. Restart capture in order to transfer - * next frame start into the first sgbuf, not the last - * one. - */ - if (result != VIDEOBUF_ERROR) { - suspend_capture(pcdev); - result = VIDEOBUF_ERROR; - } - } - } - - if (!buf) { - dev_dbg(dev, "%s: no more videobufs, stop capture\n", __func__); - disable_capture(pcdev); - return; - } - - if (pcdev->vb_mode == OMAP1_CAM_DMA_CONTIG) { - /* - * In CONTIG mode, the current buffer parameters had already - * been entered into the DMA programming register set while the - * buffer was fetched with prepare_next_vb(), they may have also - * been transferred into the runtime set and already active if - * the DMA still running. - */ - } else { - /* In SG mode, extra steps are required */ - if (result == VIDEOBUF_ERROR) - /* make sure we (re)use sglist from start on error */ - buf->sgbuf = NULL; - - /* - * In any case, enter the next sgbuf parameters into the DMA - * programming register set. They will be used either during - * nearest DMA autoreinitialization or, in case of an error, - * on DMA startup below. - */ - try_next_sgbuf(pcdev->dma_ch, buf); - } - - if (result == VIDEOBUF_ERROR) { - dev_dbg(dev, "%s: videobuf error; reset FIFO, restart DMA\n", - __func__); - start_capture(pcdev); - /* - * In SG mode, the above also resulted in the next sgbuf - * parameters being entered into the DMA programming register - * set, making them ready for next DMA autoreinitialization. - */ - } - - /* - * Finally, try fetching next buffer. - * In CONTIG mode, it will also enter it into the DMA programming - * register set, making it ready for next DMA autoreinitialization. - */ - prepare_next_vb(pcdev); -} - -static void dma_isr(int channel, unsigned short status, void *data) -{ - struct omap1_cam_dev *pcdev = data; - struct omap1_cam_buf *buf = pcdev->active; - unsigned long flags; - - spin_lock_irqsave(&pcdev->lock, flags); - - if (WARN_ON(!buf)) { - suspend_capture(pcdev); - disable_capture(pcdev); - goto out; - } - - if (pcdev->vb_mode == OMAP1_CAM_DMA_CONTIG) { - /* - * In CONTIG mode, assume we have just managed to collect the - * whole frame, hopefully before our end of frame watchdog is - * triggered. Then, all we have to do is disabling the watchdog - * for this frame, and calling videobuf_done() with success - * indicated. - */ - CAM_WRITE(pcdev, MODE, - CAM_READ_CACHE(pcdev, MODE) & ~EN_V_DOWN); - videobuf_done(pcdev, VIDEOBUF_DONE); - } else { - /* - * In SG mode, we have to process every sgbuf from the current - * sglist, one after another. - */ - if (buf->sgbuf) { - /* - * Current sglist not completed yet, try fetching next - * sgbuf, hopefully putting it into the DMA programming - * register set, making it ready for next DMA - * autoreinitialization. - */ - try_next_sgbuf(pcdev->dma_ch, buf); - if (buf->sgbuf) - goto out; - - /* - * No more sgbufs left in the current sglist. This - * doesn't mean that the whole videobuffer is already - * complete, but only that the last sgbuf from the - * current sglist is about to be filled. It will be - * ready on next DMA interrupt, signalled with the - * buf->sgbuf set back to NULL. - */ - if (buf->result != VIDEOBUF_ERROR) { - /* - * Video frame collected without errors so far, - * we can prepare for collecting a next one - * as soon as DMA gets autoreinitialized - * after the current (last) sgbuf is completed. - */ - buf = prepare_next_vb(pcdev); - if (!buf) - goto out; - - try_next_sgbuf(pcdev->dma_ch, buf); - goto out; - } - } - /* end of videobuf */ - videobuf_done(pcdev, buf->result); - } - -out: - spin_unlock_irqrestore(&pcdev->lock, flags); -} - -static irqreturn_t cam_isr(int irq, void *data) -{ - struct omap1_cam_dev *pcdev = data; - struct device *dev = pcdev->soc_host.icd->parent; - struct omap1_cam_buf *buf = pcdev->active; - u32 it_status; - unsigned long flags; - - it_status = CAM_READ(pcdev, IT_STATUS); - if (!it_status) - return IRQ_NONE; - - spin_lock_irqsave(&pcdev->lock, flags); - - if (WARN_ON(!buf)) { - dev_warn(dev, "%s: unhandled camera interrupt, status == %#x\n", - __func__, it_status); - suspend_capture(pcdev); - disable_capture(pcdev); - goto out; - } - - if (unlikely(it_status & FIFO_FULL)) { - dev_warn(dev, "%s: FIFO overflow\n", __func__); - - } else if (it_status & V_DOWN) { - /* end of video frame watchdog */ - if (pcdev->vb_mode == OMAP1_CAM_DMA_CONTIG) { - /* - * In CONTIG mode, the watchdog is disabled with - * successful DMA end of block interrupt, and reenabled - * on next frame start. If we get here, there is nothing - * to check, we must be out of sync. - */ - } else { - if (buf->sgcount == 2) { - /* - * If exactly 2 sgbufs from the next sglist have - * been programmed into the DMA engine (the - * first one already transferred into the DMA - * runtime register set, the second one still - * in the programming set), then we are in sync. - */ - goto out; - } - } - dev_notice(dev, "%s: unexpected end of video frame\n", - __func__); - - } else if (it_status & V_UP) { - u32 mode; - - if (pcdev->vb_mode == OMAP1_CAM_DMA_CONTIG) { - /* - * In CONTIG mode, we need this interrupt every frame - * in oredr to reenable our end of frame watchdog. - */ - mode = CAM_READ_CACHE(pcdev, MODE); - } else { - /* - * In SG mode, the below enabled end of frame watchdog - * is kept on permanently, so we can turn this one shot - * setup off. - */ - mode = CAM_READ_CACHE(pcdev, MODE) & ~EN_V_UP; - } - - if (!(mode & EN_V_DOWN)) { - /* (re)enable end of frame watchdog interrupt */ - mode |= EN_V_DOWN; - } - CAM_WRITE(pcdev, MODE, mode); - goto out; - - } else { - dev_warn(dev, "%s: unhandled camera interrupt, status == %#x\n", - __func__, it_status); - goto out; - } - - videobuf_done(pcdev, VIDEOBUF_ERROR); -out: - spin_unlock_irqrestore(&pcdev->lock, flags); - return IRQ_HANDLED; -} - -static struct videobuf_queue_ops omap1_videobuf_ops = { - .buf_setup = omap1_videobuf_setup, - .buf_prepare = omap1_videobuf_prepare, - .buf_queue = omap1_videobuf_queue, - .buf_release = omap1_videobuf_release, -}; - - -/* - * SOC Camera host operations - */ - -static void sensor_reset(struct omap1_cam_dev *pcdev, bool reset) -{ - /* apply/release camera sensor reset if requested by platform data */ - if (pcdev->pflags & OMAP1_CAMERA_RST_HIGH) - CAM_WRITE(pcdev, GPIO, reset); - else if (pcdev->pflags & OMAP1_CAMERA_RST_LOW) - CAM_WRITE(pcdev, GPIO, !reset); -} - -static int omap1_cam_add_device(struct soc_camera_device *icd) -{ - dev_dbg(icd->parent, "OMAP1 Camera driver attached to camera %d\n", - icd->devnum); - - return 0; -} - -static void omap1_cam_remove_device(struct soc_camera_device *icd) -{ - dev_dbg(icd->parent, - "OMAP1 Camera driver detached from camera %d\n", icd->devnum); -} - -/* - * The following two functions absolutely depend on the fact, that - * there can be only one camera on OMAP1 camera sensor interface - */ -static int omap1_cam_clock_start(struct soc_camera_host *ici) -{ - struct omap1_cam_dev *pcdev = ici->priv; - u32 ctrlclock; - - clk_enable(pcdev->clk); - - /* setup sensor clock */ - ctrlclock = CAM_READ(pcdev, CTRLCLOCK); - ctrlclock &= ~(CAMEXCLK_EN | MCLK_EN | DPLL_EN); - CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock); - - ctrlclock &= ~FOSCMOD_MASK; - switch (pcdev->camexclk) { - case 6000000: - ctrlclock |= CAMEXCLK_EN | FOSCMOD_6MHz; - break; - case 8000000: - ctrlclock |= CAMEXCLK_EN | FOSCMOD_8MHz | DPLL_EN; - break; - case 9600000: - ctrlclock |= CAMEXCLK_EN | FOSCMOD_9_6MHz | DPLL_EN; - break; - case 12000000: - ctrlclock |= CAMEXCLK_EN | FOSCMOD_12MHz; - break; - case 24000000: - ctrlclock |= CAMEXCLK_EN | FOSCMOD_24MHz | DPLL_EN; - default: - break; - } - CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock & ~DPLL_EN); - - /* enable internal clock */ - ctrlclock |= MCLK_EN; - CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock); - - sensor_reset(pcdev, false); - - return 0; -} - -static void omap1_cam_clock_stop(struct soc_camera_host *ici) -{ - struct omap1_cam_dev *pcdev = ici->priv; - u32 ctrlclock; - - suspend_capture(pcdev); - disable_capture(pcdev); - - sensor_reset(pcdev, true); - - /* disable and release system clocks */ - ctrlclock = CAM_READ_CACHE(pcdev, CTRLCLOCK); - ctrlclock &= ~(MCLK_EN | DPLL_EN | CAMEXCLK_EN); - CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock); - - ctrlclock = (ctrlclock & ~FOSCMOD_MASK) | FOSCMOD_12MHz; - CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock); - CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock | MCLK_EN); - - CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock & ~MCLK_EN); - - clk_disable(pcdev->clk); -} - -/* Duplicate standard formats based on host capability of byte swapping */ -static const struct soc_mbus_lookup omap1_cam_formats[] = { -{ - .code = MEDIA_BUS_FMT_UYVY8_2X8, - .fmt = { - .fourcc = V4L2_PIX_FMT_YUYV, - .name = "YUYV", - .bits_per_sample = 8, - .packing = SOC_MBUS_PACKING_2X8_PADHI, - .order = SOC_MBUS_ORDER_BE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, -}, { - .code = MEDIA_BUS_FMT_VYUY8_2X8, - .fmt = { - .fourcc = V4L2_PIX_FMT_YVYU, - .name = "YVYU", - .bits_per_sample = 8, - .packing = SOC_MBUS_PACKING_2X8_PADHI, - .order = SOC_MBUS_ORDER_BE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, -}, { - .code = MEDIA_BUS_FMT_YUYV8_2X8, - .fmt = { - .fourcc = V4L2_PIX_FMT_UYVY, - .name = "UYVY", - .bits_per_sample = 8, - .packing = SOC_MBUS_PACKING_2X8_PADHI, - .order = SOC_MBUS_ORDER_BE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, -}, { - .code = MEDIA_BUS_FMT_YVYU8_2X8, - .fmt = { - .fourcc = V4L2_PIX_FMT_VYUY, - .name = "VYUY", - .bits_per_sample = 8, - .packing = SOC_MBUS_PACKING_2X8_PADHI, - .order = SOC_MBUS_ORDER_BE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, -}, { - .code = MEDIA_BUS_FMT_RGB555_2X8_PADHI_BE, - .fmt = { - .fourcc = V4L2_PIX_FMT_RGB555, - .name = "RGB555", - .bits_per_sample = 8, - .packing = SOC_MBUS_PACKING_2X8_PADHI, - .order = SOC_MBUS_ORDER_BE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, -}, { - .code = MEDIA_BUS_FMT_RGB555_2X8_PADHI_LE, - .fmt = { - .fourcc = V4L2_PIX_FMT_RGB555X, - .name = "RGB555X", - .bits_per_sample = 8, - .packing = SOC_MBUS_PACKING_2X8_PADHI, - .order = SOC_MBUS_ORDER_BE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, -}, { - .code = MEDIA_BUS_FMT_RGB565_2X8_BE, - .fmt = { - .fourcc = V4L2_PIX_FMT_RGB565, - .name = "RGB565", - .bits_per_sample = 8, - .packing = SOC_MBUS_PACKING_2X8_PADHI, - .order = SOC_MBUS_ORDER_BE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, -}, { - .code = MEDIA_BUS_FMT_RGB565_2X8_LE, - .fmt = { - .fourcc = V4L2_PIX_FMT_RGB565X, - .name = "RGB565X", - .bits_per_sample = 8, - .packing = SOC_MBUS_PACKING_2X8_PADHI, - .order = SOC_MBUS_ORDER_BE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, -}, -}; - -static int omap1_cam_get_formats(struct soc_camera_device *icd, - unsigned int idx, struct soc_camera_format_xlate *xlate) -{ - struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - struct device *dev = icd->parent; - int formats = 0, ret; - struct v4l2_subdev_mbus_code_enum code = { - .which = V4L2_SUBDEV_FORMAT_ACTIVE, - .index = idx, - }; - const struct soc_mbus_pixelfmt *fmt; - - ret = v4l2_subdev_call(sd, pad, enum_mbus_code, NULL, &code); - if (ret < 0) - /* No more formats */ - return 0; - - fmt = soc_mbus_get_fmtdesc(code.code); - if (!fmt) { - dev_warn(dev, "%s: unsupported format code #%d: %d\n", __func__, - idx, code.code); - return 0; - } - - /* Check support for the requested bits-per-sample */ - if (fmt->bits_per_sample != 8) - return 0; - - switch (code.code) { - case MEDIA_BUS_FMT_YUYV8_2X8: - case MEDIA_BUS_FMT_YVYU8_2X8: - case MEDIA_BUS_FMT_UYVY8_2X8: - case MEDIA_BUS_FMT_VYUY8_2X8: - case MEDIA_BUS_FMT_RGB555_2X8_PADHI_BE: - case MEDIA_BUS_FMT_RGB555_2X8_PADHI_LE: - case MEDIA_BUS_FMT_RGB565_2X8_BE: - case MEDIA_BUS_FMT_RGB565_2X8_LE: - formats++; - if (xlate) { - xlate->host_fmt = soc_mbus_find_fmtdesc(code.code, - omap1_cam_formats, - ARRAY_SIZE(omap1_cam_formats)); - xlate->code = code.code; - xlate++; - dev_dbg(dev, - "%s: providing format %s as byte swapped code #%d\n", - __func__, xlate->host_fmt->name, code.code); - } - default: - if (xlate) - dev_dbg(dev, - "%s: providing format %s in pass-through mode\n", - __func__, fmt->name); - } - formats++; - if (xlate) { - xlate->host_fmt = fmt; - xlate->code = code.code; - xlate++; - } - - return formats; -} - -static bool is_dma_aligned(s32 bytes_per_line, unsigned int height, - enum omap1_cam_vb_mode vb_mode) -{ - int size = bytes_per_line * height; - - return IS_ALIGNED(bytes_per_line, DMA_ELEMENT_SIZE) && - IS_ALIGNED(size, DMA_FRAME_SIZE(vb_mode) * DMA_ELEMENT_SIZE); -} - -static int dma_align(int *width, int *height, - const struct soc_mbus_pixelfmt *fmt, - enum omap1_cam_vb_mode vb_mode, bool enlarge) -{ - s32 bytes_per_line = soc_mbus_bytes_per_line(*width, fmt); - - if (bytes_per_line < 0) - return bytes_per_line; - - if (!is_dma_aligned(bytes_per_line, *height, vb_mode)) { - unsigned int pxalign = __fls(bytes_per_line / *width); - unsigned int salign = DMA_FRAME_SHIFT(vb_mode) + - DMA_ELEMENT_SHIFT - pxalign; - unsigned int incr = enlarge << salign; - - v4l_bound_align_image(width, 1, *width + incr, 0, - height, 1, *height + incr, 0, salign); - return 0; - } - return 1; -} - -#define subdev_call_with_sense(pcdev, dev, icd, sd, op, function, args...) \ -({ \ - struct soc_camera_sense sense = { \ - .master_clock = pcdev->camexclk, \ - .pixel_clock_max = 0, \ - }; \ - int __ret; \ - \ - if (pcdev->pdata) \ - sense.pixel_clock_max = pcdev->pdata->lclk_khz_max * 1000; \ - icd->sense = &sense; \ - __ret = v4l2_subdev_call(sd, op, function, ##args); \ - icd->sense = NULL; \ - \ - if (sense.flags & SOCAM_SENSE_PCLK_CHANGED) { \ - if (sense.pixel_clock > sense.pixel_clock_max) { \ - dev_err(dev, \ - "%s: pixel clock %lu set by the camera too high!\n", \ - __func__, sense.pixel_clock); \ - __ret = -EINVAL; \ - } \ - } \ - __ret; \ -}) - -static int set_format(struct omap1_cam_dev *pcdev, struct device *dev, - struct soc_camera_device *icd, struct v4l2_subdev *sd, - struct v4l2_subdev_format *format, - const struct soc_camera_format_xlate *xlate) -{ - s32 bytes_per_line; - struct v4l2_mbus_framefmt *mf = &format->format; - int ret = subdev_call_with_sense(pcdev, dev, icd, sd, pad, set_fmt, NULL, format); - - if (ret < 0) { - dev_err(dev, "%s: set_fmt failed\n", __func__); - return ret; - } - - if (mf->code != xlate->code) { - dev_err(dev, "%s: unexpected pixel code change\n", __func__); - return -EINVAL; - } - - bytes_per_line = soc_mbus_bytes_per_line(mf->width, xlate->host_fmt); - if (bytes_per_line < 0) { - dev_err(dev, "%s: soc_mbus_bytes_per_line() failed\n", - __func__); - return bytes_per_line; - } - - if (!is_dma_aligned(bytes_per_line, mf->height, pcdev->vb_mode)) { - dev_err(dev, "%s: resulting geometry %ux%u not DMA aligned\n", - __func__, mf->width, mf->height); - return -EINVAL; - } - return 0; -} - -static int omap1_cam_set_crop(struct soc_camera_device *icd, - const struct v4l2_crop *crop) -{ - const struct v4l2_rect *rect = &crop->c; - const struct soc_camera_format_xlate *xlate = icd->current_fmt; - struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - struct device *dev = icd->parent; - struct soc_camera_host *ici = to_soc_camera_host(dev); - struct omap1_cam_dev *pcdev = ici->priv; - struct v4l2_subdev_format fmt = { - .which = V4L2_SUBDEV_FORMAT_ACTIVE, - }; - struct v4l2_mbus_framefmt *mf = &fmt.format; - int ret; - - ret = subdev_call_with_sense(pcdev, dev, icd, sd, video, s_crop, crop); - if (ret < 0) { - dev_warn(dev, "%s: failed to crop to %ux%u@%u:%u\n", __func__, - rect->width, rect->height, rect->left, rect->top); - return ret; - } - - ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &fmt); - if (ret < 0) { - dev_warn(dev, "%s: failed to fetch current format\n", __func__); - return ret; - } - - ret = dma_align(&mf->width, &mf->height, xlate->host_fmt, pcdev->vb_mode, - false); - if (ret < 0) { - dev_err(dev, "%s: failed to align %ux%u %s with DMA\n", - __func__, mf->width, mf->height, - xlate->host_fmt->name); - return ret; - } - - if (!ret) { - /* sensor returned geometry not DMA aligned, trying to fix */ - ret = set_format(pcdev, dev, icd, sd, &fmt, xlate); - if (ret < 0) { - dev_err(dev, "%s: failed to set format\n", __func__); - return ret; - } - } - - icd->user_width = mf->width; - icd->user_height = mf->height; - - return 0; -} - -static int omap1_cam_set_fmt(struct soc_camera_device *icd, - struct v4l2_format *f) -{ - struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - const struct soc_camera_format_xlate *xlate; - struct device *dev = icd->parent; - struct soc_camera_host *ici = to_soc_camera_host(dev); - struct omap1_cam_dev *pcdev = ici->priv; - struct v4l2_pix_format *pix = &f->fmt.pix; - struct v4l2_subdev_format format = { - .which = V4L2_SUBDEV_FORMAT_ACTIVE, - }; - struct v4l2_mbus_framefmt *mf = &format.format; - int ret; - - xlate = soc_camera_xlate_by_fourcc(icd, pix->pixelformat); - if (!xlate) { - dev_warn(dev, "%s: format %#x not found\n", __func__, - pix->pixelformat); - return -EINVAL; - } - - mf->width = pix->width; - mf->height = pix->height; - mf->field = pix->field; - mf->colorspace = pix->colorspace; - mf->code = xlate->code; - - ret = dma_align(&mf->width, &mf->height, xlate->host_fmt, pcdev->vb_mode, - true); - if (ret < 0) { - dev_err(dev, "%s: failed to align %ux%u %s with DMA\n", - __func__, pix->width, pix->height, - xlate->host_fmt->name); - return ret; - } - - ret = set_format(pcdev, dev, icd, sd, &format, xlate); - if (ret < 0) { - dev_err(dev, "%s: failed to set format\n", __func__); - return ret; - } - - pix->width = mf->width; - pix->height = mf->height; - pix->field = mf->field; - pix->colorspace = mf->colorspace; - icd->current_fmt = xlate; - - return 0; -} - -static int omap1_cam_try_fmt(struct soc_camera_device *icd, - struct v4l2_format *f) -{ - struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - const struct soc_camera_format_xlate *xlate; - struct v4l2_pix_format *pix = &f->fmt.pix; - struct v4l2_subdev_pad_config pad_cfg; - struct v4l2_subdev_format format = { - .which = V4L2_SUBDEV_FORMAT_TRY, - }; - struct v4l2_mbus_framefmt *mf = &format.format; - int ret; - /* TODO: limit to mx1 hardware capabilities */ - - xlate = soc_camera_xlate_by_fourcc(icd, pix->pixelformat); - if (!xlate) { - dev_warn(icd->parent, "Format %#x not found\n", - pix->pixelformat); - return -EINVAL; - } - - mf->width = pix->width; - mf->height = pix->height; - mf->field = pix->field; - mf->colorspace = pix->colorspace; - mf->code = xlate->code; - - /* limit to sensor capabilities */ - ret = v4l2_subdev_call(sd, pad, set_fmt, &pad_cfg, &format); - if (ret < 0) - return ret; - - pix->width = mf->width; - pix->height = mf->height; - pix->field = mf->field; - pix->colorspace = mf->colorspace; - - return 0; -} - -static bool sg_mode; - -/* - * Local mmap_mapper wrapper, - * used for detecting videobuf-dma-contig buffer allocation failures - * and switching to videobuf-dma-sg automatically for future attempts. - */ -static int omap1_cam_mmap_mapper(struct videobuf_queue *q, - struct videobuf_buffer *buf, - struct vm_area_struct *vma) -{ - struct soc_camera_device *icd = q->priv_data; - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - struct omap1_cam_dev *pcdev = ici->priv; - int ret; - - ret = pcdev->mmap_mapper(q, buf, vma); - - if (ret == -ENOMEM) - sg_mode = true; - - return ret; -} - -static void omap1_cam_init_videobuf(struct videobuf_queue *q, - struct soc_camera_device *icd) -{ - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - struct omap1_cam_dev *pcdev = ici->priv; - - if (!sg_mode) - videobuf_queue_dma_contig_init(q, &omap1_videobuf_ops, - icd->parent, &pcdev->lock, - V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_FIELD_NONE, - sizeof(struct omap1_cam_buf), icd, &ici->host_lock); - else - videobuf_queue_sg_init(q, &omap1_videobuf_ops, - icd->parent, &pcdev->lock, - V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_FIELD_NONE, - sizeof(struct omap1_cam_buf), icd, &ici->host_lock); - - /* use videobuf mode (auto)selected with the module parameter */ - pcdev->vb_mode = sg_mode ? OMAP1_CAM_DMA_SG : OMAP1_CAM_DMA_CONTIG; - - /* - * Ensure we substitute the videobuf-dma-contig version of the - * mmap_mapper() callback with our own wrapper, used for switching - * automatically to videobuf-dma-sg on buffer allocation failure. - */ - if (!sg_mode && q->int_ops->mmap_mapper != omap1_cam_mmap_mapper) { - pcdev->mmap_mapper = q->int_ops->mmap_mapper; - q->int_ops->mmap_mapper = omap1_cam_mmap_mapper; - } -} - -static int omap1_cam_reqbufs(struct soc_camera_device *icd, - struct v4l2_requestbuffers *p) -{ - int i; - - /* - * This is for locking debugging only. I removed spinlocks and now I - * check whether .prepare is ever called on a linked buffer, or whether - * a dma IRQ can occur for an in-work or unlinked buffer. Until now - * it hadn't triggered - */ - for (i = 0; i < p->count; i++) { - struct omap1_cam_buf *buf = container_of(icd->vb_vidq.bufs[i], - struct omap1_cam_buf, vb); - buf->inwork = 0; - INIT_LIST_HEAD(&buf->vb.queue); - } - - return 0; -} - -static int omap1_cam_querycap(struct soc_camera_host *ici, - struct v4l2_capability *cap) -{ - /* cap->name is set by the friendly caller:-> */ - strlcpy(cap->card, "OMAP1 Camera", sizeof(cap->card)); - cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; - cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; - - return 0; -} - -static int omap1_cam_set_bus_param(struct soc_camera_device *icd) -{ - struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - struct device *dev = icd->parent; - struct soc_camera_host *ici = to_soc_camera_host(dev); - struct omap1_cam_dev *pcdev = ici->priv; - u32 pixfmt = icd->current_fmt->host_fmt->fourcc; - const struct soc_camera_format_xlate *xlate; - const struct soc_mbus_pixelfmt *fmt; - struct v4l2_mbus_config cfg = {.type = V4L2_MBUS_PARALLEL,}; - unsigned long common_flags; - u32 ctrlclock, mode; - int ret; - - ret = v4l2_subdev_call(sd, video, g_mbus_config, &cfg); - if (!ret) { - common_flags = soc_mbus_config_compatible(&cfg, SOCAM_BUS_FLAGS); - if (!common_flags) { - dev_warn(dev, - "Flags incompatible: camera 0x%x, host 0x%x\n", - cfg.flags, SOCAM_BUS_FLAGS); - return -EINVAL; - } - } else if (ret != -ENOIOCTLCMD) { - return ret; - } else { - common_flags = SOCAM_BUS_FLAGS; - } - - /* Make choices, possibly based on platform configuration */ - if ((common_flags & V4L2_MBUS_PCLK_SAMPLE_RISING) && - (common_flags & V4L2_MBUS_PCLK_SAMPLE_FALLING)) { - if (!pcdev->pdata || - pcdev->pdata->flags & OMAP1_CAMERA_LCLK_RISING) - common_flags &= ~V4L2_MBUS_PCLK_SAMPLE_FALLING; - else - common_flags &= ~V4L2_MBUS_PCLK_SAMPLE_RISING; - } - - cfg.flags = common_flags; - ret = v4l2_subdev_call(sd, video, s_mbus_config, &cfg); - if (ret < 0 && ret != -ENOIOCTLCMD) { - dev_dbg(dev, "camera s_mbus_config(0x%lx) returned %d\n", - common_flags, ret); - return ret; - } - - ctrlclock = CAM_READ_CACHE(pcdev, CTRLCLOCK); - if (ctrlclock & LCLK_EN) - CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock & ~LCLK_EN); - - if (common_flags & V4L2_MBUS_PCLK_SAMPLE_RISING) { - dev_dbg(dev, "CTRLCLOCK_REG |= POLCLK\n"); - ctrlclock |= POLCLK; - } else { - dev_dbg(dev, "CTRLCLOCK_REG &= ~POLCLK\n"); - ctrlclock &= ~POLCLK; - } - CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock & ~LCLK_EN); - - if (ctrlclock & LCLK_EN) - CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock); - - /* select bus endianness */ - xlate = soc_camera_xlate_by_fourcc(icd, pixfmt); - fmt = xlate->host_fmt; - - mode = CAM_READ(pcdev, MODE) & ~(RAZ_FIFO | IRQ_MASK | DMA); - if (fmt->order == SOC_MBUS_ORDER_LE) { - dev_dbg(dev, "MODE_REG &= ~ORDERCAMD\n"); - CAM_WRITE(pcdev, MODE, mode & ~ORDERCAMD); - } else { - dev_dbg(dev, "MODE_REG |= ORDERCAMD\n"); - CAM_WRITE(pcdev, MODE, mode | ORDERCAMD); - } - - return 0; -} - -static unsigned int omap1_cam_poll(struct file *file, poll_table *pt) -{ - struct soc_camera_device *icd = file->private_data; - struct omap1_cam_buf *buf; - - buf = list_entry(icd->vb_vidq.stream.next, struct omap1_cam_buf, - vb.stream); - - poll_wait(file, &buf->vb.done, pt); - - if (buf->vb.state == VIDEOBUF_DONE || - buf->vb.state == VIDEOBUF_ERROR) - return POLLIN | POLLRDNORM; - - return 0; -} - -static struct soc_camera_host_ops omap1_host_ops = { - .owner = THIS_MODULE, - .add = omap1_cam_add_device, - .remove = omap1_cam_remove_device, - .clock_start = omap1_cam_clock_start, - .clock_stop = omap1_cam_clock_stop, - .get_formats = omap1_cam_get_formats, - .set_crop = omap1_cam_set_crop, - .set_fmt = omap1_cam_set_fmt, - .try_fmt = omap1_cam_try_fmt, - .init_videobuf = omap1_cam_init_videobuf, - .reqbufs = omap1_cam_reqbufs, - .querycap = omap1_cam_querycap, - .set_bus_param = omap1_cam_set_bus_param, - .poll = omap1_cam_poll, -}; - -static int omap1_cam_probe(struct platform_device *pdev) -{ - struct omap1_cam_dev *pcdev; - struct resource *res; - struct clk *clk; - void __iomem *base; - unsigned int irq; - int err = 0; - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - irq = platform_get_irq(pdev, 0); - if (!res || (int)irq <= 0) { - err = -ENODEV; - goto exit; - } - - clk = clk_get(&pdev->dev, "armper_ck"); - if (IS_ERR(clk)) { - err = PTR_ERR(clk); - goto exit; - } - - pcdev = kzalloc(sizeof(*pcdev) + resource_size(res), GFP_KERNEL); - if (!pcdev) { - dev_err(&pdev->dev, "Could not allocate pcdev\n"); - err = -ENOMEM; - goto exit_put_clk; - } - - pcdev->res = res; - pcdev->clk = clk; - - pcdev->pdata = pdev->dev.platform_data; - if (pcdev->pdata) { - pcdev->pflags = pcdev->pdata->flags; - pcdev->camexclk = pcdev->pdata->camexclk_khz * 1000; - } - - switch (pcdev->camexclk) { - case 6000000: - case 8000000: - case 9600000: - case 12000000: - case 24000000: - break; - default: - /* pcdev->camexclk != 0 => pcdev->pdata != NULL */ - dev_warn(&pdev->dev, - "Incorrect sensor clock frequency %ld kHz, " - "should be one of 0, 6, 8, 9.6, 12 or 24 MHz, " - "please correct your platform data\n", - pcdev->pdata->camexclk_khz); - pcdev->camexclk = 0; - case 0: - dev_info(&pdev->dev, "Not providing sensor clock\n"); - } - - INIT_LIST_HEAD(&pcdev->capture); - spin_lock_init(&pcdev->lock); - - /* - * Request the region. - */ - if (!request_mem_region(res->start, resource_size(res), DRIVER_NAME)) { - err = -EBUSY; - goto exit_kfree; - } - - base = ioremap(res->start, resource_size(res)); - if (!base) { - err = -ENOMEM; - goto exit_release; - } - pcdev->irq = irq; - pcdev->base = base; - - sensor_reset(pcdev, true); - - err = omap_request_dma(OMAP_DMA_CAMERA_IF_RX, DRIVER_NAME, - dma_isr, (void *)pcdev, &pcdev->dma_ch); - if (err < 0) { - dev_err(&pdev->dev, "Can't request DMA for OMAP1 Camera\n"); - err = -EBUSY; - goto exit_iounmap; - } - dev_dbg(&pdev->dev, "got DMA channel %d\n", pcdev->dma_ch); - - /* preconfigure DMA */ - omap_set_dma_src_params(pcdev->dma_ch, OMAP_DMA_PORT_TIPB, - OMAP_DMA_AMODE_CONSTANT, res->start + REG_CAMDATA, - 0, 0); - omap_set_dma_dest_burst_mode(pcdev->dma_ch, OMAP_DMA_DATA_BURST_4); - /* setup DMA autoinitialization */ - omap_dma_link_lch(pcdev->dma_ch, pcdev->dma_ch); - - err = request_irq(pcdev->irq, cam_isr, 0, DRIVER_NAME, pcdev); - if (err) { - dev_err(&pdev->dev, "Camera interrupt register failed\n"); - goto exit_free_dma; - } - - pcdev->soc_host.drv_name = DRIVER_NAME; - pcdev->soc_host.ops = &omap1_host_ops; - pcdev->soc_host.priv = pcdev; - pcdev->soc_host.v4l2_dev.dev = &pdev->dev; - pcdev->soc_host.nr = pdev->id; - - err = soc_camera_host_register(&pcdev->soc_host); - if (err) - goto exit_free_irq; - - dev_info(&pdev->dev, "OMAP1 Camera Interface driver loaded\n"); - - return 0; - -exit_free_irq: - free_irq(pcdev->irq, pcdev); -exit_free_dma: - omap_free_dma(pcdev->dma_ch); -exit_iounmap: - iounmap(base); -exit_release: - release_mem_region(res->start, resource_size(res)); -exit_kfree: - kfree(pcdev); -exit_put_clk: - clk_put(clk); -exit: - return err; -} - -static int omap1_cam_remove(struct platform_device *pdev) -{ - struct soc_camera_host *soc_host = to_soc_camera_host(&pdev->dev); - struct omap1_cam_dev *pcdev = container_of(soc_host, - struct omap1_cam_dev, soc_host); - struct resource *res; - - free_irq(pcdev->irq, pcdev); - - omap_free_dma(pcdev->dma_ch); - - soc_camera_host_unregister(soc_host); - - iounmap(pcdev->base); - - res = pcdev->res; - release_mem_region(res->start, resource_size(res)); - - clk_put(pcdev->clk); - - kfree(pcdev); - - dev_info(&pdev->dev, "OMAP1 Camera Interface driver unloaded\n"); - - return 0; -} - -static struct platform_driver omap1_cam_driver = { - .driver = { - .name = DRIVER_NAME, - }, - .probe = omap1_cam_probe, - .remove = omap1_cam_remove, -}; - -module_platform_driver(omap1_cam_driver); - -module_param(sg_mode, bool, 0644); -MODULE_PARM_DESC(sg_mode, "videobuf mode, 0: dma-contig (default), 1: dma-sg"); - -MODULE_DESCRIPTION("OMAP1 Camera Interface driver"); -MODULE_AUTHOR("Janusz Krzysztofik <jkrzyszt@tis.icnet.pl>"); -MODULE_LICENSE("GPL v2"); -MODULE_VERSION(DRIVER_VERSION); -MODULE_ALIAS("platform:" DRIVER_NAME); diff --git a/drivers/media/platform/soc_camera/pxa_camera.c b/drivers/media/platform/soc_camera/pxa_camera.c index 415f3bda60bf..2aaf4a8f71a0 100644 --- a/drivers/media/platform/soc_camera/pxa_camera.c +++ b/drivers/media/platform/soc_camera/pxa_camera.c @@ -28,6 +28,9 @@ #include <linux/clk.h> #include <linux/sched.h> #include <linux/slab.h> +#include <linux/dmaengine.h> +#include <linux/dma-mapping.h> +#include <linux/dma/pxa-dma.h> #include <media/v4l2-common.h> #include <media/v4l2-dev.h> @@ -38,7 +41,6 @@ #include <linux/videodev2.h> -#include <mach/dma.h> #include <linux/platform_data/media/camera-pxa.h> #define PXA_CAM_VERSION "0.0.6" @@ -175,21 +177,16 @@ enum pxa_camera_active_dma { DMA_V = 0x4, }; -/* descriptor needed for the PXA DMA engine */ -struct pxa_cam_dma { - dma_addr_t sg_dma; - struct pxa_dma_desc *sg_cpu; - size_t sg_size; - int sglen; -}; - /* buffer for one video frame */ struct pxa_buffer { /* common v4l buffer stuff -- must be first */ struct videobuf_buffer vb; u32 code; /* our descriptor lists for Y, U and V channels */ - struct pxa_cam_dma dmas[3]; + struct dma_async_tx_descriptor *descs[3]; + dma_cookie_t cookie[3]; + struct scatterlist *sg[3]; + int sg_len[3]; int inwork; enum pxa_camera_active_dma active_dma; }; @@ -207,7 +204,7 @@ struct pxa_camera_dev { void __iomem *base; int channels; - unsigned int dma_chans[3]; + struct dma_chan *dma_chans[3]; struct pxacamera_platform_data *pdata; struct resource *res; @@ -222,7 +219,7 @@ struct pxa_camera_dev { spinlock_t lock; struct pxa_buffer *active; - struct pxa_dma_desc *sg_tail[3]; + struct tasklet_struct task_eof; u32 save_cicr[5]; }; @@ -258,7 +255,6 @@ static int pxa_videobuf_setup(struct videobuf_queue *vq, unsigned int *count, static void free_buffer(struct videobuf_queue *vq, struct pxa_buffer *buf) { struct soc_camera_device *icd = vq->priv_data; - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); struct videobuf_dmabuf *dma = videobuf_to_dma(&buf->vb); int i; @@ -272,42 +268,45 @@ static void free_buffer(struct videobuf_queue *vq, struct pxa_buffer *buf) * longer in STATE_QUEUED or STATE_ACTIVE */ videobuf_waiton(vq, &buf->vb, 0, 0); - videobuf_dma_unmap(vq->dev, dma); - videobuf_dma_free(dma); - for (i = 0; i < ARRAY_SIZE(buf->dmas); i++) { - if (buf->dmas[i].sg_cpu) - dma_free_coherent(ici->v4l2_dev.dev, - buf->dmas[i].sg_size, - buf->dmas[i].sg_cpu, - buf->dmas[i].sg_dma); - buf->dmas[i].sg_cpu = NULL; + for (i = 0; i < 3 && buf->descs[i]; i++) { + dmaengine_desc_free(buf->descs[i]); + kfree(buf->sg[i]); + buf->descs[i] = NULL; + buf->sg[i] = NULL; + buf->sg_len[i] = 0; } + videobuf_dma_unmap(vq->dev, dma); + videobuf_dma_free(dma); buf->vb.state = VIDEOBUF_NEEDS_INIT; + + dev_dbg(icd->parent, "%s end (vb=0x%p) 0x%08lx %d\n", __func__, + &buf->vb, buf->vb.baddr, buf->vb.bsize); } -static int calculate_dma_sglen(struct scatterlist *sglist, int sglen, - int sg_first_ofs, int size) +static void pxa_camera_dma_irq(struct pxa_camera_dev *pcdev, + enum pxa_camera_active_dma act_dma); + +static void pxa_camera_dma_irq_y(void *data) { - int i, offset, dma_len, xfer_len; - struct scatterlist *sg; + struct pxa_camera_dev *pcdev = data; - offset = sg_first_ofs; - for_each_sg(sglist, sg, sglen, i) { - dma_len = sg_dma_len(sg); + pxa_camera_dma_irq(pcdev, DMA_Y); +} - /* PXA27x Developer's Manual 27.4.4.1: round up to 8 bytes */ - xfer_len = roundup(min(dma_len - offset, size), 8); +static void pxa_camera_dma_irq_u(void *data) +{ + struct pxa_camera_dev *pcdev = data; - size = max(0, size - xfer_len); - offset = 0; - if (size == 0) - break; - } + pxa_camera_dma_irq(pcdev, DMA_U); +} + +static void pxa_camera_dma_irq_v(void *data) +{ + struct pxa_camera_dev *pcdev = data; - BUG_ON(size != 0); - return i + 1; + pxa_camera_dma_irq(pcdev, DMA_V); } /** @@ -318,93 +317,53 @@ static int calculate_dma_sglen(struct scatterlist *sglist, int sglen, * @channel: dma channel (0 => 'Y', 1 => 'U', 2 => 'V') * @cibr: camera Receive Buffer Register * @size: bytes to transfer - * @sg_first: first element of sg_list - * @sg_first_ofs: offset in first element of sg_list + * @offset: offset in videobuffer of the first byte to transfer * * Prepares the pxa dma descriptors to transfer one camera channel. - * Beware sg_first and sg_first_ofs are both input and output parameters. * - * Returns 0 or -ENOMEM if no coherent memory is available + * Returns 0 if success or -ENOMEM if no memory is available */ static int pxa_init_dma_channel(struct pxa_camera_dev *pcdev, struct pxa_buffer *buf, struct videobuf_dmabuf *dma, int channel, - int cibr, int size, - struct scatterlist **sg_first, int *sg_first_ofs) + int cibr, int size, int offset) { - struct pxa_cam_dma *pxa_dma = &buf->dmas[channel]; - struct device *dev = pcdev->soc_host.v4l2_dev.dev; - struct scatterlist *sg; - int i, offset, sglen; - int dma_len = 0, xfer_len = 0; - - if (pxa_dma->sg_cpu) - dma_free_coherent(dev, pxa_dma->sg_size, - pxa_dma->sg_cpu, pxa_dma->sg_dma); - - sglen = calculate_dma_sglen(*sg_first, dma->sglen, - *sg_first_ofs, size); - - pxa_dma->sg_size = (sglen + 1) * sizeof(struct pxa_dma_desc); - pxa_dma->sg_cpu = dma_alloc_coherent(dev, pxa_dma->sg_size, - &pxa_dma->sg_dma, GFP_KERNEL); - if (!pxa_dma->sg_cpu) - return -ENOMEM; - - pxa_dma->sglen = sglen; - offset = *sg_first_ofs; - - dev_dbg(dev, "DMA: sg_first=%p, sglen=%d, ofs=%d, dma.desc=%x\n", - *sg_first, sglen, *sg_first_ofs, pxa_dma->sg_dma); - - - for_each_sg(*sg_first, sg, sglen, i) { - dma_len = sg_dma_len(sg); - - /* PXA27x Developer's Manual 27.4.4.1: round up to 8 bytes */ - xfer_len = roundup(min(dma_len - offset, size), 8); - - size = max(0, size - xfer_len); - - pxa_dma->sg_cpu[i].dsadr = pcdev->res->start + cibr; - pxa_dma->sg_cpu[i].dtadr = sg_dma_address(sg) + offset; - pxa_dma->sg_cpu[i].dcmd = - DCMD_FLOWSRC | DCMD_BURST8 | DCMD_INCTRGADDR | xfer_len; -#ifdef DEBUG - if (!i) - pxa_dma->sg_cpu[i].dcmd |= DCMD_STARTIRQEN; -#endif - pxa_dma->sg_cpu[i].ddadr = - pxa_dma->sg_dma + (i + 1) * sizeof(struct pxa_dma_desc); - - dev_vdbg(dev, "DMA: desc.%08x->@phys=0x%08x, len=%d\n", - pxa_dma->sg_dma + i * sizeof(struct pxa_dma_desc), - sg_dma_address(sg) + offset, xfer_len); - offset = 0; - - if (size == 0) - break; + struct dma_chan *dma_chan = pcdev->dma_chans[channel]; + struct scatterlist *sg = buf->sg[channel]; + int sglen = buf->sg_len[channel]; + struct dma_async_tx_descriptor *tx; + + tx = dmaengine_prep_slave_sg(dma_chan, sg, sglen, DMA_DEV_TO_MEM, + DMA_PREP_INTERRUPT | DMA_CTRL_REUSE); + if (!tx) { + dev_err(pcdev->soc_host.v4l2_dev.dev, + "dmaengine_prep_slave_sg failed\n"); + goto fail; } - pxa_dma->sg_cpu[sglen].ddadr = DDADR_STOP; - pxa_dma->sg_cpu[sglen].dcmd = DCMD_FLOWSRC | DCMD_BURST8 | DCMD_ENDIRQEN; - - /* - * Handle 1 special case : - * - in 3 planes (YUV422P format), we might finish with xfer_len equal - * to dma_len (end on PAGE boundary). In this case, the sg element - * for next plane should be the next after the last used to store the - * last scatter gather RAM page - */ - if (xfer_len >= dma_len) { - *sg_first_ofs = xfer_len - dma_len; - *sg_first = sg_next(sg); - } else { - *sg_first_ofs = xfer_len; - *sg_first = sg; + tx->callback_param = pcdev; + switch (channel) { + case 0: + tx->callback = pxa_camera_dma_irq_y; + break; + case 1: + tx->callback = pxa_camera_dma_irq_u; + break; + case 2: + tx->callback = pxa_camera_dma_irq_v; + break; } + buf->descs[channel] = tx; return 0; +fail: + kfree(sg); + + dev_dbg(pcdev->soc_host.v4l2_dev.dev, + "%s (vb=0x%p) dma_tx=%p\n", + __func__, &buf->vb, tx); + + return -ENOMEM; } static void pxa_videobuf_set_actdma(struct pxa_camera_dev *pcdev, @@ -431,6 +390,7 @@ static int pxa_videobuf_prepare(struct videobuf_queue *vq, struct pxa_buffer *buf = container_of(vb, struct pxa_buffer, vb); int ret; int size_y, size_u = 0, size_v = 0; + size_t sizes[3]; dev_dbg(dev, "%s (vb=0x%p) 0x%08lx %d\n", __func__, vb, vb->baddr, vb->bsize); @@ -473,13 +433,11 @@ static int pxa_videobuf_prepare(struct videobuf_queue *vq, if (vb->state == VIDEOBUF_NEEDS_INIT) { int size = vb->size; - int next_ofs = 0; struct videobuf_dmabuf *dma = videobuf_to_dma(vb); - struct scatterlist *sg; ret = videobuf_iolock(vq, vb, NULL); if (ret) - goto fail; + goto out; if (pcdev->channels == 3) { size_y = size / 2; @@ -488,11 +446,19 @@ static int pxa_videobuf_prepare(struct videobuf_queue *vq, size_y = size; } - sg = dma->sglist; + sizes[0] = size_y; + sizes[1] = size_u; + sizes[2] = size_v; + ret = sg_split(dma->sglist, dma->sglen, 0, pcdev->channels, + sizes, buf->sg, buf->sg_len, GFP_KERNEL); + if (ret < 0) { + dev_err(dev, "sg_split failed: %d\n", ret); + goto fail; + } /* init DMA for Y channel */ - ret = pxa_init_dma_channel(pcdev, buf, dma, 0, CIBR0, size_y, - &sg, &next_ofs); + ret = pxa_init_dma_channel(pcdev, buf, dma, 0, CIBR0, + size_y, 0); if (ret) { dev_err(dev, "DMA initialization for Y/RGB failed\n"); goto fail; @@ -501,19 +467,19 @@ static int pxa_videobuf_prepare(struct videobuf_queue *vq, /* init DMA for U channel */ if (size_u) ret = pxa_init_dma_channel(pcdev, buf, dma, 1, CIBR1, - size_u, &sg, &next_ofs); + size_u, size_y); if (ret) { dev_err(dev, "DMA initialization for U failed\n"); - goto fail_u; + goto fail; } /* init DMA for V channel */ if (size_v) ret = pxa_init_dma_channel(pcdev, buf, dma, 2, CIBR2, - size_v, &sg, &next_ofs); + size_v, size_y + size_u); if (ret) { dev_err(dev, "DMA initialization for V failed\n"); - goto fail_v; + goto fail; } vb->state = VIDEOBUF_PREPARED; @@ -524,12 +490,6 @@ static int pxa_videobuf_prepare(struct videobuf_queue *vq, return 0; -fail_v: - dma_free_coherent(dev, buf->dmas[1].sg_size, - buf->dmas[1].sg_cpu, buf->dmas[1].sg_dma); -fail_u: - dma_free_coherent(dev, buf->dmas[0].sg_size, - buf->dmas[0].sg_cpu, buf->dmas[0].sg_dma); fail: free_buffer(vq, buf); out: @@ -553,10 +513,8 @@ static void pxa_dma_start_channels(struct pxa_camera_dev *pcdev) for (i = 0; i < pcdev->channels; i++) { dev_dbg(pcdev->soc_host.v4l2_dev.dev, - "%s (channel=%d) ddadr=%08x\n", __func__, - i, active->dmas[i].sg_dma); - DDADR(pcdev->dma_chans[i]) = active->dmas[i].sg_dma; - DCSR(pcdev->dma_chans[i]) = DCSR_RUN; + "%s (channel=%d)\n", __func__, i); + dma_async_issue_pending(pcdev->dma_chans[i]); } } @@ -567,7 +525,7 @@ static void pxa_dma_stop_channels(struct pxa_camera_dev *pcdev) for (i = 0; i < pcdev->channels; i++) { dev_dbg(pcdev->soc_host.v4l2_dev.dev, "%s (channel=%d)\n", __func__, i); - DCSR(pcdev->dma_chans[i]) = 0; + dmaengine_terminate_all(pcdev->dma_chans[i]); } } @@ -575,18 +533,12 @@ static void pxa_dma_add_tail_buf(struct pxa_camera_dev *pcdev, struct pxa_buffer *buf) { int i; - struct pxa_dma_desc *buf_last_desc; for (i = 0; i < pcdev->channels; i++) { - buf_last_desc = buf->dmas[i].sg_cpu + buf->dmas[i].sglen; - buf_last_desc->ddadr = DDADR_STOP; - - if (pcdev->sg_tail[i]) - /* Link the new buffer to the old tail */ - pcdev->sg_tail[i]->ddadr = buf->dmas[i].sg_dma; - - /* Update the channel tail */ - pcdev->sg_tail[i] = buf_last_desc; + buf->cookie[i] = dmaengine_submit(buf->descs[i]); + dev_dbg(pcdev->soc_host.v4l2_dev.dev, + "%s (channel=%d) : submit vb=%p cookie=%d\n", + __func__, i, buf, buf->descs[i]->cookie); } } @@ -603,6 +555,7 @@ static void pxa_camera_start_capture(struct pxa_camera_dev *pcdev) unsigned long cicr0; dev_dbg(pcdev->soc_host.v4l2_dev.dev, "%s\n", __func__); + __raw_writel(__raw_readl(pcdev->base + CISR), pcdev->base + CISR); /* Enable End-Of-Frame Interrupt */ cicr0 = __raw_readl(pcdev->base + CICR0) | CICR0_ENB; cicr0 &= ~CICR0_EOFM; @@ -677,8 +630,6 @@ static void pxa_camera_wakeup(struct pxa_camera_dev *pcdev, struct videobuf_buffer *vb, struct pxa_buffer *buf) { - int i; - /* _init is used to debug races, see comment in pxa_camera_reqbufs() */ list_del_init(&vb->queue); vb->state = VIDEOBUF_DONE; @@ -690,8 +641,6 @@ static void pxa_camera_wakeup(struct pxa_camera_dev *pcdev, if (list_empty(&pcdev->capture)) { pxa_camera_stop_capture(pcdev); - for (i = 0; i < pcdev->channels; i++) - pcdev->sg_tail[i] = NULL; return; } @@ -715,50 +664,41 @@ static void pxa_camera_wakeup(struct pxa_camera_dev *pcdev, * * Context: should only be called within the dma irq handler */ -static void pxa_camera_check_link_miss(struct pxa_camera_dev *pcdev) +static void pxa_camera_check_link_miss(struct pxa_camera_dev *pcdev, + dma_cookie_t last_submitted, + dma_cookie_t last_issued) { - int i, is_dma_stopped = 1; + bool is_dma_stopped = last_submitted != last_issued; - for (i = 0; i < pcdev->channels; i++) - if (DDADR(pcdev->dma_chans[i]) != DDADR_STOP) - is_dma_stopped = 0; dev_dbg(pcdev->soc_host.v4l2_dev.dev, - "%s : top queued buffer=%p, dma_stopped=%d\n", + "%s : top queued buffer=%p, is_dma_stopped=%d\n", __func__, pcdev->active, is_dma_stopped); + if (pcdev->active && is_dma_stopped) pxa_camera_start_capture(pcdev); } -static void pxa_camera_dma_irq(int channel, struct pxa_camera_dev *pcdev, +static void pxa_camera_dma_irq(struct pxa_camera_dev *pcdev, enum pxa_camera_active_dma act_dma) { struct device *dev = pcdev->soc_host.v4l2_dev.dev; - struct pxa_buffer *buf; + struct pxa_buffer *buf, *last_buf; unsigned long flags; - u32 status, camera_status, overrun; + u32 camera_status, overrun; + int chan; struct videobuf_buffer *vb; + enum dma_status last_status; + dma_cookie_t last_issued; spin_lock_irqsave(&pcdev->lock, flags); - status = DCSR(channel); - DCSR(channel) = status; - camera_status = __raw_readl(pcdev->base + CISR); + dev_dbg(dev, "camera dma irq, cisr=0x%x dma=%d\n", + camera_status, act_dma); overrun = CISR_IFO_0; if (pcdev->channels == 3) overrun |= CISR_IFO_1 | CISR_IFO_2; - if (status & DCSR_BUSERR) { - dev_err(dev, "DMA Bus Error IRQ!\n"); - goto out; - } - - if (!(status & (DCSR_ENDINTR | DCSR_STARTINTR))) { - dev_err(dev, "Unknown DMA IRQ source, status: 0x%08x\n", - status); - goto out; - } - /* * pcdev->active should not be NULL in DMA irq handler. * @@ -778,52 +718,47 @@ static void pxa_camera_dma_irq(int channel, struct pxa_camera_dev *pcdev, buf = container_of(vb, struct pxa_buffer, vb); WARN_ON(buf->inwork || list_empty(&vb->queue)); - dev_dbg(dev, "%s channel=%d %s%s(vb=0x%p) dma.desc=%x\n", - __func__, channel, status & DCSR_STARTINTR ? "SOF " : "", - status & DCSR_ENDINTR ? "EOF " : "", vb, DDADR(channel)); - - if (status & DCSR_ENDINTR) { - /* - * It's normal if the last frame creates an overrun, as there - * are no more DMA descriptors to fetch from QCI fifos - */ - if (camera_status & overrun && - !list_is_last(pcdev->capture.next, &pcdev->capture)) { - dev_dbg(dev, "FIFO overrun! CISR: %x\n", - camera_status); - pxa_camera_stop_capture(pcdev); - pxa_camera_start_capture(pcdev); - goto out; - } - buf->active_dma &= ~act_dma; - if (!buf->active_dma) { - pxa_camera_wakeup(pcdev, vb, buf); - pxa_camera_check_link_miss(pcdev); - } + /* + * It's normal if the last frame creates an overrun, as there + * are no more DMA descriptors to fetch from QCI fifos + */ + switch (act_dma) { + case DMA_U: + chan = 1; + break; + case DMA_V: + chan = 2; + break; + default: + chan = 0; + break; + } + last_buf = list_entry(pcdev->capture.prev, + struct pxa_buffer, vb.queue); + last_status = dma_async_is_tx_complete(pcdev->dma_chans[chan], + last_buf->cookie[chan], + NULL, &last_issued); + if (camera_status & overrun && + last_status != DMA_COMPLETE) { + dev_dbg(dev, "FIFO overrun! CISR: %x\n", + camera_status); + pxa_camera_stop_capture(pcdev); + list_for_each_entry(buf, &pcdev->capture, vb.queue) + pxa_dma_add_tail_buf(pcdev, buf); + pxa_camera_start_capture(pcdev); + goto out; + } + buf->active_dma &= ~act_dma; + if (!buf->active_dma) { + pxa_camera_wakeup(pcdev, vb, buf); + pxa_camera_check_link_miss(pcdev, last_buf->cookie[chan], + last_issued); } out: spin_unlock_irqrestore(&pcdev->lock, flags); } -static void pxa_camera_dma_irq_y(int channel, void *data) -{ - struct pxa_camera_dev *pcdev = data; - pxa_camera_dma_irq(channel, pcdev, DMA_Y); -} - -static void pxa_camera_dma_irq_u(int channel, void *data) -{ - struct pxa_camera_dev *pcdev = data; - pxa_camera_dma_irq(channel, pcdev, DMA_U); -} - -static void pxa_camera_dma_irq_v(int channel, void *data) -{ - struct pxa_camera_dev *pcdev = data; - pxa_camera_dma_irq(channel, pcdev, DMA_V); -} - static struct videobuf_queue_ops pxa_videobuf_ops = { .buf_setup = pxa_videobuf_setup, .buf_prepare = pxa_videobuf_prepare, @@ -920,13 +855,35 @@ static void pxa_camera_deactivate(struct pxa_camera_dev *pcdev) clk_disable_unprepare(pcdev->clk); } -static irqreturn_t pxa_camera_irq(int irq, void *data) +static void pxa_camera_eof(unsigned long arg) { - struct pxa_camera_dev *pcdev = data; - unsigned long status, cifr, cicr0; + struct pxa_camera_dev *pcdev = (struct pxa_camera_dev *)arg; + unsigned long cifr; struct pxa_buffer *buf; struct videobuf_buffer *vb; + dev_dbg(pcdev->soc_host.v4l2_dev.dev, + "Camera interrupt status 0x%x\n", + __raw_readl(pcdev->base + CISR)); + + /* Reset the FIFOs */ + cifr = __raw_readl(pcdev->base + CIFR) | CIFR_RESET_F; + __raw_writel(cifr, pcdev->base + CIFR); + + pcdev->active = list_first_entry(&pcdev->capture, + struct pxa_buffer, vb.queue); + vb = &pcdev->active->vb; + buf = container_of(vb, struct pxa_buffer, vb); + pxa_videobuf_set_actdma(pcdev, buf); + + pxa_dma_start_channels(pcdev); +} + +static irqreturn_t pxa_camera_irq(int irq, void *data) +{ + struct pxa_camera_dev *pcdev = data; + unsigned long status, cicr0; + status = __raw_readl(pcdev->base + CISR); dev_dbg(pcdev->soc_host.v4l2_dev.dev, "Camera interrupt status 0x%lx\n", status); @@ -937,20 +894,9 @@ static irqreturn_t pxa_camera_irq(int irq, void *data) __raw_writel(status, pcdev->base + CISR); if (status & CISR_EOF) { - /* Reset the FIFOs */ - cifr = __raw_readl(pcdev->base + CIFR) | CIFR_RESET_F; - __raw_writel(cifr, pcdev->base + CIFR); - - pcdev->active = list_first_entry(&pcdev->capture, - struct pxa_buffer, vb.queue); - vb = &pcdev->active->vb; - buf = container_of(vb, struct pxa_buffer, vb); - pxa_videobuf_set_actdma(pcdev, buf); - - pxa_dma_start_channels(pcdev); - cicr0 = __raw_readl(pcdev->base + CICR0) | CICR0_EOFM; __raw_writel(cicr0, pcdev->base + CICR0); + tasklet_schedule(&pcdev->task_eof); } return IRQ_HANDLED; @@ -993,10 +939,7 @@ static void pxa_camera_clock_stop(struct soc_camera_host *ici) __raw_writel(0x3ff, pcdev->base + CICR0); /* Stop DMA engine */ - DCSR(pcdev->dma_chans[0]) = 0; - DCSR(pcdev->dma_chans[1]) = 0; - DCSR(pcdev->dma_chans[2]) = 0; - + pxa_dma_stop_channels(pcdev); pxa_camera_deactivate(pcdev); } @@ -1623,10 +1566,6 @@ static int pxa_camera_resume(struct device *dev) struct pxa_camera_dev *pcdev = ici->priv; int i = 0, ret = 0; - DRCMR(68) = pcdev->dma_chans[0] | DRCMR_MAPVLD; - DRCMR(69) = pcdev->dma_chans[1] | DRCMR_MAPVLD; - DRCMR(70) = pcdev->dma_chans[2] | DRCMR_MAPVLD; - __raw_writel(pcdev->save_cicr[i++] & ~CICR0_ENB, pcdev->base + CICR0); __raw_writel(pcdev->save_cicr[i++], pcdev->base + CICR1); __raw_writel(pcdev->save_cicr[i++], pcdev->base + CICR2); @@ -1732,8 +1671,15 @@ static int pxa_camera_probe(struct platform_device *pdev) struct pxa_camera_dev *pcdev; struct resource *res; void __iomem *base; + struct dma_slave_config config = { + .src_addr_width = 0, + .src_maxburst = 8, + .direction = DMA_DEV_TO_MEM, + }; + dma_cap_mask_t mask; + struct pxad_param params; int irq; - int err = 0; + int err = 0, i; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); irq = platform_get_irq(pdev, 0); @@ -1801,36 +1747,47 @@ static int pxa_camera_probe(struct platform_device *pdev) pcdev->base = base; /* request dma */ - err = pxa_request_dma("CI_Y", DMA_PRIO_HIGH, - pxa_camera_dma_irq_y, pcdev); - if (err < 0) { + dma_cap_zero(mask); + dma_cap_set(DMA_SLAVE, mask); + dma_cap_set(DMA_PRIVATE, mask); + + params.prio = 0; + params.drcmr = 68; + pcdev->dma_chans[0] = + dma_request_slave_channel_compat(mask, pxad_filter_fn, + ¶ms, &pdev->dev, "CI_Y"); + if (!pcdev->dma_chans[0]) { dev_err(&pdev->dev, "Can't request DMA for Y\n"); - return err; + return -ENODEV; } - pcdev->dma_chans[0] = err; - dev_dbg(&pdev->dev, "got DMA channel %d\n", pcdev->dma_chans[0]); - err = pxa_request_dma("CI_U", DMA_PRIO_HIGH, - pxa_camera_dma_irq_u, pcdev); - if (err < 0) { - dev_err(&pdev->dev, "Can't request DMA for U\n"); + params.drcmr = 69; + pcdev->dma_chans[1] = + dma_request_slave_channel_compat(mask, pxad_filter_fn, + ¶ms, &pdev->dev, "CI_U"); + if (!pcdev->dma_chans[1]) { + dev_err(&pdev->dev, "Can't request DMA for Y\n"); goto exit_free_dma_y; } - pcdev->dma_chans[1] = err; - dev_dbg(&pdev->dev, "got DMA channel (U) %d\n", pcdev->dma_chans[1]); - err = pxa_request_dma("CI_V", DMA_PRIO_HIGH, - pxa_camera_dma_irq_v, pcdev); - if (err < 0) { + params.drcmr = 70; + pcdev->dma_chans[2] = + dma_request_slave_channel_compat(mask, pxad_filter_fn, + ¶ms, &pdev->dev, "CI_V"); + if (!pcdev->dma_chans[2]) { dev_err(&pdev->dev, "Can't request DMA for V\n"); goto exit_free_dma_u; } - pcdev->dma_chans[2] = err; - dev_dbg(&pdev->dev, "got DMA channel (V) %d\n", pcdev->dma_chans[2]); - DRCMR(68) = pcdev->dma_chans[0] | DRCMR_MAPVLD; - DRCMR(69) = pcdev->dma_chans[1] | DRCMR_MAPVLD; - DRCMR(70) = pcdev->dma_chans[2] | DRCMR_MAPVLD; + for (i = 0; i < 3; i++) { + config.src_addr = pcdev->res->start + CIBR0 + i * 8; + err = dmaengine_slave_config(pcdev->dma_chans[i], &config); + if (err < 0) { + dev_err(&pdev->dev, "dma slave config failed: %d\n", + err); + goto exit_free_dma; + } + } /* request irq */ err = devm_request_irq(&pdev->dev, pcdev->irq, pxa_camera_irq, 0, @@ -1845,6 +1802,7 @@ static int pxa_camera_probe(struct platform_device *pdev) pcdev->soc_host.priv = pcdev; pcdev->soc_host.v4l2_dev.dev = &pdev->dev; pcdev->soc_host.nr = pdev->id; + tasklet_init(&pcdev->task_eof, pxa_camera_eof, (unsigned long)pcdev); err = soc_camera_host_register(&pcdev->soc_host); if (err) @@ -1853,11 +1811,11 @@ static int pxa_camera_probe(struct platform_device *pdev) return 0; exit_free_dma: - pxa_free_dma(pcdev->dma_chans[2]); + dma_release_channel(pcdev->dma_chans[2]); exit_free_dma_u: - pxa_free_dma(pcdev->dma_chans[1]); + dma_release_channel(pcdev->dma_chans[1]); exit_free_dma_y: - pxa_free_dma(pcdev->dma_chans[0]); + dma_release_channel(pcdev->dma_chans[0]); return err; } @@ -1867,9 +1825,9 @@ static int pxa_camera_remove(struct platform_device *pdev) struct pxa_camera_dev *pcdev = container_of(soc_host, struct pxa_camera_dev, soc_host); - pxa_free_dma(pcdev->dma_chans[0]); - pxa_free_dma(pcdev->dma_chans[1]); - pxa_free_dma(pcdev->dma_chans[2]); + dma_release_channel(pcdev->dma_chans[0]); + dma_release_channel(pcdev->dma_chans[1]); + dma_release_channel(pcdev->dma_chans[2]); soc_camera_host_unregister(soc_host); diff --git a/drivers/media/platform/soc_camera/rcar_vin.c b/drivers/media/platform/soc_camera/rcar_vin.c index dc75a80794fb..3b8edf458964 100644 --- a/drivers/media/platform/soc_camera/rcar_vin.c +++ b/drivers/media/platform/soc_camera/rcar_vin.c @@ -124,7 +124,7 @@ #define VNDMR_EXRGB (1 << 8) #define VNDMR_BPSM (1 << 4) #define VNDMR_DTMD_YCSEP (1 << 1) -#define VNDMR_DTMD_ARGB1555 (1 << 0) +#define VNDMR_DTMD_ARGB (1 << 0) /* Video n Data Mode Register 2 bits */ #define VNDMR2_VPS (1 << 30) @@ -643,21 +643,26 @@ static int rcar_vin_setup(struct rcar_vin_priv *priv) output_is_yuv = true; break; case V4L2_PIX_FMT_RGB555X: - dmr = VNDMR_DTMD_ARGB1555; + dmr = VNDMR_DTMD_ARGB; break; case V4L2_PIX_FMT_RGB565: dmr = 0; break; case V4L2_PIX_FMT_RGB32: - if (priv->chip == RCAR_GEN2 || priv->chip == RCAR_H1 || - priv->chip == RCAR_E1) { - dmr = VNDMR_EXRGB; - break; - } + if (priv->chip != RCAR_GEN2 && priv->chip != RCAR_H1 && + priv->chip != RCAR_E1) + goto e_format; + + dmr = VNDMR_EXRGB; + break; + case V4L2_PIX_FMT_ARGB32: + if (priv->chip != RCAR_GEN3) + goto e_format; + + dmr = VNDMR_EXRGB | VNDMR_DTMD_ARGB; + break; default: - dev_warn(icd->parent, "Invalid fourcc format (0x%x)\n", - icd->current_fmt->host_fmt->fourcc); - return -EINVAL; + goto e_format; } /* Always update on field change */ @@ -679,6 +684,11 @@ static int rcar_vin_setup(struct rcar_vin_priv *priv) iowrite32(vnmc | VNMC_ME, priv->base + VNMC_REG); return 0; + +e_format: + dev_warn(icd->parent, "Invalid fourcc format (0x%x)\n", + icd->current_fmt->host_fmt->fourcc); + return -EINVAL; } static void rcar_vin_capture(struct rcar_vin_priv *priv) @@ -1304,6 +1314,14 @@ static const struct soc_mbus_pixelfmt rcar_vin_formats[] = { .order = SOC_MBUS_ORDER_LE, .layout = SOC_MBUS_LAYOUT_PACKED, }, + { + .fourcc = V4L2_PIX_FMT_ARGB32, + .name = "ARGB8888", + .bits_per_sample = 32, + .packing = SOC_MBUS_PACKING_NONE, + .order = SOC_MBUS_ORDER_LE, + .layout = SOC_MBUS_LAYOUT_PACKED, + }, }; static int rcar_vin_get_formats(struct soc_camera_device *icd, unsigned int idx, @@ -1611,6 +1629,7 @@ static int rcar_vin_set_fmt(struct soc_camera_device *icd, case V4L2_PIX_FMT_RGB32: can_scale = priv->chip != RCAR_E1; break; + case V4L2_PIX_FMT_ARGB32: case V4L2_PIX_FMT_UYVY: case V4L2_PIX_FMT_YUYV: case V4L2_PIX_FMT_RGB565: diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index 35fa1071c5b2..82001e6b5553 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -804,6 +804,9 @@ static int cal_get_external_info(struct cal_ctx *ctx) { struct v4l2_ctrl *ctrl; + if (!ctx->sensor) + return -ENODEV; + ctrl = v4l2_ctrl_find(ctx->sensor->ctrl_handler, V4L2_CID_PIXEL_RATE); if (!ctrl) { ctx_err(ctx, "no pixel rate control in subdev: %s\n", @@ -950,9 +953,6 @@ static int __subdev_get_format(struct cal_ctx *ctx, struct v4l2_mbus_framefmt *mbus_fmt = &sd_fmt.format; int ret; - if (!ctx->sensor) - return -EINVAL; - sd_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; sd_fmt.pad = 0; @@ -975,9 +975,6 @@ static int __subdev_set_format(struct cal_ctx *ctx, struct v4l2_mbus_framefmt *mbus_fmt = &sd_fmt.format; int ret; - if (!ctx->sensor) - return -EINVAL; - sd_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; sd_fmt.pad = 0; *mbus_fmt = *fmt; @@ -1152,7 +1149,7 @@ static int cal_enum_framesizes(struct file *file, void *fh, ret = v4l2_subdev_call(ctx->sensor, pad, enum_frame_size, NULL, &fse); if (ret) - return -EINVAL; + return ret; ctx_dbg(1, ctx, "%s: index: %d code: %x W:[%d,%d] H:[%d,%d]\n", __func__, fse.index, fse.code, fse.min_width, fse.max_width, @@ -1201,42 +1198,25 @@ static int cal_enum_frameintervals(struct file *file, void *priv, { struct cal_ctx *ctx = video_drvdata(file); const struct cal_fmt *fmt; - struct v4l2_subdev_frame_size_enum fse; + struct v4l2_subdev_frame_interval_enum fie = { + .index = fival->index, + .width = fival->width, + .height = fival->height, + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + }; int ret; - if (fival->index) - return -EINVAL; - fmt = find_format_by_pix(ctx, fival->pixel_format); if (!fmt) return -EINVAL; - /* check for valid width/height */ - ret = 0; - fse.pad = 0; - fse.code = fmt->code; - fse.which = V4L2_SUBDEV_FORMAT_ACTIVE; - for (fse.index = 0; ; fse.index++) { - ret = v4l2_subdev_call(ctx->sensor, pad, enum_frame_size, - NULL, &fse); - if (ret) - return -EINVAL; - - if ((fival->width == fse.max_width) && - (fival->height == fse.max_height)) - break; - else if ((fival->width >= fse.min_width) && - (fival->width <= fse.max_width) && - (fival->height >= fse.min_height) && - (fival->height <= fse.max_height)) - break; - - return -EINVAL; - } - + fie.code = fmt->code; + ret = v4l2_subdev_call(ctx->sensor, pad, enum_frame_interval, + NULL, &fie); + if (ret) + return ret; fival->type = V4L2_FRMIVAL_TYPE_DISCRETE; - fival->discrete.numerator = 1; - fival->discrete.denominator = 30; + fival->discrete = fie.interval; return 0; } @@ -1347,13 +1327,11 @@ static int cal_start_streaming(struct vb2_queue *vq, unsigned int count) cal_wr_dma_addr(ctx, addr); csi2_ppi_enable(ctx); - if (ctx->sensor) { - if (v4l2_subdev_call(ctx->sensor, video, s_stream, 1)) { - ctx_err(ctx, "stream on failed in subdev\n"); - cal_runtime_put(ctx->dev); - ret = -EINVAL; - goto err; - } + ret = v4l2_subdev_call(ctx->sensor, video, s_stream, 1); + if (ret) { + ctx_err(ctx, "stream on failed in subdev\n"); + cal_runtime_put(ctx->dev); + goto err; } if (debug >= 4) @@ -1376,10 +1354,8 @@ static void cal_stop_streaming(struct vb2_queue *vq) struct cal_buffer *buf, *tmp; unsigned long flags; - if (ctx->sensor) { - if (v4l2_subdev_call(ctx->sensor, video, s_stream, 0)) - ctx_err(ctx, "stream off failed in subdev\n"); - } + if (v4l2_subdev_call(ctx->sensor, video, s_stream, 0)) + ctx_err(ctx, "stream off failed in subdev\n"); csi2_ppi_disable(ctx); disable_irqs(ctx); diff --git a/drivers/media/platform/vivid/vivid-tpg.c b/drivers/media/platform/vivid/vivid-tpg.c index 14256141f905..da862bb2e5f8 100644 --- a/drivers/media/platform/vivid/vivid-tpg.c +++ b/drivers/media/platform/vivid/vivid-tpg.c @@ -251,6 +251,10 @@ bool tpg_s_fourcc(struct tpg_data *tpg, u32 fourcc) tpg->planes = 3; tpg->is_yuv = true; break; + case V4L2_PIX_FMT_YUV422M: + case V4L2_PIX_FMT_YVU422M: + tpg->buffers = 3; + /* fall through */ case V4L2_PIX_FMT_YUV422P: tpg->vdownsampling[1] = 1; tpg->vdownsampling[2] = 1; @@ -283,6 +287,16 @@ bool tpg_s_fourcc(struct tpg_data *tpg, u32 fourcc) tpg->planes = 2; tpg->is_yuv = true; break; + case V4L2_PIX_FMT_YUV444M: + case V4L2_PIX_FMT_YVU444M: + tpg->buffers = 3; + tpg->planes = 3; + tpg->vdownsampling[1] = 1; + tpg->vdownsampling[2] = 1; + tpg->hdownsampling[1] = 1; + tpg->hdownsampling[2] = 1; + tpg->is_yuv = true; + break; case V4L2_PIX_FMT_NV24: case V4L2_PIX_FMT_NV42: tpg->vdownsampling[1] = 1; @@ -368,6 +382,10 @@ bool tpg_s_fourcc(struct tpg_data *tpg, u32 fourcc) tpg->twopixelsize[0] = 4; tpg->twopixelsize[1] = 4; break; + case V4L2_PIX_FMT_YUV444M: + case V4L2_PIX_FMT_YVU444M: + case V4L2_PIX_FMT_YUV422M: + case V4L2_PIX_FMT_YVU422M: case V4L2_PIX_FMT_YUV422P: case V4L2_PIX_FMT_YUV420: case V4L2_PIX_FMT_YVU420: @@ -933,6 +951,7 @@ static void gen_twopix(struct tpg_data *tpg, buf[0][offset] = r_y; buf[0][offset+1] = r_y == 0xff ? r_y : 0; break; + case V4L2_PIX_FMT_YUV422M: case V4L2_PIX_FMT_YUV422P: case V4L2_PIX_FMT_YUV420: case V4L2_PIX_FMT_YUV420M: @@ -947,6 +966,7 @@ static void gen_twopix(struct tpg_data *tpg, buf[1][0] = g_u; buf[2][0] = b_v; break; + case V4L2_PIX_FMT_YVU422M: case V4L2_PIX_FMT_YVU420: case V4L2_PIX_FMT_YVU420M: buf[0][offset] = r_y; @@ -988,6 +1008,18 @@ static void gen_twopix(struct tpg_data *tpg, buf[1][1] = g_u; break; + case V4L2_PIX_FMT_YUV444M: + buf[0][offset] = r_y; + buf[1][offset] = g_u; + buf[2][offset] = b_v; + break; + + case V4L2_PIX_FMT_YVU444M: + buf[0][offset] = r_y; + buf[1][offset] = b_v; + buf[2][offset] = g_u; + break; + case V4L2_PIX_FMT_NV24: buf[0][offset] = r_y; buf[1][2 * offset] = g_u; diff --git a/drivers/media/platform/vivid/vivid-vid-common.c b/drivers/media/platform/vivid/vivid-vid-common.c index 1678b730dba2..b0d4e3a0acf0 100644 --- a/drivers/media/platform/vivid/vivid-vid-common.c +++ b/drivers/media/platform/vivid/vivid-vid-common.c @@ -445,6 +445,9 @@ struct vivid_fmt vivid_formats[] = { .planes = 1, .buffers = 1, }, + + /* Multiplanar formats */ + { .fourcc = V4L2_PIX_FMT_NV16M, .vdownsampling = { 1, 1 }, @@ -495,10 +498,42 @@ struct vivid_fmt vivid_formats[] = { .planes = 2, .buffers = 2, }, + { + .fourcc = V4L2_PIX_FMT_YUV422M, + .vdownsampling = { 1, 1, 1 }, + .bit_depth = { 8, 4, 4 }, + .is_yuv = true, + .planes = 3, + .buffers = 3, + }, + { + .fourcc = V4L2_PIX_FMT_YVU422M, + .vdownsampling = { 1, 1, 1 }, + .bit_depth = { 8, 4, 4 }, + .is_yuv = true, + .planes = 3, + .buffers = 3, + }, + { + .fourcc = V4L2_PIX_FMT_YUV444M, + .vdownsampling = { 1, 1, 1 }, + .bit_depth = { 8, 8, 8 }, + .is_yuv = true, + .planes = 3, + .buffers = 3, + }, + { + .fourcc = V4L2_PIX_FMT_YVU444M, + .vdownsampling = { 1, 1, 1 }, + .bit_depth = { 8, 8, 8 }, + .is_yuv = true, + .planes = 3, + .buffers = 3, + }, }; -/* There are 6 multiplanar formats in the list */ -#define VIVID_MPLANAR_FORMATS 6 +/* There are this many multiplanar formats in the list */ +#define VIVID_MPLANAR_FORMATS 10 const struct vivid_fmt *vivid_get_format(struct vivid_dev *dev, u32 pixelformat) { diff --git a/drivers/media/platform/vsp1/vsp1_dl.c b/drivers/media/platform/vsp1/vsp1_dl.c index 7dc27ac6bd02..1a9a58588f84 100644 --- a/drivers/media/platform/vsp1/vsp1_dl.c +++ b/drivers/media/platform/vsp1/vsp1_dl.c @@ -278,7 +278,7 @@ struct vsp1_dl *vsp1_dl_create(struct vsp1_device *vsp1) dl->vsp1 = vsp1; dl->size = VSP1_DL_BODY_SIZE * ARRAY_SIZE(dl->lists.all); - dl->mem = dma_alloc_writecombine(vsp1->dev, dl->size, &dl->dma, + dl->mem = dma_alloc_wc(vsp1->dev, dl->size, &dl->dma, GFP_KERNEL); if (!dl->mem) { kfree(dl); @@ -300,6 +300,6 @@ struct vsp1_dl *vsp1_dl_create(struct vsp1_device *vsp1) void vsp1_dl_destroy(struct vsp1_dl *dl) { - dma_free_writecombine(dl->vsp1->dev, dl->size, dl->mem, dl->dma); + dma_free_wc(dl->vsp1->dev, dl->size, dl->mem, dl->dma); kfree(dl); } diff --git a/drivers/media/platform/vsp1/vsp1_video.c b/drivers/media/platform/vsp1/vsp1_video.c index 61ee0f92c1e5..72cc7d3729f8 100644 --- a/drivers/media/platform/vsp1/vsp1_video.c +++ b/drivers/media/platform/vsp1/vsp1_video.c @@ -289,7 +289,7 @@ static int vsp1_video_pipeline_validate(struct vsp1_pipeline *pipe, struct vsp1_rwpf *rwpf; struct vsp1_entity *e; - if (is_media_entity_v4l2_io(entity)) + if (!is_media_entity_v4l2_subdev(entity)) continue; subdev = media_entity_to_v4l2_subdev(entity); diff --git a/drivers/media/rc/ati_remote.c b/drivers/media/rc/ati_remote.c index a35631891cc0..3f61d77d4147 100644 --- a/drivers/media/rc/ati_remote.c +++ b/drivers/media/rc/ati_remote.c @@ -443,6 +443,21 @@ static int ati_remote_sendpacket(struct ati_remote *ati_remote, u16 cmd, return retval; } +struct accel_times { + const char value; + unsigned int msecs; +}; + +static const struct accel_times accel[] = { + { 1, 125 }, + { 2, 250 }, + { 4, 500 }, + { 6, 1000 }, + { 9, 1500 }, + { 13, 2000 }, + { 20, 0 }, +}; + /* * ati_remote_compute_accel * @@ -454,30 +469,22 @@ static int ati_remote_sendpacket(struct ati_remote *ati_remote, u16 cmd, */ static int ati_remote_compute_accel(struct ati_remote *ati_remote) { - static const char accel[] = { 1, 2, 4, 6, 9, 13, 20 }; - unsigned long now = jiffies; - int acc; + unsigned long now = jiffies, reset_time; + int i; + + reset_time = msecs_to_jiffies(250); - if (time_after(now, ati_remote->old_jiffies + msecs_to_jiffies(250))) { - acc = 1; + if (time_after(now, ati_remote->old_jiffies + reset_time)) { ati_remote->acc_jiffies = now; + return 1; } - else if (time_before(now, ati_remote->acc_jiffies + msecs_to_jiffies(125))) - acc = accel[0]; - else if (time_before(now, ati_remote->acc_jiffies + msecs_to_jiffies(250))) - acc = accel[1]; - else if (time_before(now, ati_remote->acc_jiffies + msecs_to_jiffies(500))) - acc = accel[2]; - else if (time_before(now, ati_remote->acc_jiffies + msecs_to_jiffies(1000))) - acc = accel[3]; - else if (time_before(now, ati_remote->acc_jiffies + msecs_to_jiffies(1500))) - acc = accel[4]; - else if (time_before(now, ati_remote->acc_jiffies + msecs_to_jiffies(2000))) - acc = accel[5]; - else - acc = accel[6]; + for (i = 0; i < ARRAY_SIZE(accel) - 1; i++) { + unsigned long timeout = msecs_to_jiffies(accel[i].msecs); - return acc; + if (time_before(now, ati_remote->acc_jiffies + timeout)) + return accel[i].value; + } + return accel[i].value; } /* diff --git a/drivers/media/rc/igorplugusb.c b/drivers/media/rc/igorplugusb.c index b36e51576f8e..e0c531fa01da 100644 --- a/drivers/media/rc/igorplugusb.c +++ b/drivers/media/rc/igorplugusb.c @@ -152,7 +152,7 @@ static int igorplugusb_probe(struct usb_interface *intf, struct usb_endpoint_descriptor *ep; struct igorplugusb *ir; struct rc_dev *rc; - int ret; + int ret = -ENOMEM; udev = interface_to_usbdev(intf); idesc = intf->cur_altsetting; @@ -182,7 +182,7 @@ static int igorplugusb_probe(struct usb_interface *intf, ir->urb = usb_alloc_urb(0, GFP_KERNEL); if (!ir->urb) - return -ENOMEM; + goto fail; usb_fill_control_urb(ir->urb, udev, usb_rcvctrlpipe(udev, 0), (uint8_t *)&ir->request, @@ -191,6 +191,9 @@ static int igorplugusb_probe(struct usb_interface *intf, usb_make_path(udev, ir->phys, sizeof(ir->phys)); rc = rc_allocate_device(); + if (!rc) + goto fail; + rc->input_name = DRIVER_DESC; rc->input_phys = ir->phys; usb_to_input_id(udev, &rc->input_id); @@ -214,9 +217,7 @@ static int igorplugusb_probe(struct usb_interface *intf, ret = rc_register_device(rc); if (ret) { dev_err(&intf->dev, "failed to register rc device: %d", ret); - rc_free_device(rc); - usb_free_urb(ir->urb); - return ret; + goto fail; } usb_set_intfdata(intf, ir); @@ -224,6 +225,12 @@ static int igorplugusb_probe(struct usb_interface *intf, igorplugusb_cmd(ir, SET_INFRABUFFER_EMPTY); return 0; +fail: + rc_free_device(ir->rc); + usb_free_urb(ir->urb); + del_timer(&ir->timer); + + return ret; } static void igorplugusb_disconnect(struct usb_interface *intf) diff --git a/drivers/media/rc/keymaps/rc-avermedia-rm-ks.c b/drivers/media/rc/keymaps/rc-avermedia-rm-ks.c index 8344bcc595be..2583400ca1b4 100644 --- a/drivers/media/rc/keymaps/rc-avermedia-rm-ks.c +++ b/drivers/media/rc/keymaps/rc-avermedia-rm-ks.c @@ -23,35 +23,35 @@ /* Initial keytable is from Jose Alberto Reguero <jareguero@telefonica.net> and Felipe Morales Moreno <felipe.morales.moreno@gmail.com> */ -/* FIXME: mappings are not 100% correct? */ +/* Keytable fixed by Philippe Valembois <lephilousophe@users.sourceforge.net> */ static struct rc_map_table avermedia_rm_ks[] = { - { 0x0501, KEY_POWER2 }, - { 0x0502, KEY_CHANNELUP }, - { 0x0503, KEY_CHANNELDOWN }, - { 0x0504, KEY_VOLUMEUP }, - { 0x0505, KEY_VOLUMEDOWN }, - { 0x0506, KEY_MUTE }, - { 0x0507, KEY_RIGHT }, - { 0x0508, KEY_RED }, - { 0x0509, KEY_1 }, - { 0x050a, KEY_2 }, - { 0x050b, KEY_3 }, - { 0x050c, KEY_4 }, - { 0x050d, KEY_5 }, - { 0x050e, KEY_6 }, - { 0x050f, KEY_7 }, - { 0x0510, KEY_8 }, - { 0x0511, KEY_9 }, - { 0x0512, KEY_0 }, - { 0x0513, KEY_AUDIO }, - { 0x0515, KEY_EPG }, - { 0x0516, KEY_PLAY }, - { 0x0517, KEY_RECORD }, - { 0x0518, KEY_STOP }, - { 0x051c, KEY_BACK }, - { 0x051d, KEY_FORWARD }, - { 0x054d, KEY_LEFT }, - { 0x0556, KEY_ZOOM }, + { 0x0501, KEY_POWER2 }, /* Power (RED POWER BUTTON) */ + { 0x0502, KEY_CHANNELUP }, /* Channel+ */ + { 0x0503, KEY_CHANNELDOWN }, /* Channel- */ + { 0x0504, KEY_VOLUMEUP }, /* Volume+ */ + { 0x0505, KEY_VOLUMEDOWN }, /* Volume- */ + { 0x0506, KEY_MUTE }, /* Mute */ + { 0x0507, KEY_AGAIN }, /* Recall */ + { 0x0508, KEY_VIDEO }, /* Source */ + { 0x0509, KEY_1 }, /* 1 */ + { 0x050a, KEY_2 }, /* 2 */ + { 0x050b, KEY_3 }, /* 3 */ + { 0x050c, KEY_4 }, /* 4 */ + { 0x050d, KEY_5 }, /* 5 */ + { 0x050e, KEY_6 }, /* 6 */ + { 0x050f, KEY_7 }, /* 7 */ + { 0x0510, KEY_8 }, /* 8 */ + { 0x0511, KEY_9 }, /* 9 */ + { 0x0512, KEY_0 }, /* 0 */ + { 0x0513, KEY_AUDIO }, /* Audio */ + { 0x0515, KEY_EPG }, /* EPG */ + { 0x0516, KEY_PLAYPAUSE }, /* Play/Pause */ + { 0x0517, KEY_RECORD }, /* Record */ + { 0x0518, KEY_STOP }, /* Stop */ + { 0x051c, KEY_BACK }, /* << */ + { 0x051d, KEY_FORWARD }, /* >> */ + { 0x054d, KEY_INFO }, /* Display information */ + { 0x0556, KEY_ZOOM }, /* Fullscreen */ }; static struct rc_map_list avermedia_rm_ks_map = { diff --git a/drivers/media/rc/lirc_dev.c b/drivers/media/rc/lirc_dev.c index 4de0e85af805..92ae1903c010 100644 --- a/drivers/media/rc/lirc_dev.c +++ b/drivers/media/rc/lirc_dev.c @@ -506,6 +506,7 @@ int lirc_dev_fop_close(struct inode *inode, struct file *file) { struct irctl *ir = irctls[iminor(inode)]; struct cdev *cdev; + int ret; if (!ir) { printk(KERN_ERR "%s: called with invalid irctl\n", __func__); @@ -516,7 +517,8 @@ int lirc_dev_fop_close(struct inode *inode, struct file *file) dev_dbg(ir->d.dev, LOGHEAD "close called\n", ir->d.name, ir->d.minor); - WARN_ON(mutex_lock_killable(&lirc_dev_lock)); + ret = mutex_lock_killable(&lirc_dev_lock); + WARN_ON(ret); rc_close(ir->d.rdev); @@ -532,7 +534,8 @@ int lirc_dev_fop_close(struct inode *inode, struct file *file) kfree(ir); } - mutex_unlock(&lirc_dev_lock); + if (!ret) + mutex_unlock(&lirc_dev_lock); return 0; } diff --git a/drivers/media/rc/mceusb.c b/drivers/media/rc/mceusb.c index 2cdb740cde48..35155ae500c7 100644 --- a/drivers/media/rc/mceusb.c +++ b/drivers/media/rc/mceusb.c @@ -587,9 +587,8 @@ static void mceusb_dev_printdata(struct mceusb_dev *ir, char *buf, if (len == 2) dev_dbg(dev, "Get hw/sw rev?"); else - dev_dbg(dev, "hw/sw rev 0x%02x 0x%02x 0x%02x 0x%02x", - data1, data2, - buf[start + 4], buf[start + 5]); + dev_dbg(dev, "hw/sw rev %*ph", + 4, &buf[start + 2]); break; case MCE_CMD_RESUME: dev_dbg(dev, "Device resume requested"); diff --git a/drivers/media/rc/nuvoton-cir.c b/drivers/media/rc/nuvoton-cir.c index c96da3aaf00b..99b303b702ac 100644 --- a/drivers/media/rc/nuvoton-cir.c +++ b/drivers/media/rc/nuvoton-cir.c @@ -39,6 +39,8 @@ #include "nuvoton-cir.h" +static void nvt_clear_cir_wake_fifo(struct nvt_dev *nvt); + static const struct nvt_chip nvt_chips[] = { { "w83667hg", NVT_W83667HG }, { "NCT6775F", NVT_6775F }, @@ -177,6 +179,104 @@ static void nvt_set_ioaddr(struct nvt_dev *nvt, unsigned long *ioaddr) } } +static ssize_t wakeup_data_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct rc_dev *rc_dev = to_rc_dev(dev); + struct nvt_dev *nvt = rc_dev->priv; + int fifo_len, duration; + unsigned long flags; + ssize_t buf_len = 0; + int i; + + spin_lock_irqsave(&nvt->nvt_lock, flags); + + fifo_len = nvt_cir_wake_reg_read(nvt, CIR_WAKE_FIFO_COUNT); + fifo_len = min(fifo_len, WAKEUP_MAX_SIZE); + + /* go to first element to be read */ + while (nvt_cir_wake_reg_read(nvt, CIR_WAKE_RD_FIFO_ONLY_IDX)) + nvt_cir_wake_reg_read(nvt, CIR_WAKE_RD_FIFO_ONLY); + + for (i = 0; i < fifo_len; i++) { + duration = nvt_cir_wake_reg_read(nvt, CIR_WAKE_RD_FIFO_ONLY); + duration = (duration & BUF_LEN_MASK) * SAMPLE_PERIOD; + buf_len += snprintf(buf + buf_len, PAGE_SIZE - buf_len, + "%d ", duration); + } + buf_len += snprintf(buf + buf_len, PAGE_SIZE - buf_len, "\n"); + + spin_unlock_irqrestore(&nvt->nvt_lock, flags); + + return buf_len; +} + +static ssize_t wakeup_data_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + struct rc_dev *rc_dev = to_rc_dev(dev); + struct nvt_dev *nvt = rc_dev->priv; + unsigned long flags; + u8 tolerance, config, wake_buf[WAKEUP_MAX_SIZE]; + char **argv; + int i, count; + unsigned int val; + ssize_t ret; + + argv = argv_split(GFP_KERNEL, buf, &count); + if (!argv) + return -ENOMEM; + if (!count || count > WAKEUP_MAX_SIZE) { + ret = -EINVAL; + goto out; + } + + for (i = 0; i < count; i++) { + ret = kstrtouint(argv[i], 10, &val); + if (ret) + goto out; + val = DIV_ROUND_CLOSEST(val, SAMPLE_PERIOD); + if (!val || val > 0x7f) { + ret = -EINVAL; + goto out; + } + wake_buf[i] = val; + /* sequence must start with a pulse */ + if (i % 2 == 0) + wake_buf[i] |= BUF_PULSE_BIT; + } + + /* hardcode the tolerance to 10% */ + tolerance = DIV_ROUND_UP(count, 10); + + spin_lock_irqsave(&nvt->nvt_lock, flags); + + nvt_clear_cir_wake_fifo(nvt); + nvt_cir_wake_reg_write(nvt, count, CIR_WAKE_FIFO_CMP_DEEP); + nvt_cir_wake_reg_write(nvt, tolerance, CIR_WAKE_FIFO_CMP_TOL); + + config = nvt_cir_wake_reg_read(nvt, CIR_WAKE_IRCON); + + /* enable writes to wake fifo */ + nvt_cir_wake_reg_write(nvt, config | CIR_WAKE_IRCON_MODE1, + CIR_WAKE_IRCON); + + for (i = 0; i < count; i++) + nvt_cir_wake_reg_write(nvt, wake_buf[i], CIR_WAKE_WR_FIFO_DATA); + + nvt_cir_wake_reg_write(nvt, config, CIR_WAKE_IRCON); + + spin_unlock_irqrestore(&nvt->nvt_lock, flags); + + ret = len; +out: + argv_free(argv); + return ret; +} +static DEVICE_ATTR_RW(wakeup_data); + /* dump current cir register contents */ static void cir_dump_regs(struct nvt_dev *nvt) { @@ -1133,6 +1233,10 @@ static int nvt_probe(struct pnp_dev *pdev, const struct pnp_device_id *dev_id) NVT_DRIVER_NAME "-wake", (void *)nvt)) goto exit_unregister_device; + ret = device_create_file(&rdev->dev, &dev_attr_wakeup_data); + if (ret) + goto exit_unregister_device; + device_init_wakeup(&pdev->dev, true); dev_notice(&pdev->dev, "driver has been successfully loaded\n"); @@ -1156,6 +1260,8 @@ static void nvt_remove(struct pnp_dev *pdev) { struct nvt_dev *nvt = pnp_get_drvdata(pdev); + device_remove_file(&nvt->rdev->dev, &dev_attr_wakeup_data); + nvt_disable_cir(nvt); /* enable CIR Wake (for IR power-on) */ diff --git a/drivers/media/rc/nuvoton-cir.h b/drivers/media/rc/nuvoton-cir.h index 4a5650dffa29..c9c98ebb19ee 100644 --- a/drivers/media/rc/nuvoton-cir.h +++ b/drivers/media/rc/nuvoton-cir.h @@ -417,3 +417,6 @@ struct nvt_dev { /* as VISTA MCE definition, valid carrier value */ #define MAX_CARRIER 60000 #define MIN_CARRIER 30000 + +/* max wakeup sequence length */ +#define WAKEUP_MAX_SIZE 65 diff --git a/drivers/media/rc/rc-main.c b/drivers/media/rc/rc-main.c index dcf20d9cbe09..4e9bbe735ae9 100644 --- a/drivers/media/rc/rc-main.c +++ b/drivers/media/rc/rc-main.c @@ -13,6 +13,7 @@ */ #include <media/rc-core.h> +#include <linux/atomic.h> #include <linux/spinlock.h> #include <linux/delay.h> #include <linux/input.h> @@ -723,10 +724,6 @@ int rc_open(struct rc_dev *rdev) return -EINVAL; mutex_lock(&rdev->lock); - if (!rdev->initialized) { - rval = -EINVAL; - goto unlock; - } if (!rdev->users++ && rdev->open != NULL) rval = rdev->open(rdev); @@ -734,7 +731,6 @@ int rc_open(struct rc_dev *rdev) if (rval) rdev->users--; -unlock: mutex_unlock(&rdev->lock); return rval; @@ -879,11 +875,10 @@ static ssize_t show_protocols(struct device *device, if (!dev) return -EINVAL; + if (!atomic_read(&dev->initialized)) + return -ERESTARTSYS; + mutex_lock(&dev->lock); - if (!dev->initialized) { - mutex_unlock(&dev->lock); - return -EINVAL; - } if (fattr->type == RC_FILTER_NORMAL) { enabled = dev->enabled_protocols; @@ -1064,6 +1059,9 @@ static ssize_t store_protocols(struct device *device, if (!dev) return -EINVAL; + if (!atomic_read(&dev->initialized)) + return -ERESTARTSYS; + if (fattr->type == RC_FILTER_NORMAL) { IR_dprintk(1, "Normal protocol change requested\n"); current_protocols = &dev->enabled_protocols; @@ -1084,10 +1082,6 @@ static ssize_t store_protocols(struct device *device, } mutex_lock(&dev->lock); - if (!dev->initialized) { - rc = -EINVAL; - goto out; - } old_protocols = *current_protocols; new_protocols = old_protocols; @@ -1168,11 +1162,10 @@ static ssize_t show_filter(struct device *device, if (!dev) return -EINVAL; + if (!atomic_read(&dev->initialized)) + return -ERESTARTSYS; + mutex_lock(&dev->lock); - if (!dev->initialized) { - mutex_unlock(&dev->lock); - return -EINVAL; - } if (fattr->type == RC_FILTER_NORMAL) filter = &dev->scancode_filter; @@ -1223,6 +1216,9 @@ static ssize_t store_filter(struct device *device, if (!dev) return -EINVAL; + if (!atomic_read(&dev->initialized)) + return -ERESTARTSYS; + ret = kstrtoul(buf, 0, &val); if (ret < 0) return ret; @@ -1241,10 +1237,6 @@ static ssize_t store_filter(struct device *device, return -EINVAL; mutex_lock(&dev->lock); - if (!dev->initialized) { - ret = -EINVAL; - goto unlock; - } new_filter = *filter; if (fattr->mask) @@ -1431,6 +1423,7 @@ int rc_register_device(struct rc_dev *dev) dev->minor = minor; dev_set_name(&dev->dev, "rc%u", dev->minor); dev_set_drvdata(&dev->dev, dev); + atomic_set(&dev->initialized, 0); dev->dev.groups = dev->sysfs_groups; dev->sysfs_groups[attr++] = &rc_dev_protocol_attr_grp; @@ -1455,10 +1448,6 @@ int rc_register_device(struct rc_dev *dev) dev->input_dev->phys = dev->input_phys; dev->input_dev->name = dev->input_name; - rc = input_register_device(dev->input_dev); - if (rc) - goto out_table; - /* * Default delay of 250ms is too short for some protocols, especially * since the timeout is currently set to 250ms. Increase it to 500ms, @@ -1474,6 +1463,11 @@ int rc_register_device(struct rc_dev *dev) */ dev->input_dev->rep[REP_PERIOD] = 125; + /* rc_open will be called here */ + rc = input_register_device(dev->input_dev); + if (rc) + goto out_table; + path = kobject_get_path(&dev->dev.kobj, GFP_KERNEL); dev_info(&dev->dev, "%s as %s\n", dev->input_name ?: "Unspecified device", path ?: "N/A"); @@ -1497,8 +1491,9 @@ int rc_register_device(struct rc_dev *dev) dev->enabled_protocols = rc_type; } + /* Allow the RC sysfs nodes to be accessible */ mutex_lock(&dev->lock); - dev->initialized = true; + atomic_set(&dev->initialized, 1); mutex_unlock(&dev->lock); IR_dprintk(1, "Registered rc%u (driver: %s, remote: %s, mode %s)\n", diff --git a/drivers/media/rc/sunxi-cir.c b/drivers/media/rc/sunxi-cir.c index 40f77685cc4a..eaadc081760a 100644 --- a/drivers/media/rc/sunxi-cir.c +++ b/drivers/media/rc/sunxi-cir.c @@ -326,6 +326,7 @@ static const struct of_device_id sunxi_ir_match[] = { { .compatible = "allwinner,sun5i-a13-ir", }, {}, }; +MODULE_DEVICE_TABLE(of, sunxi_ir_match); static struct platform_driver sunxi_ir_driver = { .probe = sunxi_ir_probe, diff --git a/drivers/media/tuners/xc4000.c b/drivers/media/tuners/xc4000.c index 219ebafae70f..d95c7e082ccf 100644 --- a/drivers/media/tuners/xc4000.c +++ b/drivers/media/tuners/xc4000.c @@ -1508,7 +1508,7 @@ static int xc4000_get_signal(struct dvb_frontend *fe, u16 *strength) if (value >= 0x2000) { value = 0; } else { - value = ~value << 3; + value = (~value << 3) & 0xffff; } goto ret; diff --git a/drivers/media/usb/airspy/airspy.c b/drivers/media/usb/airspy/airspy.c index 0d4ac5947f3a..87c12930416f 100644 --- a/drivers/media/usb/airspy/airspy.c +++ b/drivers/media/usb/airspy/airspy.c @@ -104,9 +104,8 @@ struct airspy_frame_buf { }; struct airspy { -#define POWER_ON (1 << 1) -#define URB_BUF (1 << 2) -#define USB_STATE_URB_BUF (1 << 3) +#define POWER_ON 1 +#define USB_STATE_URB_BUF 2 unsigned long flags; struct device *dev; @@ -359,7 +358,7 @@ static int airspy_submit_urbs(struct airspy *s) static int airspy_free_stream_bufs(struct airspy *s) { - if (s->flags & USB_STATE_URB_BUF) { + if (test_bit(USB_STATE_URB_BUF, &s->flags)) { while (s->buf_num) { s->buf_num--; dev_dbg(s->dev, "free buf=%d\n", s->buf_num); @@ -368,7 +367,7 @@ static int airspy_free_stream_bufs(struct airspy *s) s->dma_addr[s->buf_num]); } } - s->flags &= ~USB_STATE_URB_BUF; + clear_bit(USB_STATE_URB_BUF, &s->flags); return 0; } @@ -394,7 +393,7 @@ static int airspy_alloc_stream_bufs(struct airspy *s) dev_dbg(s->dev, "alloc buf=%d %p (dma %llu)\n", s->buf_num, s->buf_list[s->buf_num], (long long)s->dma_addr[s->buf_num]); - s->flags |= USB_STATE_URB_BUF; + set_bit(USB_STATE_URB_BUF, &s->flags); } return 0; diff --git a/drivers/media/usb/au0828/au0828-core.c b/drivers/media/usb/au0828/au0828-core.c index 7cafe4dd5fd1..5dc82e8c8670 100644 --- a/drivers/media/usb/au0828/au0828-core.c +++ b/drivers/media/usb/au0828/au0828-core.c @@ -20,6 +20,7 @@ */ #include "au0828.h" +#include "au8522.h" #include <linux/module.h> #include <linux/slab.h> @@ -134,10 +135,10 @@ static void au0828_unregister_media_device(struct au0828_dev *dev) { #ifdef CONFIG_MEDIA_CONTROLLER - if (dev->media_dev) { + if (dev->media_dev && + media_devnode_is_registered(&dev->media_dev->devnode)) { media_device_unregister(dev->media_dev); media_device_cleanup(dev->media_dev); - kfree(dev->media_dev); dev->media_dev = NULL; } #endif @@ -191,18 +192,337 @@ static int au0828_media_device_init(struct au0828_dev *dev, #ifdef CONFIG_MEDIA_CONTROLLER struct media_device *mdev; - if (!dev->board.name) - mdev = v4l2_mc_usb_media_device_init(udev, "unknown au0828"); - else - mdev = v4l2_mc_usb_media_device_init(udev, dev->board.name); + mdev = media_device_get_devres(&udev->dev); if (!mdev) return -ENOMEM; + /* check if media device is already initialized */ + if (!mdev->dev) + media_device_usb_init(mdev, udev, udev->product); + dev->media_dev = mdev; #endif return 0; } +#ifdef CONFIG_MEDIA_CONTROLLER +static void au0828_media_graph_notify(struct media_entity *new, + void *notify_data) +{ + struct au0828_dev *dev = (struct au0828_dev *) notify_data; + int ret; + struct media_entity *entity, *mixer = NULL, *decoder = NULL; + + if (!new) { + /* + * Called during au0828 probe time to connect + * entites that were created prior to registering + * the notify handler. Find mixer and decoder. + */ + media_device_for_each_entity(entity, dev->media_dev) { + if (entity->function == MEDIA_ENT_F_AUDIO_MIXER) + mixer = entity; + else if (entity->function == MEDIA_ENT_F_ATV_DECODER) + decoder = entity; + } + goto create_link; + } + + switch (new->function) { + case MEDIA_ENT_F_AUDIO_MIXER: + mixer = new; + if (dev->decoder) + decoder = dev->decoder; + break; + case MEDIA_ENT_F_ATV_DECODER: + /* In case, Mixer is added first, find mixer and create link */ + media_device_for_each_entity(entity, dev->media_dev) { + if (entity->function == MEDIA_ENT_F_AUDIO_MIXER) + mixer = entity; + } + decoder = new; + break; + default: + break; + } + +create_link: + if (decoder && mixer) { + ret = media_create_pad_link(decoder, + DEMOD_PAD_AUDIO_OUT, + mixer, 0, + MEDIA_LNK_FL_ENABLED); + if (ret) + dev_err(&dev->usbdev->dev, + "Mixer Pad Link Create Error: %d\n", ret); + } +} + +static int au0828_enable_source(struct media_entity *entity, + struct media_pipeline *pipe) +{ + struct media_entity *source, *find_source; + struct media_entity *sink; + struct media_link *link, *found_link = NULL; + int ret = 0; + struct media_device *mdev = entity->graph_obj.mdev; + struct au0828_dev *dev; + + if (!mdev) + return -ENODEV; + + mutex_lock(&mdev->graph_mutex); + + dev = mdev->source_priv; + + /* + * For Audio and V4L2 entity, find the link to which decoder + * is the sink. Look for an active link between decoder and + * source (tuner/s-video/Composite), if one exists, nothing + * to do. If not, look for any active links between source + * and any other entity. If one exists, source is busy. If + * source is free, setup link and start pipeline from source. + * For DVB FE entity, the source for the link is the tuner. + * Check if tuner is available and setup link and start + * pipeline. + */ + if (entity->function == MEDIA_ENT_F_DTV_DEMOD) { + sink = entity; + find_source = dev->tuner; + } else { + /* Analog isn't configured or register failed */ + if (!dev->decoder) { + ret = -ENODEV; + goto end; + } + + sink = dev->decoder; + + /* + * Default input is tuner and default input_type + * is AU0828_VMUX_TELEVISION. + * FIXME: + * There is a problem when s_input is called to + * change the default input. s_input will try to + * enable_source before attempting to change the + * input on the device, and will end up enabling + * default source which is tuner. + * + * Additional logic is necessary in au0828 + * to detect that the input has changed and + * enable the right source. + */ + + if (dev->input_type == AU0828_VMUX_TELEVISION) + find_source = dev->tuner; + else if (dev->input_type == AU0828_VMUX_SVIDEO || + dev->input_type == AU0828_VMUX_COMPOSITE) + find_source = &dev->input_ent[dev->input_type]; + else { + /* unknown input - let user select input */ + ret = 0; + goto end; + } + } + + /* Is an active link between sink and source */ + if (dev->active_link) { + /* + * If DVB is using the tuner and calling entity is + * audio/video, the following check will be false, + * since sink is different. Result is Busy. + */ + if (dev->active_link->sink->entity == sink && + dev->active_link->source->entity == find_source) { + /* + * Either ALSA or Video own tuner. sink is + * the same for both. Prevent Video stepping + * on ALSA when ALSA owns the source. + */ + if (dev->active_link_owner != entity && + dev->active_link_owner->function == + MEDIA_ENT_F_AUDIO_CAPTURE) { + pr_debug("ALSA has the tuner\n"); + ret = -EBUSY; + goto end; + } + ret = 0; + goto end; + } else { + ret = -EBUSY; + goto end; + } + } + + list_for_each_entry(link, &sink->links, list) { + /* Check sink, and source */ + if (link->sink->entity == sink && + link->source->entity == find_source) { + found_link = link; + break; + } + } + + if (!found_link) { + ret = -ENODEV; + goto end; + } + + /* activate link between source and sink and start pipeline */ + source = found_link->source->entity; + ret = __media_entity_setup_link(found_link, MEDIA_LNK_FL_ENABLED); + if (ret) { + pr_err("Activate tuner link %s->%s. Error %d\n", + source->name, sink->name, ret); + goto end; + } + + ret = __media_entity_pipeline_start(entity, pipe); + if (ret) { + pr_err("Start Pipeline: %s->%s Error %d\n", + source->name, entity->name, ret); + ret = __media_entity_setup_link(found_link, 0); + pr_err("Deactivate link Error %d\n", ret); + goto end; + } + /* + * save active link and active link owner to avoid audio + * deactivating video owned link from disable_source and + * vice versa + */ + dev->active_link = found_link; + dev->active_link_owner = entity; + dev->active_source = source; + dev->active_sink = sink; + + pr_debug("Enabled Source: %s->%s->%s Ret %d\n", + dev->active_source->name, dev->active_sink->name, + dev->active_link_owner->name, ret); +end: + mutex_unlock(&mdev->graph_mutex); + pr_debug("au0828_enable_source() end %s %d %d\n", + entity->name, entity->function, ret); + return ret; +} + +static void au0828_disable_source(struct media_entity *entity) +{ + int ret = 0; + struct media_device *mdev = entity->graph_obj.mdev; + struct au0828_dev *dev; + + if (!mdev) + return; + + mutex_lock(&mdev->graph_mutex); + dev = mdev->source_priv; + + if (!dev->active_link) { + ret = -ENODEV; + goto end; + } + + /* link is active - stop pipeline from source (tuner) */ + if (dev->active_link->sink->entity == dev->active_sink && + dev->active_link->source->entity == dev->active_source) { + /* + * prevent video from deactivating link when audio + * has active pipeline + */ + if (dev->active_link_owner != entity) + goto end; + __media_entity_pipeline_stop(entity); + ret = __media_entity_setup_link(dev->active_link, 0); + if (ret) + pr_err("Deactivate link Error %d\n", ret); + + pr_debug("Disabled Source: %s->%s->%s Ret %d\n", + dev->active_source->name, dev->active_sink->name, + dev->active_link_owner->name, ret); + + dev->active_link = NULL; + dev->active_link_owner = NULL; + dev->active_source = NULL; + dev->active_sink = NULL; + } + +end: + mutex_unlock(&mdev->graph_mutex); +} +#endif + +static int au0828_media_device_register(struct au0828_dev *dev, + struct usb_device *udev) +{ +#ifdef CONFIG_MEDIA_CONTROLLER + int ret; + struct media_entity *entity, *demod = NULL, *tuner = NULL; + + if (!dev->media_dev) + return 0; + + if (!media_devnode_is_registered(&dev->media_dev->devnode)) { + + /* register media device */ + ret = media_device_register(dev->media_dev); + if (ret) { + dev_err(&udev->dev, + "Media Device Register Error: %d\n", ret); + return ret; + } + } else { + /* + * Call au0828_media_graph_notify() to connect + * audio graph to our graph. In this case, audio + * driver registered the device and there is no + * entity_notify to be called when new entities + * are added. Invoke it now. + */ + au0828_media_graph_notify(NULL, (void *) dev); + } + + /* + * Find tuner and demod to disable the link between + * the two to avoid disable step when tuner is requested + * by video or audio. Note that this step can't be done + * until dvb graph is created during dvb register. + */ + media_device_for_each_entity(entity, dev->media_dev) { + if (entity->function == MEDIA_ENT_F_DTV_DEMOD) + demod = entity; + else if (entity->function == MEDIA_ENT_F_TUNER) + tuner = entity; + } + /* Disable link between tuner and demod */ + if (tuner && demod) { + struct media_link *link; + + list_for_each_entry(link, &demod->links, list) { + if (link->sink->entity == demod && + link->source->entity == tuner) { + media_entity_setup_link(link, 0); + } + } + } + + /* register entity_notify callback */ + dev->entity_notify.notify_data = (void *) dev; + dev->entity_notify.notify = (void *) au0828_media_graph_notify; + ret = media_device_register_entity_notify(dev->media_dev, + &dev->entity_notify); + if (ret) { + dev_err(&udev->dev, + "Media Device register entity_notify Error: %d\n", + ret); + return ret; + } + /* set enable_source */ + dev->media_dev->source_priv = (void *) dev; + dev->media_dev->enable_source = au0828_enable_source; + dev->media_dev->disable_source = au0828_disable_source; +#endif + return 0; +} static int au0828_usb_probe(struct usb_interface *interface, const struct usb_device_id *id) @@ -306,9 +626,7 @@ static int au0828_usb_probe(struct usb_interface *interface, mutex_unlock(&dev->lock); -#ifdef CONFIG_MEDIA_CONTROLLER - retval = media_device_register(dev->media_dev); -#endif + retval = au0828_media_device_register(dev, usbdev); done: if (retval < 0) diff --git a/drivers/media/usb/au0828/au0828-video.c b/drivers/media/usb/au0828/au0828-video.c index 2fc2b29d2dd9..13f6dab9ccc2 100644 --- a/drivers/media/usb/au0828/au0828-video.c +++ b/drivers/media/usb/au0828/au0828-video.c @@ -28,12 +28,14 @@ */ #include "au0828.h" +#include "au8522.h" #include <linux/module.h> #include <linux/slab.h> #include <linux/init.h> #include <linux/device.h> #include <media/v4l2-common.h> +#include <media/v4l2-mc.h> #include <media/v4l2-ioctl.h> #include <media/v4l2-event.h> #include <media/tuner.h> @@ -651,81 +653,6 @@ void au0828_usb_v4l2_media_release(struct au0828_dev *dev) #endif } -static int au0828_create_media_graph(struct au0828_dev *dev) -{ -#ifdef CONFIG_MEDIA_CONTROLLER - struct media_device *mdev = dev->media_dev; - struct media_entity *entity; - struct media_entity *tuner = NULL, *decoder = NULL; - int i, ret; - - if (!mdev) - return 0; - - media_device_for_each_entity(entity, mdev) { - switch (entity->function) { - case MEDIA_ENT_F_TUNER: - tuner = entity; - break; - case MEDIA_ENT_F_ATV_DECODER: - decoder = entity; - break; - } - } - - /* Analog setup, using tuner as a link */ - - /* Something bad happened! */ - if (!decoder) - return -EINVAL; - - if (tuner) { - ret = media_create_pad_link(tuner, TUNER_PAD_OUTPUT, - decoder, 0, - MEDIA_LNK_FL_ENABLED); - if (ret) - return ret; - } - ret = media_create_pad_link(decoder, 1, &dev->vdev.entity, 0, - MEDIA_LNK_FL_ENABLED); - if (ret) - return ret; - ret = media_create_pad_link(decoder, 2, &dev->vbi_dev.entity, 0, - MEDIA_LNK_FL_ENABLED); - if (ret) - return ret; - - for (i = 0; i < AU0828_MAX_INPUT; i++) { - struct media_entity *ent = &dev->input_ent[i]; - - switch (AUVI_INPUT(i).type) { - case AU0828_VMUX_UNDEFINED: - break; - case AU0828_VMUX_CABLE: - case AU0828_VMUX_TELEVISION: - case AU0828_VMUX_DVB: - if (!tuner) - break; - - ret = media_create_pad_link(ent, 0, tuner, - TUNER_PAD_RF_INPUT, - MEDIA_LNK_FL_ENABLED); - if (ret) - return ret; - break; - case AU0828_VMUX_COMPOSITE: - case AU0828_VMUX_SVIDEO: - /* FIXME: fix the decoder PAD */ - ret = media_create_pad_link(ent, 0, decoder, 0, 0); - if (ret) - return ret; - break; - } - } -#endif - return 0; -} - static void au0828_usb_v4l2_release(struct v4l2_device *v4l2_dev) { struct au0828_dev *dev = @@ -774,64 +701,6 @@ int au0828_v4l2_device_register(struct usb_interface *interface, return 0; } -static int au0828_enable_analog_tuner(struct au0828_dev *dev) -{ -#ifdef CONFIG_MEDIA_CONTROLLER - struct media_device *mdev = dev->media_dev; - struct media_entity *source; - struct media_link *link, *found_link = NULL; - int ret, active_links = 0; - - if (!mdev || !dev->decoder) - return 0; - - /* - * This will find the tuner that is connected into the decoder. - * Technically, this is not 100% correct, as the device may be - * using an analog input instead of the tuner. However, as we can't - * do DVB streaming while the DMA engine is being used for V4L2, - * this should be enough for the actual needs. - */ - list_for_each_entry(link, &dev->decoder->links, list) { - if (link->sink->entity == dev->decoder) { - found_link = link; - if (link->flags & MEDIA_LNK_FL_ENABLED) - active_links++; - break; - } - } - - if (active_links == 1 || !found_link) - return 0; - - source = found_link->source->entity; - list_for_each_entry(link, &source->links, list) { - struct media_entity *sink; - int flags = 0; - - sink = link->sink->entity; - - if (sink == dev->decoder) - flags = MEDIA_LNK_FL_ENABLED; - - ret = media_entity_setup_link(link, flags); - if (ret) { - pr_err( - "Couldn't change link %s->%s to %s. Error %d\n", - source->name, sink->name, - flags ? "enabled" : "disabled", - ret); - return ret; - } else - au0828_isocdbg( - "link %s->%s was %s\n", - source->name, sink->name, - flags ? "ENABLED" : "disabled"); - } -#endif - return 0; -} - static int queue_setup(struct vb2_queue *vq, unsigned int *nbuffers, unsigned int *nplanes, unsigned int sizes[], void *alloc_ctxs[]) @@ -843,9 +712,6 @@ static int queue_setup(struct vb2_queue *vq, return sizes[0] < size ? -EINVAL : 0; *nplanes = 1; sizes[0] = size; - - au0828_enable_analog_tuner(dev); - return 0; } @@ -1213,8 +1079,39 @@ static int au0828_v4l2_close(struct file *filp) goto end; if (dev->users == 1) { - /* Save some power by putting tuner to sleep */ - v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_power, 0); + /* + * Avoid putting tuner in sleep if DVB or ALSA are + * streaming. + * + * On most USB devices like au0828 the tuner can + * be safely put in sleep stare here if ALSA isn't + * streaming. Exceptions are some very old USB tuner + * models such as em28xx-based WinTV USB2 which have + * a separate audio output jack. The devices that have + * a separate audio output jack have analog tuners, + * like Philips FM1236. Those devices are always on, + * so the s_power callback are silently ignored. + * So, the current logic here does the following: + * Disable (put tuner to sleep) when + * - ALSA and DVB aren't not streaming; + * - the last V4L2 file handler is closed. + * + * FIXME: + * + * Additionally, this logic could be improved to + * disable the media source if the above conditions + * are met and if the device: + * - doesn't have a separate audio out plug (or + * - doesn't use a silicon tuner like xc2028/3028/4000/5000). + * + * Once this additional logic is in place, a callback + * is needed to enable the media source and power on + * the tuner, for radio to work. + */ + ret = v4l_enable_media_source(vdev); + if (ret == 0) + v4l2_device_call_all(&dev->v4l2_dev, 0, core, + s_power, 0); dev->std_set_in_tuner_core = 0; /* When close the device, set the usb intf0 into alt0 to free @@ -1520,9 +1417,11 @@ static void au0828_s_input(struct au0828_dev *dev, int index) default: dprintk(1, "unknown input type set [%d]\n", AUVI_INPUT(index).type); - break; + return; } + dev->ctrl_input = index; + v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_routing, AUVI_INPUT(index).vmux, 0, 0); @@ -1554,6 +1453,7 @@ static void au0828_s_input(struct au0828_dev *dev, int index) static int vidioc_s_input(struct file *file, void *priv, unsigned int index) { struct au0828_dev *dev = video_drvdata(file); + struct video_device *vfd = video_devdata(file); dprintk(1, "VIDIOC_S_INPUT in function %s, input=%d\n", __func__, index); @@ -1561,9 +1461,19 @@ static int vidioc_s_input(struct file *file, void *priv, unsigned int index) return -EINVAL; if (AUVI_INPUT(index).type == 0) return -EINVAL; - dev->ctrl_input = index; + + if (dev->ctrl_input == index) + return 0; + au0828_s_input(dev, index); - return 0; + + /* + * Input has been changed. Disable the media source + * associated with the old input and enable source + * for the newly set input + */ + v4l_disable_media_source(vfd); + return v4l_enable_media_source(vfd); } static int vidioc_enumaudio(struct file *file, void *priv, struct v4l2_audio *a) @@ -1614,10 +1524,16 @@ static int vidioc_s_audio(struct file *file, void *priv, const struct v4l2_audio static int vidioc_g_tuner(struct file *file, void *priv, struct v4l2_tuner *t) { struct au0828_dev *dev = video_drvdata(file); + struct video_device *vfd = video_devdata(file); + int ret; if (t->index != 0) return -EINVAL; + ret = v4l_enable_media_source(vfd); + if (ret) + return ret; + dprintk(1, "%s called std_set %d dev_state %d\n", __func__, dev->std_set_in_tuner_core, dev->dev_state); @@ -2071,6 +1987,7 @@ int au0828_analog_register(struct au0828_dev *dev, dev->ctrl_ainput = 0; dev->ctrl_freq = 960; dev->std = V4L2_STD_NTSC_M; + /* Default input is TV Tuner */ au0828_s_input(dev, 0); mutex_init(&dev->vb_queue_lock); @@ -2122,14 +2039,16 @@ int au0828_analog_register(struct au0828_dev *dev, ret = -ENODEV; goto err_reg_vbi_dev; } - retval = au0828_create_media_graph(dev); + +#ifdef CONFIG_MEDIA_CONTROLLER + retval = v4l2_mc_create_media_graph(dev->media_dev); if (retval) { pr_err("%s() au0282_dev_register failed to create graph\n", __func__); ret = -ENODEV; goto err_reg_vbi_dev; } - +#endif dprintk(1, "%s completed!\n", __func__); diff --git a/drivers/media/usb/au0828/au0828.h b/drivers/media/usb/au0828/au0828.h index 23f869cf11da..ff7f8510fb77 100644 --- a/drivers/media/usb/au0828/au0828.h +++ b/drivers/media/usb/au0828/au0828.h @@ -282,6 +282,12 @@ struct au0828_dev { struct media_entity *decoder; struct media_entity input_ent[AU0828_MAX_INPUT]; struct media_pad input_pad[AU0828_MAX_INPUT]; + struct media_entity_notify entity_notify; + struct media_entity *tuner; + struct media_link *active_link; + struct media_entity *active_link_owner; + struct media_entity *active_source; + struct media_entity *active_sink; #endif }; diff --git a/drivers/media/usb/cx231xx/cx231xx-cards.c b/drivers/media/usb/cx231xx/cx231xx-cards.c index 9e3a5d2038c2..c63248a18823 100644 --- a/drivers/media/usb/cx231xx/cx231xx-cards.c +++ b/drivers/media/usb/cx231xx/cx231xx-cards.c @@ -1212,10 +1212,12 @@ static int cx231xx_media_device_init(struct cx231xx *dev, #ifdef CONFIG_MEDIA_CONTROLLER struct media_device *mdev; - mdev = v4l2_mc_usb_media_device_init(udev, dev->board.name); + mdev = kzalloc(sizeof(*mdev), GFP_KERNEL); if (!mdev) return -ENOMEM; + media_device_usb_init(mdev, udev, dev->board.name); + dev->media_dev = mdev; #endif return 0; diff --git a/drivers/media/usb/dvb-usb-v2/af9035.c b/drivers/media/usb/dvb-usb-v2/af9035.c index b3c09fe54d9b..2638e3251f2a 100644 --- a/drivers/media/usb/dvb-usb-v2/af9035.c +++ b/drivers/media/usb/dvb-usb-v2/af9035.c @@ -2053,6 +2053,8 @@ static const struct usb_device_id af9035_id_table[] = { &af9035_props, "Avermedia A835B(3835)", RC_MAP_IT913X_V2) }, { DVB_USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_A835B_4835, &af9035_props, "Avermedia A835B(4835)", RC_MAP_IT913X_V2) }, + { DVB_USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_TD110, + &af9035_props, "Avermedia AverTV Volar HD 2 (TD110)", RC_MAP_AVERMEDIA_RM_KS) }, { DVB_USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_H335, &af9035_props, "Avermedia H335", RC_MAP_IT913X_V2) }, { DVB_USB_DEVICE(USB_VID_KWORLD_2, USB_PID_KWORLD_UB499_2T_T09, diff --git a/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c b/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c index 4a8769781cea..3fbb2cd19f5e 100644 --- a/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c +++ b/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c @@ -20,7 +20,7 @@ */ #include "dvb_usb_common.h" -#include <media/v4l2-mc.h> +#include <media/media-device.h> static int dvb_usbv2_disable_rc_polling; module_param_named(disable_rc_polling, dvb_usbv2_disable_rc_polling, int, 0644); @@ -408,10 +408,12 @@ static int dvb_usbv2_media_device_init(struct dvb_usb_adapter *adap) struct dvb_usb_device *d = adap_to_d(adap); struct usb_device *udev = d->udev; - mdev = v4l2_mc_usb_media_device_init(udev, d->name); + mdev = kzalloc(sizeof(*mdev), GFP_KERNEL); if (!mdev) return -ENOMEM; + media_device_usb_init(mdev, udev, d->name); + dvb_register_media_controller(&adap->dvb_adap, mdev); dev_info(&d->udev->dev, "media controller created\n"); diff --git a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c index c4c6e92e8643..fa72642d41f3 100644 --- a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c +++ b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c @@ -1571,19 +1571,19 @@ static int rtl28xxu_frontend_ctrl(struct dvb_frontend *fe, int onoff) if (dev->chip_id == CHIP_ID_RTL2831U) return 0; - /* control internal demod ADC */ - if (fe->id == 0 && onoff) - val = 0x48; /* enable ADC */ - else - val = 0x00; /* disable ADC */ - - ret = rtl28xxu_wr_reg_mask(d, SYS_DEMOD_CTL, val, 0x48); - if (ret) - goto err; + if (fe->id == 0) { + /* control internal demod ADC */ + if (onoff) + val = 0x48; /* enable ADC */ + else + val = 0x00; /* disable ADC */ - /* bypass slave demod TS through master demod */ - if (fe->id == 1 && onoff) { - ret = pdata->enable_slave_ts(dev->i2c_client_demod); + ret = rtl28xxu_wr_reg_mask(d, SYS_DEMOD_CTL, val, 0x48); + if (ret) + goto err; + } else if (fe->id == 1) { + /* bypass slave demod TS through master demod */ + ret = pdata->slave_ts_ctrl(dev->i2c_client_demod, onoff); if (ret) goto err; } diff --git a/drivers/media/usb/dvb-usb/dvb-usb-dvb.c b/drivers/media/usb/dvb-usb/dvb-usb-dvb.c index 513b0c14e4f0..6477b04e95c7 100644 --- a/drivers/media/usb/dvb-usb/dvb-usb-dvb.c +++ b/drivers/media/usb/dvb-usb/dvb-usb-dvb.c @@ -7,7 +7,7 @@ * linux-dvb API. */ #include "dvb-usb-common.h" -#include <media/v4l2-mc.h> +#include <media/media-device.h> /* does the complete input transfer handling */ static int dvb_usb_ctrl_feed(struct dvb_demux_feed *dvbdmxfeed, int onoff) @@ -103,7 +103,11 @@ static int dvb_usb_media_device_init(struct dvb_usb_adapter *adap) struct dvb_usb_device *d = adap->dev; struct usb_device *udev = d->udev; - mdev = v4l2_mc_usb_media_device_init(udev, d->desc->name); + mdev = kzalloc(sizeof(*mdev), GFP_KERNEL); + if (!mdev) + return -ENOMEM; + + media_device_usb_init(mdev, udev, d->desc->name); dvb_register_media_controller(&adap->dvb_adap, mdev); diff --git a/drivers/media/usb/dvb-usb/dw2102.c b/drivers/media/usb/dvb-usb/dw2102.c index dd46d6c78c4e..6d0dd859d684 100644 --- a/drivers/media/usb/dvb-usb/dw2102.c +++ b/drivers/media/usb/dvb-usb/dw2102.c @@ -1,9 +1,10 @@ /* DVB USB framework compliant Linux driver for the * DVBWorld DVB-S 2101, 2102, DVB-S2 2104, DVB-C 3101, - * TeVii S600, S630, S650, S660, S480, S421, S632 + * TeVii S421, S480, S482, S600, S630, S632, S650, S660, S662, * Prof 1100, 7500, * Geniatech SU3000, T220, - * TechnoTrend S2-4600 Cards + * TechnoTrend S2-4600, + * Terratec Cinergy S2 cards * Copyright (C) 2008-2012 Igor M. Liplianin (liplianin@me.by) * * This program is free software; you can redistribute it and/or modify it @@ -33,7 +34,6 @@ #include "tda18271.h" #include "cxd2820r.h" #include "m88ds3103.h" -#include "ts2020.h" /* Max transfer size done by I2C transfer functions */ #define MAX_XFER_SIZE 64 @@ -66,6 +66,10 @@ #define USB_PID_TEVII_S660 0xd660 #endif +#ifndef USB_PID_TEVII_S662 +#define USB_PID_TEVII_S662 0xd662 +#endif + #ifndef USB_PID_TEVII_S480_1 #define USB_PID_TEVII_S480_1 0xd481 #endif @@ -118,6 +122,7 @@ struct dw2102_state { u8 initialized; u8 last_lock; + struct i2c_client *i2c_client_demod; struct i2c_client *i2c_client_tuner; /* fe hook functions*/ @@ -1141,22 +1146,6 @@ static struct tda18271_config tda18271_config = { .gate = TDA18271_GATE_DIGITAL, }; -static const struct m88ds3103_config tt_s2_4600_m88ds3103_config = { - .i2c_addr = 0x68, - .clock = 27000000, - .i2c_wr_max = 33, - .ts_mode = M88DS3103_TS_CI, - .ts_clk = 16000, - .ts_clk_pol = 0, - .spec_inv = 0, - .agc_inv = 0, - .clock_out = M88DS3103_CLOCK_OUT_ENABLED, - .envelope_mode = 0, - .agc = 0x99, - .lnb_hv_pol = 1, - .lnb_en_pol = 0, -}; - static u8 m88rs2000_inittab[] = { DEMOD_WRITE, 0x9a, 0x30, DEMOD_WRITE, 0x00, 0x01, @@ -1509,7 +1498,8 @@ static int tt_s2_4600_frontend_attach(struct dvb_usb_adapter *adap) u8 ibuf[] = { 0 }; struct i2c_adapter *i2c_adapter; struct i2c_client *client; - struct i2c_board_info info; + struct i2c_board_info board_info; + struct m88ds3103_platform_data m88ds3103_pdata = {}; struct ts2020_config ts2020_config = {}; if (dvb_usb_generic_rw(d, obuf, 3, ibuf, 1, 0) < 0) @@ -1542,22 +1532,44 @@ static int tt_s2_4600_frontend_attach(struct dvb_usb_adapter *adap) if (dvb_usb_generic_rw(d, obuf, 1, ibuf, 1, 0) < 0) err("command 0x51 transfer failed."); - memset(&info, 0, sizeof(struct i2c_board_info)); - - adap->fe_adap[0].fe = dvb_attach(m88ds3103_attach, - &tt_s2_4600_m88ds3103_config, - &d->i2c_adap, - &i2c_adapter); - if (adap->fe_adap[0].fe == NULL) + /* attach demod */ + m88ds3103_pdata.clk = 27000000; + m88ds3103_pdata.i2c_wr_max = 33; + m88ds3103_pdata.ts_mode = M88DS3103_TS_CI; + m88ds3103_pdata.ts_clk = 16000; + m88ds3103_pdata.ts_clk_pol = 0; + m88ds3103_pdata.spec_inv = 0; + m88ds3103_pdata.agc = 0x99; + m88ds3103_pdata.agc_inv = 0; + m88ds3103_pdata.clk_out = M88DS3103_CLOCK_OUT_ENABLED; + m88ds3103_pdata.envelope_mode = 0; + m88ds3103_pdata.lnb_hv_pol = 1; + m88ds3103_pdata.lnb_en_pol = 0; + memset(&board_info, 0, sizeof(board_info)); + strlcpy(board_info.type, "m88ds3103", I2C_NAME_SIZE); + board_info.addr = 0x68; + board_info.platform_data = &m88ds3103_pdata; + request_module("m88ds3103"); + client = i2c_new_device(&d->i2c_adap, &board_info); + if (client == NULL || client->dev.driver == NULL) return -ENODEV; + if (!try_module_get(client->dev.driver->owner)) { + i2c_unregister_device(client); + return -ENODEV; + } + adap->fe_adap[0].fe = m88ds3103_pdata.get_dvb_frontend(client); + i2c_adapter = m88ds3103_pdata.get_i2c_adapter(client); + + state->i2c_client_demod = client; /* attach tuner */ ts2020_config.fe = adap->fe_adap[0].fe; - strlcpy(info.type, "ts2022", I2C_NAME_SIZE); - info.addr = 0x60; - info.platform_data = &ts2020_config; + memset(&board_info, 0, sizeof(board_info)); + strlcpy(board_info.type, "ts2022", I2C_NAME_SIZE); + board_info.addr = 0x60; + board_info.platform_data = &ts2020_config; request_module("ts2020"); - client = i2c_new_device(i2c_adapter, &info); + client = i2c_new_device(i2c_adapter, &board_info); if (client == NULL || client->dev.driver == NULL) { dvb_frontend_detach(adap->fe_adap[0].fe); @@ -1689,6 +1701,7 @@ enum dw2102_table_entry { TEVII_S482_1, TEVII_S482_2, TERRATEC_CINERGY_S2_BOX, + TEVII_S662 }; static struct usb_device_id dw2102_table[] = { @@ -1717,6 +1730,7 @@ static struct usb_device_id dw2102_table[] = { [TEVII_S482_1] = {USB_DEVICE(0x9022, 0xd483)}, [TEVII_S482_2] = {USB_DEVICE(0x9022, 0xd484)}, [TERRATEC_CINERGY_S2_BOX] = {USB_DEVICE(USB_VID_TERRATEC, 0x0105)}, + [TEVII_S662] = {USB_DEVICE(0x9022, USB_PID_TEVII_S662)}, { } }; @@ -2234,7 +2248,7 @@ static struct dvb_usb_device_properties tt_s2_4600_properties = { } }, } }, - .num_device_descs = 4, + .num_device_descs = 5, .devices = { { "TechnoTrend TT-connect S2-4600", { &dw2102_table[TECHNOTREND_S2_4600], NULL }, @@ -2252,6 +2266,10 @@ static struct dvb_usb_device_properties tt_s2_4600_properties = { { &dw2102_table[TERRATEC_CINERGY_S2_BOX], NULL }, { NULL }, }, + { "TeVii S662", + { &dw2102_table[TEVII_S662], NULL }, + { NULL }, + }, } }; @@ -2350,6 +2368,13 @@ static void dw2102_disconnect(struct usb_interface *intf) i2c_unregister_device(client); } + /* remove I2C client for demodulator */ + client = st->i2c_client_demod; + if (client) { + module_put(client->dev.driver->owner); + i2c_unregister_device(client); + } + dvb_usb_device_exit(intf); } @@ -2365,10 +2390,10 @@ module_usb_driver(dw2102_driver); MODULE_AUTHOR("Igor M. Liplianin (c) liplianin@me.by"); MODULE_DESCRIPTION("Driver for DVBWorld DVB-S 2101, 2102, DVB-S2 2104," " DVB-C 3101 USB2.0," - " TeVii S600, S630, S650, S660, S480, S421, S632" - " Prof 1100, 7500 USB2.0," + " TeVii S421, S480, S482, S600, S630, S632, S650," + " TeVii S660, S662, Prof 1100, 7500 USB2.0," " Geniatech SU3000, T220," - " TechnoTrend S2-4600 devices"); + " TechnoTrend S2-4600, Terratec Cinergy S2 devices"); MODULE_VERSION("0.1"); MODULE_LICENSE("GPL"); MODULE_FIRMWARE(DW2101_FIRMWARE); diff --git a/drivers/media/usb/dvb-usb/technisat-usb2.c b/drivers/media/usb/dvb-usb/technisat-usb2.c index 51487d2f7764..d9f3262bf071 100644 --- a/drivers/media/usb/dvb-usb/technisat-usb2.c +++ b/drivers/media/usb/dvb-usb/technisat-usb2.c @@ -60,6 +60,8 @@ struct technisat_usb2_state { u8 power_state; u16 last_scan_code; + + u8 buf[64]; }; /* debug print helpers */ @@ -220,19 +222,19 @@ enum technisat_usb2_led_state { TECH_LED_UNDEFINED }; -static int technisat_usb2_set_led(struct dvb_usb_device *d, int red, enum technisat_usb2_led_state state) +static int technisat_usb2_set_led(struct dvb_usb_device *d, int red, + enum technisat_usb2_led_state st) { + struct technisat_usb2_state *state = d->priv; + u8 *led = state->buf; int ret; - u8 led[8] = { - red ? SET_RED_LED_VENDOR_REQUEST : SET_GREEN_LED_VENDOR_REQUEST, - 0 - }; + led[0] = red ? SET_RED_LED_VENDOR_REQUEST : SET_GREEN_LED_VENDOR_REQUEST; - if (disable_led_control && state != TECH_LED_OFF) + if (disable_led_control && st != TECH_LED_OFF) return 0; - switch (state) { + switch (st) { case TECH_LED_ON: led[1] = 0x82; break; @@ -263,7 +265,7 @@ static int technisat_usb2_set_led(struct dvb_usb_device *d, int red, enum techni red ? SET_RED_LED_VENDOR_REQUEST : SET_GREEN_LED_VENDOR_REQUEST, USB_TYPE_VENDOR | USB_DIR_OUT, 0, 0, - led, sizeof(led), 500); + led, 8, 500); mutex_unlock(&d->i2c_mutex); return ret; @@ -271,8 +273,11 @@ static int technisat_usb2_set_led(struct dvb_usb_device *d, int red, enum techni static int technisat_usb2_set_led_timer(struct dvb_usb_device *d, u8 red, u8 green) { + struct technisat_usb2_state *state = d->priv; + u8 *b = state->buf; int ret; - u8 b = 0; + + b[0] = 0; if (mutex_lock_interruptible(&d->i2c_mutex) < 0) return -EAGAIN; @@ -281,7 +286,7 @@ static int technisat_usb2_set_led_timer(struct dvb_usb_device *d, u8 red, u8 gre SET_LED_TIMER_DIVIDER_VENDOR_REQUEST, USB_TYPE_VENDOR | USB_DIR_OUT, (red << 8) | green, 0, - &b, 1, 500); + b, 1, 500); mutex_unlock(&d->i2c_mutex); @@ -328,7 +333,11 @@ static int technisat_usb2_identify_state(struct usb_device *udev, struct dvb_usb_device_description **desc, int *cold) { int ret; - u8 version[3]; + u8 *version; + + version = kmalloc(3, GFP_KERNEL); + if (!version) + return -ENOMEM; /* first select the interface */ if (usb_set_interface(udev, 0, 1) != 0) @@ -342,7 +351,7 @@ static int technisat_usb2_identify_state(struct usb_device *udev, GET_VERSION_INFO_VENDOR_REQUEST, USB_TYPE_VENDOR | USB_DIR_IN, 0, 0, - version, sizeof(version), 500); + version, 3, 500); if (ret < 0) *cold = 1; @@ -351,6 +360,8 @@ static int technisat_usb2_identify_state(struct usb_device *udev, *cold = 0; } + kfree(version); + return 0; } @@ -594,7 +605,9 @@ static int technisat_usb2_frontend_attach(struct dvb_usb_adapter *a) static int technisat_usb2_get_ir(struct dvb_usb_device *d) { - u8 buf[62], *b; + struct technisat_usb2_state *state = d->priv; + u8 *buf = state->buf; + u8 *b; int ret; struct ir_raw_event ev; @@ -620,7 +633,7 @@ static int technisat_usb2_get_ir(struct dvb_usb_device *d) GET_IR_DATA_VENDOR_REQUEST, USB_TYPE_VENDOR | USB_DIR_IN, 0x8080, 0, - buf, sizeof(buf), 500); + buf, 62, 500); unlock: mutex_unlock(&d->i2c_mutex); diff --git a/drivers/media/usb/em28xx/em28xx-cards.c b/drivers/media/usb/em28xx/em28xx-cards.c index 389e95fb0211..930e3e3fc948 100644 --- a/drivers/media/usb/em28xx/em28xx-cards.c +++ b/drivers/media/usb/em28xx/em28xx-cards.c @@ -37,6 +37,7 @@ #include <media/i2c-addr.h> #include <media/tveeprom.h> #include <media/v4l2-common.h> +#include <sound/ac97_codec.h> #include "em28xx.h" @@ -560,6 +561,16 @@ static struct em28xx_led pctv_80e_leds[] = { {-1, 0, 0, 0}, }; +static struct em28xx_led terratec_grabby_leds[] = { + { + .role = EM28XX_LED_ANALOG_CAPTURING, + .gpio_reg = EM2820_R08_GPIO_CTRL, + .gpio_mask = EM_GPIO_3, + .inverted = 1, + }, + {-1, 0, 0, 0}, +}; + /* * Board definitions */ @@ -2015,6 +2026,8 @@ struct em28xx_board em28xx_boards[] = { .vmux = SAA7115_SVIDEO3, .amux = EM28XX_AMUX_LINE_IN, } }, + .buttons = std_snapshot_button, + .leds = terratec_grabby_leds, }, [EM2860_BOARD_TERRATEC_AV350] = { .name = "Terratec AV350", @@ -2551,6 +2564,36 @@ static inline void em28xx_set_model(struct em28xx *dev) dev->def_i2c_bus = dev->board.def_i2c_bus; } +/* Wait until AC97_RESET reports the expected value reliably before proceeding. + * We also check that two unrelated registers accesses don't return the same + * value to avoid premature return. + * This procedure helps ensuring AC97 register accesses are reliable. + */ +static int em28xx_wait_until_ac97_features_equals(struct em28xx *dev, + int expected_feat) +{ + unsigned long timeout = jiffies + msecs_to_jiffies(2000); + int feat, powerdown; + + while (time_is_after_jiffies(timeout)) { + feat = em28xx_read_ac97(dev, AC97_RESET); + if (feat < 0) + return feat; + + powerdown = em28xx_read_ac97(dev, AC97_POWERDOWN); + if (powerdown < 0) + return powerdown; + + if (feat == expected_feat && feat != powerdown) + return 0; + + msleep(50); + } + + em28xx_warn("AC97 registers access is not reliable !\n"); + return -ETIMEDOUT; +} + /* Since em28xx_pre_card_setup() requires a proper dev->model, * this won't work for boards with generic PCI IDs */ @@ -2656,6 +2699,13 @@ static void em28xx_pre_card_setup(struct em28xx *dev) em28xx_write_reg(dev, EM2820_R08_GPIO_CTRL, 0xfd); msleep(70); break; + + case EM2860_BOARD_TERRATEC_GRABBY: + /* HACK?: Ensure AC97 register reading is reliable before + * proceeding. In practice, this will wait about 1.6 seconds. + */ + em28xx_wait_until_ac97_features_equals(dev, 0x6a90); + break; } em28xx_gpio_set(dev, dev->board.tuner_gpio); @@ -3019,17 +3069,17 @@ static int em28xx_media_device_init(struct em28xx *dev, #ifdef CONFIG_MEDIA_CONTROLLER struct media_device *mdev; - if (udev->product) { - mdev = v4l2_mc_usb_media_device_init(udev, udev->product); - } else if (udev->manufacturer) { - mdev = v4l2_mc_usb_media_device_init(udev, udev->manufacturer); - } else { - mdev = v4l2_mc_usb_media_device_init(udev, dev->name); - } - + mdev = kzalloc(sizeof(*mdev), GFP_KERNEL); if (!mdev) return -ENOMEM; + if (udev->product) + media_device_usb_init(mdev, udev, udev->product); + else if (udev->manufacturer) + media_device_usb_init(mdev, udev, udev->manufacturer); + else + media_device_usb_init(mdev, udev, dev->name); + dev->media_dev = mdev; #endif return 0; diff --git a/drivers/media/usb/em28xx/em28xx-video.c b/drivers/media/usb/em28xx/em28xx-video.c index f772e2612608..44834b2eff55 100644 --- a/drivers/media/usb/em28xx/em28xx-video.c +++ b/drivers/media/usb/em28xx/em28xx-video.c @@ -2714,12 +2714,14 @@ static int em28xx_v4l2_init(struct em28xx *dev) /* Init entities at the Media Controller */ em28xx_v4l2_create_entities(dev); +#ifdef CONFIG_MEDIA_CONTROLLER ret = v4l2_mc_create_media_graph(dev->media_dev); if (ret) { em28xx_errdev("failed to create media graph\n"); em28xx_v4l2_media_release(dev); goto unregister_dev; } +#endif em28xx_info("V4L2 video device registered as %s\n", video_device_node_name(&v4l2->vdev)); diff --git a/drivers/media/usb/gspca/ov519.c b/drivers/media/usb/gspca/ov519.c index c95f32a0c02b..965372a5ff2f 100644 --- a/drivers/media/usb/gspca/ov519.c +++ b/drivers/media/usb/gspca/ov519.c @@ -360,40 +360,6 @@ static const struct v4l2_pix_format ov511_sif_mode[] = { .priv = 0}, }; -static const struct v4l2_pix_format ovfx2_vga_mode[] = { - {320, 240, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, - .bytesperline = 320, - .sizeimage = 320 * 240, - .colorspace = V4L2_COLORSPACE_SRGB, - .priv = 1}, - {640, 480, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, - .bytesperline = 640, - .sizeimage = 640 * 480, - .colorspace = V4L2_COLORSPACE_SRGB, - .priv = 0}, -}; -static const struct v4l2_pix_format ovfx2_cif_mode[] = { - {160, 120, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, - .bytesperline = 160, - .sizeimage = 160 * 120, - .colorspace = V4L2_COLORSPACE_SRGB, - .priv = 3}, - {176, 144, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, - .bytesperline = 176, - .sizeimage = 176 * 144, - .colorspace = V4L2_COLORSPACE_SRGB, - .priv = 1}, - {320, 240, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, - .bytesperline = 320, - .sizeimage = 320 * 240, - .colorspace = V4L2_COLORSPACE_SRGB, - .priv = 2}, - {352, 288, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, - .bytesperline = 352, - .sizeimage = 352 * 288, - .colorspace = V4L2_COLORSPACE_SRGB, - .priv = 0}, -}; static const struct v4l2_pix_format ovfx2_ov2610_mode[] = { {800, 600, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, .bytesperline = 800, @@ -2042,6 +2008,9 @@ static void reg_w(struct sd *sd, u16 index, u16 value) if (sd->gspca_dev.usb_err < 0) return; + /* Avoid things going to fast for the bridge with a xhci host */ + udelay(150); + switch (sd->bridge) { case BRIDGE_OV511: case BRIDGE_OV511PLUS: @@ -2103,6 +2072,8 @@ static int reg_r(struct sd *sd, u16 index) req = 1; } + /* Avoid things going to fast for the bridge with a xhci host */ + udelay(150); ret = usb_control_msg(sd->gspca_dev.dev, usb_rcvctrlpipe(sd->gspca_dev.dev, 0), req, @@ -2131,6 +2102,8 @@ static int reg_r8(struct sd *sd, if (sd->gspca_dev.usb_err < 0) return -1; + /* Avoid things going to fast for the bridge with a xhci host */ + udelay(150); ret = usb_control_msg(sd->gspca_dev.dev, usb_rcvctrlpipe(sd->gspca_dev.dev, 0), 1, /* REQ_IO */ @@ -2187,6 +2160,8 @@ static void ov518_reg_w32(struct sd *sd, u16 index, u32 value, int n) *((__le32 *) sd->gspca_dev.usb_buf) = __cpu_to_le32(value); + /* Avoid things going to fast for the bridge with a xhci host */ + udelay(150); ret = usb_control_msg(sd->gspca_dev.dev, usb_sndctrlpipe(sd->gspca_dev.dev, 0), 1 /* REG_IO */, diff --git a/drivers/media/usb/gspca/touptek.c b/drivers/media/usb/gspca/touptek.c index 7bac6bc96063..b8af4370d27c 100644 --- a/drivers/media/usb/gspca/touptek.c +++ b/drivers/media/usb/gspca/touptek.c @@ -203,7 +203,7 @@ static int val_reply(struct gspca_dev *gspca_dev, const char *reply, int rc) return -EIO; } if (reply[0] != 0x08) { - PERR("Bad reply 0x%02X", reply[0]); + PERR("Bad reply 0x%02x", (int)reply[0]); return -EIO; } return 0; @@ -211,7 +211,7 @@ static int val_reply(struct gspca_dev *gspca_dev, const char *reply, int rc) static void reg_w(struct gspca_dev *gspca_dev, u16 value, u16 index) { - char buff[1]; + char *buff = gspca_dev->usb_buf; int rc; PDEBUG(D_USBO, @@ -219,7 +219,7 @@ static void reg_w(struct gspca_dev *gspca_dev, u16 value, u16 index) value, index); rc = usb_control_msg(gspca_dev->dev, usb_rcvctrlpipe(gspca_dev->dev, 0), 0x0B, 0xC0, value, index, buff, 1, 500); - PDEBUG(D_USBO, "rc=%d, ret={0x%02X}", rc, buff[0]); + PDEBUG(D_USBO, "rc=%d, ret={0x%02x}", rc, (int)buff[0]); if (rc < 0) { PERR("Failed reg_w(0x0B, 0xC0, 0x%04X, 0x%04X) w/ rc %d\n", value, index, rc); @@ -438,7 +438,7 @@ static void configure_encrypted(struct gspca_dev *gspca_dev) static int configure(struct gspca_dev *gspca_dev) { int rc; - uint8_t buff[4]; + char *buff = gspca_dev->usb_buf; PDEBUG(D_STREAM, "configure()\n"); diff --git a/drivers/media/usb/gspca/w996Xcf.c b/drivers/media/usb/gspca/w996Xcf.c index fb9fe2ef3a6f..896f1b2b9179 100644 --- a/drivers/media/usb/gspca/w996Xcf.c +++ b/drivers/media/usb/gspca/w996Xcf.c @@ -79,6 +79,8 @@ static void w9968cf_write_fsb(struct sd *sd, u16* data) value = *data++; memcpy(sd->gspca_dev.usb_buf, data, 6); + /* Avoid things going to fast for the bridge with a xhci host */ + udelay(150); ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0, USB_TYPE_VENDOR | USB_DIR_OUT | USB_RECIP_DEVICE, value, 0x06, sd->gspca_dev.usb_buf, 6, 500); @@ -99,6 +101,9 @@ static void w9968cf_write_sb(struct sd *sd, u16 value) if (sd->gspca_dev.usb_err < 0) return; + /* Avoid things going to fast for the bridge with a xhci host */ + udelay(150); + /* We don't use reg_w here, as that would cause all writes when bitbanging i2c to be logged, making the logs impossible to read */ ret = usb_control_msg(sd->gspca_dev.dev, @@ -126,6 +131,9 @@ static int w9968cf_read_sb(struct sd *sd) if (sd->gspca_dev.usb_err < 0) return -1; + /* Avoid things going to fast for the bridge with a xhci host */ + udelay(150); + /* We don't use reg_r here, as the w9968cf is special and has 16 bit registers instead of 8 bit */ ret = usb_control_msg(sd->gspca_dev.dev, diff --git a/drivers/media/usb/pvrusb2/pvrusb2-hdw.c b/drivers/media/usb/pvrusb2/pvrusb2-hdw.c index 0533ef20decf..1a093e5953fd 100644 --- a/drivers/media/usb/pvrusb2/pvrusb2-hdw.c +++ b/drivers/media/usb/pvrusb2/pvrusb2-hdw.c @@ -4903,6 +4903,9 @@ static void pvr2_hdw_state_log_state(struct pvr2_hdw *hdw) printk(KERN_INFO "%s %.*s\n",hdw->name,ccnt,buf); } ccnt = pvr2_hdw_report_clients(hdw, buf, sizeof(buf)); + if (ccnt >= sizeof(buf)) + ccnt = sizeof(buf); + ucnt = 0; while (ucnt < ccnt) { lcnt = 0; diff --git a/drivers/media/usb/pvrusb2/pvrusb2-io.c b/drivers/media/usb/pvrusb2/pvrusb2-io.c index d860344de84e..e68ce24f27e3 100644 --- a/drivers/media/usb/pvrusb2/pvrusb2-io.c +++ b/drivers/media/usb/pvrusb2/pvrusb2-io.c @@ -473,7 +473,7 @@ static void buffer_complete(struct urb *urb) } spin_unlock_irqrestore(&sp->list_lock,irq_flags); pvr2_buffer_set_ready(bp); - if (sp && sp->callback_func) { + if (sp->callback_func) { sp->callback_func(sp->callback_data); } } diff --git a/drivers/media/usb/siano/smsusb.c b/drivers/media/usb/siano/smsusb.c index 4dac499ed28e..c2e25876e93b 100644 --- a/drivers/media/usb/siano/smsusb.c +++ b/drivers/media/usb/siano/smsusb.c @@ -27,7 +27,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. #include <linux/firmware.h> #include <linux/slab.h> #include <linux/module.h> -#include <media/v4l2-mc.h> +#include <media/media-device.h> #include "sms-cards.h" #include "smsendian.h" @@ -367,10 +367,12 @@ static void *siano_media_device_register(struct smsusb_device_t *dev, struct sms_board *board = sms_get_board(board_id); int ret; - mdev = v4l2_mc_usb_media_device_init(udev, board->name); + mdev = kzalloc(sizeof(*mdev), GFP_KERNEL); if (!mdev) return NULL; + media_device_usb_init(mdev, udev, board->name); + ret = media_device_register(mdev); if (ret) { media_device_cleanup(mdev); diff --git a/drivers/media/usb/uvc/uvc_driver.c b/drivers/media/usb/uvc/uvc_driver.c index 4e7148815a78..451e84e962e2 100644 --- a/drivers/media/usb/uvc/uvc_driver.c +++ b/drivers/media/usb/uvc/uvc_driver.c @@ -148,6 +148,26 @@ static struct uvc_format_desc uvc_fmts[] = { .guid = UVC_GUID_FORMAT_H264, .fcc = V4L2_PIX_FMT_H264, }, + { + .name = "Greyscale 8 L/R (Y8I)", + .guid = UVC_GUID_FORMAT_Y8I, + .fcc = V4L2_PIX_FMT_Y8I, + }, + { + .name = "Greyscale 12 L/R (Y12I)", + .guid = UVC_GUID_FORMAT_Y12I, + .fcc = V4L2_PIX_FMT_Y12I, + }, + { + .name = "Depth data 16-bit (Z16)", + .guid = UVC_GUID_FORMAT_Z16, + .fcc = V4L2_PIX_FMT_Z16, + }, + { + .name = "Bayer 10-bit (SRGGB10P)", + .guid = UVC_GUID_FORMAT_RW10, + .fcc = V4L2_PIX_FMT_SRGGB10P, + }, }; /* ------------------------------------------------------------------------ diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h index f0f2391e1b43..7e4d3eea371b 100644 --- a/drivers/media/usb/uvc/uvcvideo.h +++ b/drivers/media/usb/uvc/uvcvideo.h @@ -119,6 +119,18 @@ #define UVC_GUID_FORMAT_H264 \ { 'H', '2', '6', '4', 0x00, 0x00, 0x10, 0x00, \ 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71} +#define UVC_GUID_FORMAT_Y8I \ + { 'Y', '8', 'I', ' ', 0x00, 0x00, 0x10, 0x00, \ + 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71} +#define UVC_GUID_FORMAT_Y12I \ + { 'Y', '1', '2', 'I', 0x00, 0x00, 0x10, 0x00, \ + 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71} +#define UVC_GUID_FORMAT_Z16 \ + { 'Z', '1', '6', ' ', 0x00, 0x00, 0x10, 0x00, \ + 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71} +#define UVC_GUID_FORMAT_RW10 \ + { 'R', 'W', '1', '0', 0x00, 0x00, 0x10, 0x00, \ + 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71} /* ------------------------------------------------------------------------ * Driver specific constants. diff --git a/drivers/media/v4l2-core/v4l2-fh.c b/drivers/media/v4l2-core/v4l2-fh.c index c97067a25bd2..c183f0996fa1 100644 --- a/drivers/media/v4l2-core/v4l2-fh.c +++ b/drivers/media/v4l2-core/v4l2-fh.c @@ -29,6 +29,7 @@ #include <media/v4l2-fh.h> #include <media/v4l2-event.h> #include <media/v4l2-ioctl.h> +#include <media/v4l2-mc.h> void v4l2_fh_init(struct v4l2_fh *fh, struct video_device *vdev) { @@ -92,6 +93,7 @@ void v4l2_fh_exit(struct v4l2_fh *fh) { if (fh->vdev == NULL) return; + v4l_disable_media_source(fh->vdev); v4l2_event_unsubscribe_all(fh); fh->vdev = NULL; } diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c index 14843090fd61..170dd68d27f4 100644 --- a/drivers/media/v4l2-core/v4l2-ioctl.c +++ b/drivers/media/v4l2-core/v4l2-ioctl.c @@ -27,6 +27,7 @@ #include <media/v4l2-event.h> #include <media/v4l2-device.h> #include <media/videobuf2-v4l2.h> +#include <media/v4l2-mc.h> #include <trace/events/v4l2.h> @@ -1041,6 +1042,12 @@ static int v4l_querycap(const struct v4l2_ioctl_ops *ops, static int v4l_s_input(const struct v4l2_ioctl_ops *ops, struct file *file, void *fh, void *arg) { + struct video_device *vfd = video_devdata(file); + int ret; + + ret = v4l_enable_media_source(vfd); + if (ret) + return ret; return ops->vidioc_s_input(file, fh, *(unsigned int *)arg); } @@ -1165,7 +1172,7 @@ static void v4l_fill_fmtdesc(struct v4l2_fmtdesc *fmt) case V4L2_PIX_FMT_YVYU: descr = "YVYU 4:2:2"; break; case V4L2_PIX_FMT_UYVY: descr = "UYVY 4:2:2"; break; case V4L2_PIX_FMT_VYUY: descr = "VYUY 4:2:2"; break; - case V4L2_PIX_FMT_YUV422P: descr = "Planar YVU 4:2:2"; break; + case V4L2_PIX_FMT_YUV422P: descr = "Planar YUV 4:2:2"; break; case V4L2_PIX_FMT_YUV411P: descr = "Planar YUV 4:1:1"; break; case V4L2_PIX_FMT_Y41P: descr = "YUV 4:1:1 (Packed)"; break; case V4L2_PIX_FMT_YUV444: descr = "16-bit A/XYUV 4-4-4-4"; break; @@ -1452,6 +1459,9 @@ static int v4l_s_fmt(const struct v4l2_ioctl_ops *ops, bool is_tx = vfd->vfl_dir != VFL_DIR_RX; int ret; + ret = v4l_enable_media_source(vfd); + if (ret) + return ret; v4l_sanitize_format(p); switch (p->type) { @@ -1641,7 +1651,11 @@ static int v4l_s_tuner(const struct v4l2_ioctl_ops *ops, { struct video_device *vfd = video_devdata(file); struct v4l2_tuner *p = arg; + int ret; + ret = v4l_enable_media_source(vfd); + if (ret) + return ret; p->type = (vfd->vfl_type == VFL_TYPE_RADIO) ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV; return ops->vidioc_s_tuner(file, fh, p); @@ -1695,7 +1709,11 @@ static int v4l_s_frequency(const struct v4l2_ioctl_ops *ops, struct video_device *vfd = video_devdata(file); const struct v4l2_frequency *p = arg; enum v4l2_tuner_type type; + int ret; + ret = v4l_enable_media_source(vfd); + if (ret) + return ret; if (vfd->vfl_type == VFL_TYPE_SDR) { if (p->type != V4L2_TUNER_SDR && p->type != V4L2_TUNER_RF) return -EINVAL; @@ -1750,7 +1768,11 @@ static int v4l_s_std(const struct v4l2_ioctl_ops *ops, { struct video_device *vfd = video_devdata(file); v4l2_std_id id = *(v4l2_std_id *)arg, norm; + int ret; + ret = v4l_enable_media_source(vfd); + if (ret) + return ret; norm = id & vfd->tvnorms; if (vfd->tvnorms && !norm) /* Check if std is supported */ return -EINVAL; @@ -1764,7 +1786,11 @@ static int v4l_querystd(const struct v4l2_ioctl_ops *ops, { struct video_device *vfd = video_devdata(file); v4l2_std_id *p = arg; + int ret; + ret = v4l_enable_media_source(vfd); + if (ret) + return ret; /* * If no signal is detected, then the driver should return * V4L2_STD_UNKNOWN. Otherwise it should return tvnorms with @@ -1783,7 +1809,11 @@ static int v4l_s_hw_freq_seek(const struct v4l2_ioctl_ops *ops, struct video_device *vfd = video_devdata(file); struct v4l2_hw_freq_seek *p = arg; enum v4l2_tuner_type type; + int ret; + ret = v4l_enable_media_source(vfd); + if (ret) + return ret; /* s_hw_freq_seek is not supported for SDR for now */ if (vfd->vfl_type == VFL_TYPE_SDR) return -EINVAL; diff --git a/drivers/media/v4l2-core/v4l2-mc.c b/drivers/media/v4l2-core/v4l2-mc.c index a7f41b323522..2228cd3a846e 100644 --- a/drivers/media/v4l2-core/v4l2-mc.c +++ b/drivers/media/v4l2-core/v4l2-mc.c @@ -1,7 +1,10 @@ /* * Media Controller ancillary functions * - * (c) 2016 Mauro Carvalho Chehab <mchehab@osg.samsung.com> + * Copyright (c) 2016 Mauro Carvalho Chehab <mchehab@osg.samsung.com> + * Copyright (C) 2016 Shuah Khan <shuahkh@osg.samsung.com> + * Copyright (C) 2006-2010 Nokia Corporation + * Copyright (c) 2016 Intel Corporation. * * 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 @@ -17,80 +20,14 @@ #include <linux/module.h> #include <linux/pci.h> #include <linux/usb.h> +#include <media/media-device.h> #include <media/media-entity.h> +#include <media/v4l2-fh.h> #include <media/v4l2-mc.h> - - -struct media_device *v4l2_mc_pci_media_device_init(struct pci_dev *pci_dev, - const char *name) -{ -#ifdef CONFIG_PCI - struct media_device *mdev; - - mdev = kzalloc(sizeof(*mdev), GFP_KERNEL); - if (!mdev) - return NULL; - - mdev->dev = &pci_dev->dev; - - if (name) - strlcpy(mdev->model, name, sizeof(mdev->model)); - else - strlcpy(mdev->model, pci_name(pci_dev), sizeof(mdev->model)); - - sprintf(mdev->bus_info, "PCI:%s", pci_name(pci_dev)); - - mdev->hw_revision = pci_dev->subsystem_vendor << 16 - || pci_dev->subsystem_device; - - mdev->driver_version = LINUX_VERSION_CODE; - - media_device_init(mdev); - - return mdev; -#else - return NULL; -#endif -} -EXPORT_SYMBOL_GPL(v4l2_mc_pci_media_device_init); - -struct media_device *__v4l2_mc_usb_media_device_init(struct usb_device *udev, - const char *board_name, - const char *driver_name) -{ -#ifdef CONFIG_USB - struct media_device *mdev; - - mdev = kzalloc(sizeof(*mdev), GFP_KERNEL); - if (!mdev) - return NULL; - - mdev->dev = &udev->dev; - - if (driver_name) - strlcpy(mdev->driver_name, driver_name, - sizeof(mdev->driver_name)); - - if (board_name) - strlcpy(mdev->model, board_name, sizeof(mdev->model)); - else if (udev->product) - strlcpy(mdev->model, udev->product, sizeof(mdev->model)); - else - strlcpy(mdev->model, "unknown model", sizeof(mdev->model)); - if (udev->serial) - strlcpy(mdev->serial, udev->serial, sizeof(mdev->serial)); - usb_make_path(udev, mdev->bus_info, sizeof(mdev->bus_info)); - mdev->hw_revision = le16_to_cpu(udev->descriptor.bcdDevice); - mdev->driver_version = LINUX_VERSION_CODE; - - media_device_init(mdev); - - return mdev; -#else - return NULL; -#endif -} -EXPORT_SYMBOL_GPL(__v4l2_mc_usb_media_device_init); +#include <media/v4l2-subdev.h> +#include <media/media-device.h> +#include <media/v4l2-mc.h> +#include <media/videobuf2-core.h> int v4l2_mc_create_media_graph(struct media_device *mdev) @@ -253,6 +190,214 @@ int v4l2_mc_create_media_graph(struct media_device *mdev) flags = 0; } + return 0; } EXPORT_SYMBOL_GPL(v4l2_mc_create_media_graph); + +int v4l_enable_media_source(struct video_device *vdev) +{ + struct media_device *mdev = vdev->entity.graph_obj.mdev; + int ret; + + if (!mdev || !mdev->enable_source) + return 0; + ret = mdev->enable_source(&vdev->entity, &vdev->pipe); + if (ret) + return -EBUSY; + return 0; +} +EXPORT_SYMBOL_GPL(v4l_enable_media_source); + +void v4l_disable_media_source(struct video_device *vdev) +{ + struct media_device *mdev = vdev->entity.graph_obj.mdev; + + if (mdev && mdev->disable_source) + mdev->disable_source(&vdev->entity); +} +EXPORT_SYMBOL_GPL(v4l_disable_media_source); + +int v4l_vb2q_enable_media_source(struct vb2_queue *q) +{ + struct v4l2_fh *fh = q->owner; + + if (fh && fh->vdev) + return v4l_enable_media_source(fh->vdev); + return 0; +} +EXPORT_SYMBOL_GPL(v4l_vb2q_enable_media_source); + +/* ----------------------------------------------------------------------------- + * Pipeline power management + * + * Entities must be powered up when part of a pipeline that contains at least + * one open video device node. + * + * To achieve this use the entity use_count field to track the number of users. + * For entities corresponding to video device nodes the use_count field stores + * the users count of the node. For entities corresponding to subdevs the + * use_count field stores the total number of users of all video device nodes + * in the pipeline. + * + * The v4l2_pipeline_pm_use() function must be called in the open() and + * close() handlers of video device nodes. It increments or decrements the use + * count of all subdev entities in the pipeline. + * + * To react to link management on powered pipelines, the link setup notification + * callback updates the use count of all entities in the source and sink sides + * of the link. + */ + +/* + * pipeline_pm_use_count - Count the number of users of a pipeline + * @entity: The entity + * + * Return the total number of users of all video device nodes in the pipeline. + */ +static int pipeline_pm_use_count(struct media_entity *entity, + struct media_entity_graph *graph) +{ + int use = 0; + + media_entity_graph_walk_start(graph, entity); + + while ((entity = media_entity_graph_walk_next(graph))) { + if (is_media_entity_v4l2_io(entity)) + use += entity->use_count; + } + + return use; +} + +/* + * pipeline_pm_power_one - Apply power change to an entity + * @entity: The entity + * @change: Use count change + * + * Change the entity use count by @change. If the entity is a subdev update its + * power state by calling the core::s_power operation when the use count goes + * from 0 to != 0 or from != 0 to 0. + * + * Return 0 on success or a negative error code on failure. + */ +static int pipeline_pm_power_one(struct media_entity *entity, int change) +{ + struct v4l2_subdev *subdev; + int ret; + + subdev = is_media_entity_v4l2_subdev(entity) + ? media_entity_to_v4l2_subdev(entity) : NULL; + + if (entity->use_count == 0 && change > 0 && subdev != NULL) { + ret = v4l2_subdev_call(subdev, core, s_power, 1); + if (ret < 0 && ret != -ENOIOCTLCMD) + return ret; + } + + entity->use_count += change; + WARN_ON(entity->use_count < 0); + + if (entity->use_count == 0 && change < 0 && subdev != NULL) + v4l2_subdev_call(subdev, core, s_power, 0); + + return 0; +} + +/* + * pipeline_pm_power - Apply power change to all entities in a pipeline + * @entity: The entity + * @change: Use count change + * + * Walk the pipeline to update the use count and the power state of all non-node + * entities. + * + * Return 0 on success or a negative error code on failure. + */ +static int pipeline_pm_power(struct media_entity *entity, int change, + struct media_entity_graph *graph) +{ + struct media_entity *first = entity; + int ret = 0; + + if (!change) + return 0; + + media_entity_graph_walk_start(graph, entity); + + while (!ret && (entity = media_entity_graph_walk_next(graph))) + if (is_media_entity_v4l2_subdev(entity)) + ret = pipeline_pm_power_one(entity, change); + + if (!ret) + return ret; + + media_entity_graph_walk_start(graph, first); + + while ((first = media_entity_graph_walk_next(graph)) + && first != entity) + if (is_media_entity_v4l2_subdev(first)) + pipeline_pm_power_one(first, -change); + + return ret; +} + +int v4l2_pipeline_pm_use(struct media_entity *entity, int use) +{ + struct media_device *mdev = entity->graph_obj.mdev; + int change = use ? 1 : -1; + int ret; + + mutex_lock(&mdev->graph_mutex); + + /* Apply use count to node. */ + entity->use_count += change; + WARN_ON(entity->use_count < 0); + + /* Apply power change to connected non-nodes. */ + ret = pipeline_pm_power(entity, change, &mdev->pm_count_walk); + if (ret < 0) + entity->use_count -= change; + + mutex_unlock(&mdev->graph_mutex); + + return ret; +} +EXPORT_SYMBOL_GPL(v4l2_pipeline_pm_use); + +int v4l2_pipeline_link_notify(struct media_link *link, u32 flags, + unsigned int notification) +{ + struct media_entity_graph *graph = &link->graph_obj.mdev->pm_count_walk; + struct media_entity *source = link->source->entity; + struct media_entity *sink = link->sink->entity; + int source_use; + int sink_use; + int ret = 0; + + source_use = pipeline_pm_use_count(source, graph); + sink_use = pipeline_pm_use_count(sink, graph); + + if (notification == MEDIA_DEV_NOTIFY_POST_LINK_CH && + !(flags & MEDIA_LNK_FL_ENABLED)) { + /* Powering off entities is assumed to never fail. */ + pipeline_pm_power(source, -sink_use, graph); + pipeline_pm_power(sink, -source_use, graph); + return 0; + } + + if (notification == MEDIA_DEV_NOTIFY_PRE_LINK_CH && + (flags & MEDIA_LNK_FL_ENABLED)) { + + ret = pipeline_pm_power(source, sink_use, graph); + if (ret < 0) + return ret; + + ret = pipeline_pm_power(sink, source_use, graph); + if (ret < 0) + pipeline_pm_power(source, -sink_use, graph); + } + + return ret; +} +EXPORT_SYMBOL_GPL(v4l2_pipeline_link_notify); diff --git a/drivers/media/v4l2-core/videobuf-core.c b/drivers/media/v4l2-core/videobuf-core.c index 6c02989ee33f..def84753c4c3 100644 --- a/drivers/media/v4l2-core/videobuf-core.c +++ b/drivers/media/v4l2-core/videobuf-core.c @@ -75,7 +75,8 @@ struct videobuf_buffer *videobuf_alloc_vb(struct videobuf_queue *q) } EXPORT_SYMBOL_GPL(videobuf_alloc_vb); -static int is_state_active_or_queued(struct videobuf_queue *q, struct videobuf_buffer *vb) +static int state_neither_active_nor_queued(struct videobuf_queue *q, + struct videobuf_buffer *vb) { unsigned long flags; bool rc; @@ -95,7 +96,7 @@ int videobuf_waiton(struct videobuf_queue *q, struct videobuf_buffer *vb, MAGIC_CHECK(vb->magic, MAGIC_BUFFER); if (non_blocking) { - if (is_state_active_or_queued(q, vb)) + if (state_neither_active_nor_queued(q, vb)) return 0; return -EAGAIN; } @@ -107,9 +108,10 @@ int videobuf_waiton(struct videobuf_queue *q, struct videobuf_buffer *vb, if (is_ext_locked) mutex_unlock(q->ext_lock); if (intr) - ret = wait_event_interruptible(vb->done, is_state_active_or_queued(q, vb)); + ret = wait_event_interruptible(vb->done, + state_neither_active_nor_queued(q, vb)); else - wait_event(vb->done, is_state_active_or_queued(q, vb)); + wait_event(vb->done, state_neither_active_nor_queued(q, vb)); /* Relock */ if (is_ext_locked) mutex_lock(q->ext_lock); diff --git a/drivers/media/v4l2-core/videobuf-dma-sg.c b/drivers/media/v4l2-core/videobuf-dma-sg.c index f669cedca8bd..df4c052c6bd6 100644 --- a/drivers/media/v4l2-core/videobuf-dma-sg.c +++ b/drivers/media/v4l2-core/videobuf-dma-sg.c @@ -181,8 +181,7 @@ static int videobuf_dma_init_user_locked(struct videobuf_dmabuf *dma, dprintk(1, "init user [0x%lx+0x%lx => %d pages]\n", data, size, dma->nr_pages); - err = get_user_pages(current, current->mm, - data & PAGE_MASK, dma->nr_pages, + err = get_user_pages(data & PAGE_MASK, dma->nr_pages, rw == READ, 1, /* force */ dma->pages, NULL); diff --git a/drivers/media/v4l2-core/videobuf2-core.c b/drivers/media/v4l2-core/videobuf2-core.c index dab94080ec3a..5d016f496e0e 100644 --- a/drivers/media/v4l2-core/videobuf2-core.c +++ b/drivers/media/v4l2-core/videobuf2-core.c @@ -25,6 +25,7 @@ #include <linux/kthread.h> #include <media/videobuf2-core.h> +#include <media/v4l2-mc.h> #include <trace/events/vb2.h> @@ -1887,6 +1888,9 @@ int vb2_core_streamon(struct vb2_queue *q, unsigned int type) * are available. */ if (q->queued_count >= q->min_buffers_needed) { + ret = v4l_vb2q_enable_media_source(q); + if (ret) + return ret; ret = vb2_start_streaming(q); if (ret) { __vb2_queue_cancel(q); diff --git a/drivers/media/v4l2-core/videobuf2-dma-contig.c b/drivers/media/v4l2-core/videobuf2-dma-contig.c index c33127284cfe..5361197f3e57 100644 --- a/drivers/media/v4l2-core/videobuf2-dma-contig.c +++ b/drivers/media/v4l2-core/videobuf2-dma-contig.c @@ -23,13 +23,16 @@ struct vb2_dc_conf { struct device *dev; + struct dma_attrs attrs; }; struct vb2_dc_buf { struct device *dev; void *vaddr; unsigned long size; + void *cookie; dma_addr_t dma_addr; + struct dma_attrs attrs; enum dma_data_direction dma_dir; struct sg_table *dma_sgt; struct frame_vector *vec; @@ -131,7 +134,8 @@ static void vb2_dc_put(void *buf_priv) sg_free_table(buf->sgt_base); kfree(buf->sgt_base); } - dma_free_coherent(buf->dev, buf->size, buf->vaddr, buf->dma_addr); + dma_free_attrs(buf->dev, buf->size, buf->cookie, buf->dma_addr, + &buf->attrs); put_device(buf->dev); kfree(buf); } @@ -147,14 +151,18 @@ static void *vb2_dc_alloc(void *alloc_ctx, unsigned long size, if (!buf) return ERR_PTR(-ENOMEM); - buf->vaddr = dma_alloc_coherent(dev, size, &buf->dma_addr, - GFP_KERNEL | gfp_flags); - if (!buf->vaddr) { + buf->attrs = conf->attrs; + buf->cookie = dma_alloc_attrs(dev, size, &buf->dma_addr, + GFP_KERNEL | gfp_flags, &buf->attrs); + if (!buf->cookie) { dev_err(dev, "dma_alloc_coherent of size %ld failed\n", size); kfree(buf); return ERR_PTR(-ENOMEM); } + if (!dma_get_attr(DMA_ATTR_NO_KERNEL_MAPPING, &buf->attrs)) + buf->vaddr = buf->cookie; + /* Prevent the device from being released while the buffer is used */ buf->dev = get_device(dev); buf->size = size; @@ -185,8 +193,8 @@ static int vb2_dc_mmap(void *buf_priv, struct vm_area_struct *vma) */ vma->vm_pgoff = 0; - ret = dma_mmap_coherent(buf->dev, vma, buf->vaddr, - buf->dma_addr, buf->size); + ret = dma_mmap_attrs(buf->dev, vma, buf->cookie, + buf->dma_addr, buf->size, &buf->attrs); if (ret) { pr_err("Remapping memory failed, error: %d\n", ret); @@ -329,7 +337,7 @@ static void *vb2_dc_dmabuf_ops_kmap(struct dma_buf *dbuf, unsigned long pgnum) { struct vb2_dc_buf *buf = dbuf->priv; - return buf->vaddr + pgnum * PAGE_SIZE; + return buf->vaddr ? buf->vaddr + pgnum * PAGE_SIZE : NULL; } static void *vb2_dc_dmabuf_ops_vmap(struct dma_buf *dbuf) @@ -368,8 +376,8 @@ static struct sg_table *vb2_dc_get_base_sgt(struct vb2_dc_buf *buf) return NULL; } - ret = dma_get_sgtable(buf->dev, sgt, buf->vaddr, buf->dma_addr, - buf->size); + ret = dma_get_sgtable_attrs(buf->dev, sgt, buf->cookie, buf->dma_addr, + buf->size, &buf->attrs); if (ret < 0) { dev_err(buf->dev, "failed to get scatterlist from DMA API\n"); kfree(sgt); @@ -721,7 +729,8 @@ const struct vb2_mem_ops vb2_dma_contig_memops = { }; EXPORT_SYMBOL_GPL(vb2_dma_contig_memops); -void *vb2_dma_contig_init_ctx(struct device *dev) +void *vb2_dma_contig_init_ctx_attrs(struct device *dev, + struct dma_attrs *attrs) { struct vb2_dc_conf *conf; @@ -730,10 +739,12 @@ void *vb2_dma_contig_init_ctx(struct device *dev) return ERR_PTR(-ENOMEM); conf->dev = dev; + if (attrs) + conf->attrs = *attrs; return conf; } -EXPORT_SYMBOL_GPL(vb2_dma_contig_init_ctx); +EXPORT_SYMBOL_GPL(vb2_dma_contig_init_ctx_attrs); void vb2_dma_contig_cleanup_ctx(void *alloc_ctx) { |