diff options
Diffstat (limited to 'drivers/media')
214 files changed, 9338 insertions, 5722 deletions
diff --git a/drivers/media/dvb-core/dvb-usb-ids.h b/drivers/media/dvb-core/dvb-usb-ids.h index 1bdc0e7e8b79..11d2bea23b02 100644 --- a/drivers/media/dvb-core/dvb-usb-ids.h +++ b/drivers/media/dvb-core/dvb-usb-ids.h @@ -356,11 +356,13 @@ #define USB_PID_ELGATO_EYETV_DTT_2 0x003f #define USB_PID_ELGATO_EYETV_DTT_Dlx 0x0020 #define USB_PID_ELGATO_EYETV_SAT 0x002a +#define USB_PID_ELGATO_EYETV_SAT_V2 0x0025 #define USB_PID_DVB_T_USB_STICK_HIGH_SPEED_COLD 0x5000 #define USB_PID_DVB_T_USB_STICK_HIGH_SPEED_WARM 0x5001 #define USB_PID_FRIIO_WHITE 0x0001 #define USB_PID_TVWAY_PLUS 0x0002 #define USB_PID_SVEON_STV20 0xe39d +#define USB_PID_SVEON_STV20_RTL2832U 0xd39d #define USB_PID_SVEON_STV22 0xe401 #define USB_PID_SVEON_STV22_IT9137 0xe411 #define USB_PID_AZUREWAVE_AZ6027 0x3275 @@ -375,4 +377,5 @@ #define USB_PID_CTVDIGDUAL_V2 0xe410 #define USB_PID_PCTV_2002E 0x025c #define USB_PID_PCTV_2002E_SE 0x025d +#define USB_PID_SVEON_STV27 0xd3af #endif diff --git a/drivers/media/dvb-frontends/Kconfig b/drivers/media/dvb-frontends/Kconfig index 025fc5496bfc..1469d44acb22 100644 --- a/drivers/media/dvb-frontends/Kconfig +++ b/drivers/media/dvb-frontends/Kconfig @@ -446,6 +446,13 @@ config DVB_RTL2832 help Say Y when you want to support this frontend. +config DVB_SI2168 + tristate "Silicon Labs Si2168" + depends on DVB_CORE && I2C && I2C_MUX + default m if !MEDIA_SUBDRV_AUTOSELECT + help + Say Y when you want to support this frontend. + comment "DVB-C (cable) frontends" depends on DVB_CORE diff --git a/drivers/media/dvb-frontends/Makefile b/drivers/media/dvb-frontends/Makefile index 282aba2fe8db..dda0bee36f29 100644 --- a/drivers/media/dvb-frontends/Makefile +++ b/drivers/media/dvb-frontends/Makefile @@ -78,6 +78,7 @@ obj-$(CONFIG_DVB_AF9013) += af9013.o obj-$(CONFIG_DVB_CX24116) += cx24116.o obj-$(CONFIG_DVB_CX24117) += cx24117.o obj-$(CONFIG_DVB_SI21XX) += si21xx.o +obj-$(CONFIG_DVB_SI2168) += si2168.o obj-$(CONFIG_DVB_STV0288) += stv0288.o obj-$(CONFIG_DVB_STB6000) += stb6000.o obj-$(CONFIG_DVB_S921) += s921.o diff --git a/drivers/media/dvb-frontends/si2168.c b/drivers/media/dvb-frontends/si2168.c new file mode 100644 index 000000000000..8637d2ed7623 --- /dev/null +++ b/drivers/media/dvb-frontends/si2168.c @@ -0,0 +1,760 @@ +/* + * Silicon Labs Si2168 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 "si2168_priv.h" + +static const struct dvb_frontend_ops si2168_ops; + +/* execute firmware command */ +static int si2168_cmd_execute(struct si2168 *s, struct si2168_cmd *cmd) +{ + int ret; + unsigned long timeout; + + mutex_lock(&s->i2c_mutex); + + if (cmd->wlen) { + /* write cmd and args for firmware */ + ret = i2c_master_send(s->client, cmd->args, cmd->wlen); + if (ret < 0) { + goto err_mutex_unlock; + } else if (ret != cmd->wlen) { + ret = -EREMOTEIO; + goto err_mutex_unlock; + } + } + + if (cmd->rlen) { + /* wait cmd execution terminate */ + #define TIMEOUT 50 + timeout = jiffies + msecs_to_jiffies(TIMEOUT); + while (!time_after(jiffies, timeout)) { + ret = i2c_master_recv(s->client, cmd->args, cmd->rlen); + if (ret < 0) { + goto err_mutex_unlock; + } else if (ret != cmd->rlen) { + ret = -EREMOTEIO; + goto err_mutex_unlock; + } + + /* firmware ready? */ + if ((cmd->args[0] >> 7) & 0x01) + break; + } + + dev_dbg(&s->client->dev, "%s: cmd execution took %d ms\n", + __func__, + jiffies_to_msecs(jiffies) - + (jiffies_to_msecs(timeout) - TIMEOUT)); + + if (!(cmd->args[0] >> 7) & 0x01) { + ret = -ETIMEDOUT; + goto err_mutex_unlock; + } + } + + ret = 0; + +err_mutex_unlock: + mutex_unlock(&s->i2c_mutex); + if (ret) + goto err; + + return 0; +err: + dev_dbg(&s->client->dev, "%s: failed=%d\n", __func__, ret); + return ret; +} + +static int si2168_read_status(struct dvb_frontend *fe, fe_status_t *status) +{ + struct si2168 *s = fe->demodulator_priv; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + int ret; + struct si2168_cmd cmd; + + *status = 0; + + if (!s->active) { + ret = -EAGAIN; + goto err; + } + + switch (c->delivery_system) { + case SYS_DVBT: + cmd.args[0] = 0xa0; + cmd.args[1] = 0x01; + cmd.wlen = 2; + cmd.rlen = 13; + break; + case SYS_DVBC_ANNEX_A: + cmd.args[0] = 0x90; + cmd.args[1] = 0x01; + cmd.wlen = 2; + cmd.rlen = 9; + break; + case SYS_DVBT2: + cmd.args[0] = 0x50; + cmd.args[1] = 0x01; + cmd.wlen = 2; + cmd.rlen = 14; + break; + default: + ret = -EINVAL; + goto err; + } + + ret = si2168_cmd_execute(s, &cmd); + if (ret) + goto err; + + /* + * Possible values seen, in order from strong signal to weak: + * 16 0001 0110 full lock + * 1e 0001 1110 partial lock + * 1a 0001 1010 partial lock + * 18 0001 1000 no lock + * + * [b3:b1] lock bits + * [b4] statistics ready? Set in a few secs after lock is gained. + */ + + switch ((cmd.args[2] >> 1) & 0x03) { + case 0x01: + *status = FE_HAS_SIGNAL | FE_HAS_CARRIER; + break; + case 0x03: + *status = FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_VITERBI | + FE_HAS_SYNC | FE_HAS_LOCK; + break; + } + + s->fe_status = *status; + + dev_dbg(&s->client->dev, "%s: status=%02x args=%*ph\n", + __func__, *status, cmd.rlen, cmd.args); + + return 0; +err: + dev_dbg(&s->client->dev, "%s: failed=%d\n", __func__, ret); + return ret; +} + +static int si2168_set_frontend(struct dvb_frontend *fe) +{ + struct si2168 *s = fe->demodulator_priv; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + int ret; + struct si2168_cmd cmd; + u8 bandwidth, delivery_system; + + dev_dbg(&s->client->dev, + "%s: delivery_system=%u modulation=%u frequency=%u bandwidth_hz=%u symbol_rate=%u inversion=%u\n", + __func__, c->delivery_system, c->modulation, + c->frequency, c->bandwidth_hz, c->symbol_rate, + c->inversion); + + if (!s->active) { + ret = -EAGAIN; + goto err; + } + + switch (c->delivery_system) { + case SYS_DVBT: + delivery_system = 0x20; + break; + case SYS_DVBC_ANNEX_A: + delivery_system = 0x30; + break; + case SYS_DVBT2: + delivery_system = 0x70; + break; + default: + ret = -EINVAL; + goto err; + } + + if (c->bandwidth_hz <= 5000000) + bandwidth = 0x05; + else if (c->bandwidth_hz <= 6000000) + bandwidth = 0x06; + else if (c->bandwidth_hz <= 7000000) + bandwidth = 0x07; + else if (c->bandwidth_hz <= 8000000) + bandwidth = 0x08; + else if (c->bandwidth_hz <= 9000000) + bandwidth = 0x09; + else if (c->bandwidth_hz <= 10000000) + bandwidth = 0x0a; + else + bandwidth = 0x0f; + + /* program tuner */ + if (fe->ops.tuner_ops.set_params) { + ret = fe->ops.tuner_ops.set_params(fe); + if (ret) + goto err; + } + + memcpy(cmd.args, "\x88\x02\x02\x02\x02", 5); + cmd.wlen = 5; + cmd.rlen = 5; + ret = si2168_cmd_execute(s, &cmd); + if (ret) + goto err; + + /* that has no big effect */ + if (c->delivery_system == SYS_DVBT) + memcpy(cmd.args, "\x89\x21\x06\x11\xff\x98", 6); + else if (c->delivery_system == SYS_DVBC_ANNEX_A) + memcpy(cmd.args, "\x89\x21\x06\x11\x89\xf0", 6); + else if (c->delivery_system == SYS_DVBT2) + memcpy(cmd.args, "\x89\x21\x06\x11\x89\x20", 6); + cmd.wlen = 6; + cmd.rlen = 3; + ret = si2168_cmd_execute(s, &cmd); + if (ret) + goto err; + + memcpy(cmd.args, "\x51\x03", 2); + cmd.wlen = 2; + cmd.rlen = 12; + ret = si2168_cmd_execute(s, &cmd); + if (ret) + goto err; + + memcpy(cmd.args, "\x12\x08\x04", 3); + cmd.wlen = 3; + cmd.rlen = 3; + ret = si2168_cmd_execute(s, &cmd); + if (ret) + goto err; + + memcpy(cmd.args, "\x14\x00\x01\x04\x00\x00", 6); + cmd.wlen = 6; + cmd.rlen = 1; + ret = si2168_cmd_execute(s, &cmd); + if (ret) + goto err; + + memcpy(cmd.args, "\x14\x00\x03\x10\x17\x00", 6); + cmd.wlen = 6; + cmd.rlen = 1; + ret = si2168_cmd_execute(s, &cmd); + if (ret) + goto err; + + memcpy(cmd.args, "\x14\x00\x02\x10\x15\x00", 6); + cmd.wlen = 6; + cmd.rlen = 1; + ret = si2168_cmd_execute(s, &cmd); + if (ret) + goto err; + + memcpy(cmd.args, "\x14\x00\x0c\x10\x12\x00", 6); + cmd.wlen = 6; + cmd.rlen = 1; + ret = si2168_cmd_execute(s, &cmd); + if (ret) + goto err; + + memcpy(cmd.args, "\x14\x00\x06\x10\x24\x00", 6); + cmd.wlen = 6; + cmd.rlen = 1; + ret = si2168_cmd_execute(s, &cmd); + if (ret) + goto err; + + memcpy(cmd.args, "\x14\x00\x0b\x10\x88\x13", 6); + cmd.wlen = 6; + cmd.rlen = 1; + ret = si2168_cmd_execute(s, &cmd); + if (ret) + goto err; + + memcpy(cmd.args, "\x14\x00\x07\x10\x00\x24", 6); + cmd.wlen = 6; + cmd.rlen = 1; + ret = si2168_cmd_execute(s, &cmd); + if (ret) + goto err; + + memcpy(cmd.args, "\x14\x00\x0a\x10\x00\x00", 6); + cmd.args[4] = delivery_system | bandwidth; + cmd.wlen = 6; + cmd.rlen = 1; + ret = si2168_cmd_execute(s, &cmd); + if (ret) + goto err; + + memcpy(cmd.args, "\x14\x00\x04\x10\x15\x00", 6); + cmd.wlen = 6; + cmd.rlen = 1; + ret = si2168_cmd_execute(s, &cmd); + if (ret) + goto err; + + memcpy(cmd.args, "\x14\x00\x05\x10\xa1\x00", 6); + cmd.wlen = 6; + cmd.rlen = 1; + ret = si2168_cmd_execute(s, &cmd); + if (ret) + goto err; + + memcpy(cmd.args, "\x14\x00\x0f\x10\x10\x00", 6); + cmd.wlen = 6; + cmd.rlen = 1; + ret = si2168_cmd_execute(s, &cmd); + if (ret) + goto err; + + memcpy(cmd.args, "\x14\x00\x0d\x10\xd0\x02", 6); + cmd.wlen = 6; + cmd.rlen = 1; + ret = si2168_cmd_execute(s, &cmd); + if (ret) + goto err; + + memcpy(cmd.args, "\x14\x00\x01\x10\x00\x00", 6); + cmd.wlen = 6; + cmd.rlen = 1; + ret = si2168_cmd_execute(s, &cmd); + if (ret) + goto err; + + memcpy(cmd.args, "\x14\x00\x09\x10\xe3\x18", 6); + cmd.wlen = 6; + cmd.rlen = 1; + ret = si2168_cmd_execute(s, &cmd); + if (ret) + goto err; + + memcpy(cmd.args, "\x14\x00\x08\x10\xd7\x15", 6); + cmd.wlen = 6; + cmd.rlen = 1; + ret = si2168_cmd_execute(s, &cmd); + if (ret) + goto err; + + memcpy(cmd.args, "\x14\x00\x04\x03\x00\x00", 6); + cmd.wlen = 6; + cmd.rlen = 1; + ret = si2168_cmd_execute(s, &cmd); + if (ret) + goto err; + + memcpy(cmd.args, "\x14\x00\x03\x03\x00\x00", 6); + cmd.wlen = 6; + cmd.rlen = 1; + ret = si2168_cmd_execute(s, &cmd); + if (ret) + goto err; + + memcpy(cmd.args, "\x14\x00\x08\x03\x00\x00", 6); + cmd.wlen = 6; + cmd.rlen = 1; + ret = si2168_cmd_execute(s, &cmd); + if (ret) + goto err; + + memcpy(cmd.args, "\x14\x00\x07\x03\x01\x02", 6); + cmd.wlen = 6; + cmd.rlen = 1; + ret = si2168_cmd_execute(s, &cmd); + if (ret) + goto err; + + memcpy(cmd.args, "\x14\x00\x06\x03\x00\x00", 6); + cmd.wlen = 6; + cmd.rlen = 1; + ret = si2168_cmd_execute(s, &cmd); + if (ret) + goto err; + + memcpy(cmd.args, "\x14\x00\x05\x03\x00\x00", 6); + cmd.wlen = 6; + cmd.rlen = 1; + ret = si2168_cmd_execute(s, &cmd); + if (ret) + goto err; + + memcpy(cmd.args, "\x14\x00\x01\x03\x0c\x40", 6); + cmd.wlen = 6; + cmd.rlen = 1; + ret = si2168_cmd_execute(s, &cmd); + if (ret) + goto err; + + memcpy(cmd.args, "\x14\x00\x01\x10\x16\x00", 6); + cmd.wlen = 6; + cmd.rlen = 1; + ret = si2168_cmd_execute(s, &cmd); + if (ret) + goto err; + + memcpy(cmd.args, "\x14\x00\x01\x12\x00\x00", 6); + cmd.wlen = 6; + cmd.rlen = 1; + ret = si2168_cmd_execute(s, &cmd); + if (ret) + goto err; + + cmd.args[0] = 0x85; + cmd.wlen = 1; + cmd.rlen = 1; + ret = si2168_cmd_execute(s, &cmd); + if (ret) + goto err; + + s->delivery_system = c->delivery_system; + + return 0; +err: + dev_dbg(&s->client->dev, "%s: failed=%d\n", __func__, ret); + return ret; +} + +static int si2168_init(struct dvb_frontend *fe) +{ + struct si2168 *s = fe->demodulator_priv; + int ret, len, remaining; + const struct firmware *fw = NULL; + u8 *fw_file = SI2168_FIRMWARE; + const unsigned int i2c_wr_max = 8; + struct si2168_cmd cmd; + + dev_dbg(&s->client->dev, "%s:\n", __func__); + + cmd.args[0] = 0x13; + cmd.wlen = 1; + cmd.rlen = 0; + ret = si2168_cmd_execute(s, &cmd); + if (ret) + goto err; + + cmd.args[0] = 0xc0; + cmd.args[1] = 0x12; + cmd.args[2] = 0x00; + cmd.args[3] = 0x0c; + cmd.args[4] = 0x00; + cmd.args[5] = 0x0d; + cmd.args[6] = 0x16; + cmd.args[7] = 0x00; + cmd.args[8] = 0x00; + cmd.args[9] = 0x00; + cmd.args[10] = 0x00; + cmd.args[11] = 0x00; + cmd.args[12] = 0x00; + cmd.wlen = 13; + cmd.rlen = 0; + ret = si2168_cmd_execute(s, &cmd); + if (ret) + goto err; + + cmd.args[0] = 0xc0; + cmd.args[1] = 0x06; + cmd.args[2] = 0x01; + cmd.args[3] = 0x0f; + cmd.args[4] = 0x00; + cmd.args[5] = 0x20; + cmd.args[6] = 0x20; + cmd.args[7] = 0x01; + cmd.wlen = 8; + cmd.rlen = 1; + ret = si2168_cmd_execute(s, &cmd); + if (ret) + goto err; + + cmd.args[0] = 0x02; + cmd.wlen = 1; + cmd.rlen = 13; + ret = si2168_cmd_execute(s, &cmd); + if (ret) + goto err; + + cmd.args[0] = 0x05; + cmd.args[1] = 0x00; + cmd.args[2] = 0xaa; + cmd.args[3] = 0x4d; + cmd.args[4] = 0x56; + cmd.args[5] = 0x40; + cmd.args[6] = 0x00; + cmd.args[7] = 0x00; + cmd.wlen = 8; + cmd.rlen = 1; + ret = si2168_cmd_execute(s, &cmd); + if (ret) + goto err; + + /* cold state - try to download firmware */ + dev_info(&s->client->dev, "%s: found a '%s' in cold state\n", + KBUILD_MODNAME, si2168_ops.info.name); + + /* request the firmware, this will block and timeout */ + ret = request_firmware(&fw, fw_file, &s->client->dev); + if (ret) { + dev_err(&s->client->dev, "%s: firmare file '%s' not found\n", + KBUILD_MODNAME, fw_file); + goto err; + } + + dev_info(&s->client->dev, "%s: downloading firmware from file '%s'\n", + KBUILD_MODNAME, fw_file); + + for (remaining = fw->size; remaining > 0; remaining -= i2c_wr_max) { + len = remaining; + if (len > i2c_wr_max) + len = i2c_wr_max; + + memcpy(cmd.args, &fw->data[fw->size - remaining], len); + cmd.wlen = len; + cmd.rlen = 1; + ret = si2168_cmd_execute(s, &cmd); + if (ret) { + dev_err(&s->client->dev, + "%s: firmware download failed=%d\n", + KBUILD_MODNAME, ret); + goto err; + } + } + + release_firmware(fw); + fw = NULL; + + cmd.args[0] = 0x01; + cmd.args[1] = 0x01; + cmd.wlen = 2; + cmd.rlen = 1; + ret = si2168_cmd_execute(s, &cmd); + if (ret) + goto err; + + dev_info(&s->client->dev, "%s: found a '%s' in warm state\n", + KBUILD_MODNAME, si2168_ops.info.name); + + s->active = true; + + return 0; +err: + if (fw) + release_firmware(fw); + + dev_dbg(&s->client->dev, "%s: failed=%d\n", __func__, ret); + return ret; +} + +static int si2168_sleep(struct dvb_frontend *fe) +{ + struct si2168 *s = fe->demodulator_priv; + + dev_dbg(&s->client->dev, "%s:\n", __func__); + + s->active = false; + + return 0; +} + +static int si2168_get_tune_settings(struct dvb_frontend *fe, + struct dvb_frontend_tune_settings *s) +{ + s->min_delay_ms = 900; + + return 0; +} + +/* + * I2C gate logic + * We must use unlocked i2c_transfer() here because I2C lock is already taken + * by tuner driver. + */ +static int si2168_select(struct i2c_adapter *adap, void *mux_priv, u32 chan) +{ + struct si2168 *s = mux_priv; + int ret; + struct i2c_msg gate_open_msg = { + .addr = s->client->addr, + .flags = 0, + .len = 3, + .buf = "\xc0\x0d\x01", + }; + + mutex_lock(&s->i2c_mutex); + + /* open tuner I2C gate */ + ret = __i2c_transfer(s->client->adapter, &gate_open_msg, 1); + if (ret != 1) { + dev_warn(&s->client->dev, "%s: i2c write failed=%d\n", + KBUILD_MODNAME, ret); + if (ret >= 0) + ret = -EREMOTEIO; + } else { + ret = 0; + } + + return ret; +} + +static int si2168_deselect(struct i2c_adapter *adap, void *mux_priv, u32 chan) +{ + struct si2168 *s = mux_priv; + int ret; + struct i2c_msg gate_close_msg = { + .addr = s->client->addr, + .flags = 0, + .len = 3, + .buf = "\xc0\x0d\x00", + }; + + /* close tuner I2C gate */ + ret = __i2c_transfer(s->client->adapter, &gate_close_msg, 1); + if (ret != 1) { + dev_warn(&s->client->dev, "%s: i2c write failed=%d\n", + KBUILD_MODNAME, ret); + if (ret >= 0) + ret = -EREMOTEIO; + } else { + ret = 0; + } + + mutex_unlock(&s->i2c_mutex); + + return ret; +} + +static const struct dvb_frontend_ops si2168_ops = { + .delsys = {SYS_DVBT, SYS_DVBT2, SYS_DVBC_ANNEX_A}, + .info = { + .name = "Silicon Labs Si2168", + .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 = si2168_get_tune_settings, + + .init = si2168_init, + .sleep = si2168_sleep, + + .set_frontend = si2168_set_frontend, + + .read_status = si2168_read_status, +}; + +static int si2168_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct si2168_config *config = client->dev.platform_data; + struct si2168 *s; + int ret; + struct si2168_cmd cmd; + + dev_dbg(&client->dev, "%s:\n", __func__); + + s = kzalloc(sizeof(struct si2168), GFP_KERNEL); + if (!s) { + ret = -ENOMEM; + dev_err(&client->dev, "%s: kzalloc() failed\n", KBUILD_MODNAME); + goto err; + } + + s->client = client; + mutex_init(&s->i2c_mutex); + + /* check if the demod is there */ + cmd.wlen = 0; + cmd.rlen = 1; + ret = si2168_cmd_execute(s, &cmd); + if (ret) + goto err; + + /* create mux i2c adapter for tuner */ + s->adapter = i2c_add_mux_adapter(client->adapter, &client->dev, s, + 0, 0, 0, si2168_select, si2168_deselect); + if (s->adapter == NULL) + goto err; + + /* create dvb_frontend */ + memcpy(&s->fe.ops, &si2168_ops, sizeof(struct dvb_frontend_ops)); + s->fe.demodulator_priv = s; + + *config->i2c_adapter = s->adapter; + *config->fe = &s->fe; + + i2c_set_clientdata(client, s); + + dev_info(&s->client->dev, + "%s: Silicon Labs Si2168 successfully attached\n", + KBUILD_MODNAME); + return 0; +err: + kfree(s); + dev_dbg(&client->dev, "%s: failed=%d\n", __func__, ret); + return ret; +} + +static int si2168_remove(struct i2c_client *client) +{ + struct si2168 *s = i2c_get_clientdata(client); + + dev_dbg(&client->dev, "%s:\n", __func__); + + i2c_del_mux_adapter(s->adapter); + + s->fe.ops.release = NULL; + s->fe.demodulator_priv = NULL; + + kfree(s); + + return 0; +} + +static const struct i2c_device_id si2168_id[] = { + {"si2168", 0}, + {} +}; +MODULE_DEVICE_TABLE(i2c, si2168_id); + +static struct i2c_driver si2168_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "si2168", + }, + .probe = si2168_probe, + .remove = si2168_remove, + .id_table = si2168_id, +}; + +module_i2c_driver(si2168_driver); + +MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>"); +MODULE_DESCRIPTION("Silicon Labs Si2168 DVB-T/T2/C demodulator driver"); +MODULE_LICENSE("GPL"); +MODULE_FIRMWARE(SI2168_FIRMWARE); diff --git a/drivers/media/dvb-frontends/si2168.h b/drivers/media/dvb-frontends/si2168.h new file mode 100644 index 000000000000..3c5b5ab01796 --- /dev/null +++ b/drivers/media/dvb-frontends/si2168.h @@ -0,0 +1,39 @@ +/* + * Silicon Labs Si2168 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 SI2168_H +#define SI2168_H + +#include <linux/dvb/frontend.h> +/* + * I2C address + * 0x64 + */ +struct si2168_config { + /* + * frontend + * returned by driver + */ + struct dvb_frontend **fe; + + /* + * tuner I2C adapter + * returned by driver + */ + struct i2c_adapter **i2c_adapter; +}; + +#endif diff --git a/drivers/media/dvb-frontends/si2168_priv.h b/drivers/media/dvb-frontends/si2168_priv.h new file mode 100644 index 000000000000..2a343e896f40 --- /dev/null +++ b/drivers/media/dvb-frontends/si2168_priv.h @@ -0,0 +1,46 @@ +/* + * Silicon Labs Si2168 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 SI2168_PRIV_H +#define SI2168_PRIV_H + +#include "si2168.h" +#include "dvb_frontend.h" +#include <linux/firmware.h> +#include <linux/i2c-mux.h> + +#define SI2168_FIRMWARE "dvb-demod-si2168-01.fw" + +/* state struct */ +struct si2168 { + struct i2c_client *client; + struct i2c_adapter *adapter; + struct mutex i2c_mutex; + struct dvb_frontend fe; + fe_delivery_system_t delivery_system; + fe_status_t fe_status; + bool active; +}; + +/* firmare command struct */ +#define SI2157_ARGLEN 30 +struct si2168_cmd { + u8 args[SI2157_ARGLEN]; + unsigned wlen; + unsigned rlen; +}; + +#endif diff --git a/drivers/media/i2c/ad9389b.c b/drivers/media/i2c/ad9389b.c index 1b7ecfd88673..fada17566205 100644 --- a/drivers/media/i2c/ad9389b.c +++ b/drivers/media/i2c/ad9389b.c @@ -571,35 +571,6 @@ static const struct v4l2_subdev_core_ops ad9389b_core_ops = { .interrupt_service_routine = ad9389b_isr, }; -/* ------------------------------ PAD OPS ------------------------------ */ - -static int ad9389b_get_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid) -{ - struct ad9389b_state *state = get_ad9389b_state(sd); - - if (edid->pad != 0) - return -EINVAL; - if (edid->blocks == 0 || edid->blocks > 256) - return -EINVAL; - if (!edid->edid) - return -EINVAL; - if (!state->edid.segments) { - v4l2_dbg(1, debug, sd, "EDID segment 0 not found\n"); - return -ENODATA; - } - if (edid->start_block >= state->edid.segments * 2) - return -E2BIG; - if (edid->blocks + edid->start_block >= state->edid.segments * 2) - edid->blocks = state->edid.segments * 2 - edid->start_block; - memcpy(edid->edid, &state->edid.data[edid->start_block * 128], - 128 * edid->blocks); - return 0; -} - -static const struct v4l2_subdev_pad_ops ad9389b_pad_ops = { - .get_edid = ad9389b_get_edid, -}; - /* ------------------------------ VIDEO OPS ------------------------------ */ /* Enable/disable ad9389b output */ @@ -678,6 +649,9 @@ static int ad9389b_g_dv_timings(struct v4l2_subdev *sd, static int ad9389b_enum_dv_timings(struct v4l2_subdev *sd, struct v4l2_enum_dv_timings *timings) { + if (timings->pad != 0) + return -EINVAL; + return v4l2_enum_dv_timings_cap(timings, &ad9389b_timings_cap, NULL, NULL); } @@ -685,6 +659,9 @@ static int ad9389b_enum_dv_timings(struct v4l2_subdev *sd, static int ad9389b_dv_timings_cap(struct v4l2_subdev *sd, struct v4l2_dv_timings_cap *cap) { + if (cap->pad != 0) + return -EINVAL; + *cap = ad9389b_timings_cap; return 0; } @@ -693,10 +670,39 @@ static const struct v4l2_subdev_video_ops ad9389b_video_ops = { .s_stream = ad9389b_s_stream, .s_dv_timings = ad9389b_s_dv_timings, .g_dv_timings = ad9389b_g_dv_timings, +}; + +/* ------------------------------ PAD OPS ------------------------------ */ + +static int ad9389b_get_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid) +{ + struct ad9389b_state *state = get_ad9389b_state(sd); + + if (edid->pad != 0) + return -EINVAL; + if (edid->blocks == 0 || edid->blocks > 256) + return -EINVAL; + if (!state->edid.segments) { + v4l2_dbg(1, debug, sd, "EDID segment 0 not found\n"); + return -ENODATA; + } + if (edid->start_block >= state->edid.segments * 2) + return -E2BIG; + if (edid->blocks + edid->start_block >= state->edid.segments * 2) + edid->blocks = state->edid.segments * 2 - edid->start_block; + memcpy(edid->edid, &state->edid.data[edid->start_block * 128], + 128 * edid->blocks); + return 0; +} + +static const struct v4l2_subdev_pad_ops ad9389b_pad_ops = { + .get_edid = ad9389b_get_edid, .enum_dv_timings = ad9389b_enum_dv_timings, .dv_timings_cap = ad9389b_dv_timings_cap, }; +/* ------------------------------ AUDIO OPS ------------------------------ */ + static int ad9389b_s_audio_stream(struct v4l2_subdev *sd, int enable) { v4l2_dbg(1, debug, sd, "%s: %sable\n", __func__, (enable ? "en" : "dis")); diff --git a/drivers/media/i2c/adv7180.c b/drivers/media/i2c/adv7180.c index 5e638b159452..ac1cdbe251a3 100644 --- a/drivers/media/i2c/adv7180.c +++ b/drivers/media/i2c/adv7180.c @@ -461,6 +461,7 @@ static int adv7180_g_mbus_config(struct v4l2_subdev *sd, } static const struct v4l2_subdev_video_ops adv7180_video_ops = { + .s_std = adv7180_s_std, .querystd = adv7180_querystd, .g_input_status = adv7180_g_input_status, .s_routing = adv7180_s_routing, @@ -472,7 +473,6 @@ static const struct v4l2_subdev_video_ops adv7180_video_ops = { }; static const struct v4l2_subdev_core_ops adv7180_core_ops = { - .s_std = adv7180_s_std, .s_power = adv7180_s_power, }; diff --git a/drivers/media/i2c/adv7183.c b/drivers/media/i2c/adv7183.c index d45e0e3a781d..df461b07b2f7 100644 --- a/drivers/media/i2c/adv7183.c +++ b/drivers/media/i2c/adv7183.c @@ -501,8 +501,6 @@ static const struct v4l2_ctrl_ops adv7183_ctrl_ops = { static const struct v4l2_subdev_core_ops adv7183_core_ops = { .log_status = adv7183_log_status, - .g_std = adv7183_g_std, - .s_std = adv7183_s_std, .reset = adv7183_reset, #ifdef CONFIG_VIDEO_ADV_DEBUG .g_register = adv7183_g_register, @@ -511,6 +509,8 @@ static const struct v4l2_subdev_core_ops adv7183_core_ops = { }; static const struct v4l2_subdev_video_ops adv7183_video_ops = { + .g_std = adv7183_g_std, + .s_std = adv7183_s_std, .s_routing = adv7183_s_routing, .querystd = adv7183_querystd, .g_input_status = adv7183_g_input_status, diff --git a/drivers/media/i2c/adv7511.c b/drivers/media/i2c/adv7511.c index 942ca4b99297..f98acf4aafd4 100644 --- a/drivers/media/i2c/adv7511.c +++ b/drivers/media/i2c/adv7511.c @@ -597,34 +597,6 @@ static int adv7511_isr(struct v4l2_subdev *sd, u32 status, bool *handled) return 0; } -static int adv7511_get_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid) -{ - struct adv7511_state *state = get_adv7511_state(sd); - - if (edid->pad != 0) - return -EINVAL; - if ((edid->blocks == 0) || (edid->blocks > 256)) - return -EINVAL; - if (!edid->edid) - return -EINVAL; - if (!state->edid.segments) { - v4l2_dbg(1, debug, sd, "EDID segment 0 not found\n"); - return -ENODATA; - } - if (edid->start_block >= state->edid.segments * 2) - return -E2BIG; - if ((edid->blocks + edid->start_block) >= state->edid.segments * 2) - edid->blocks = state->edid.segments * 2 - edid->start_block; - - memcpy(edid->edid, &state->edid.data[edid->start_block * 128], - 128 * edid->blocks); - return 0; -} - -static const struct v4l2_subdev_pad_ops adv7511_pad_ops = { - .get_edid = adv7511_get_edid, -}; - static const struct v4l2_subdev_core_ops adv7511_core_ops = { .log_status = adv7511_log_status, #ifdef CONFIG_VIDEO_ADV_DEBUG @@ -700,12 +672,18 @@ static int adv7511_g_dv_timings(struct v4l2_subdev *sd, static int adv7511_enum_dv_timings(struct v4l2_subdev *sd, struct v4l2_enum_dv_timings *timings) { + if (timings->pad != 0) + return -EINVAL; + return v4l2_enum_dv_timings_cap(timings, &adv7511_timings_cap, NULL, NULL); } static int adv7511_dv_timings_cap(struct v4l2_subdev *sd, struct v4l2_dv_timings_cap *cap) { + if (cap->pad != 0) + return -EINVAL; + *cap = adv7511_timings_cap; return 0; } @@ -714,8 +692,6 @@ static const struct v4l2_subdev_video_ops adv7511_video_ops = { .s_stream = adv7511_s_stream, .s_dv_timings = adv7511_s_dv_timings, .g_dv_timings = adv7511_g_dv_timings, - .enum_dv_timings = adv7511_enum_dv_timings, - .dv_timings_cap = adv7511_dv_timings_cap, }; /* ------------------------------ AUDIO OPS ------------------------------ */ @@ -797,6 +773,36 @@ static const struct v4l2_subdev_audio_ops adv7511_audio_ops = { .s_routing = adv7511_s_routing, }; +/* ---------------------------- PAD OPS ------------------------------------- */ + +static int adv7511_get_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid) +{ + struct adv7511_state *state = get_adv7511_state(sd); + + if (edid->pad != 0) + return -EINVAL; + if ((edid->blocks == 0) || (edid->blocks > 256)) + return -EINVAL; + if (!state->edid.segments) { + v4l2_dbg(1, debug, sd, "EDID segment 0 not found\n"); + return -ENODATA; + } + if (edid->start_block >= state->edid.segments * 2) + return -E2BIG; + if ((edid->blocks + edid->start_block) >= state->edid.segments * 2) + edid->blocks = state->edid.segments * 2 - edid->start_block; + + memcpy(edid->edid, &state->edid.data[edid->start_block * 128], + 128 * edid->blocks); + return 0; +} + +static const struct v4l2_subdev_pad_ops adv7511_pad_ops = { + .get_edid = adv7511_get_edid, + .enum_dv_timings = adv7511_enum_dv_timings, + .dv_timings_cap = adv7511_dv_timings_cap, +}; + /* --------------------- SUBDEV OPS --------------------------------------- */ static const struct v4l2_subdev_ops adv7511_ops = { diff --git a/drivers/media/i2c/adv7604.c b/drivers/media/i2c/adv7604.c index 98cc5407f1b1..1778d320272e 100644 --- a/drivers/media/i2c/adv7604.c +++ b/drivers/media/i2c/adv7604.c @@ -27,19 +27,21 @@ * REF_03 - Analog devices, ADV7604, Hardware Manual, Rev. F, August 2010 */ - +#include <linux/delay.h> +#include <linux/gpio/consumer.h> +#include <linux/i2c.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/slab.h> -#include <linux/i2c.h> -#include <linux/delay.h> +#include <linux/v4l2-dv-timings.h> #include <linux/videodev2.h> #include <linux/workqueue.h> -#include <linux/v4l2-dv-timings.h> -#include <media/v4l2-device.h> + +#include <media/adv7604.h> #include <media/v4l2-ctrls.h> +#include <media/v4l2-device.h> #include <media/v4l2-dv-timings.h> -#include <media/adv7604.h> +#include <media/v4l2-of.h> static int debug; module_param(debug, int, 0644); @@ -53,6 +55,76 @@ MODULE_LICENSE("GPL"); /* ADV7604 system clock frequency */ #define ADV7604_fsc (28636360) +#define ADV7604_RGB_OUT (1 << 1) + +#define ADV7604_OP_FORMAT_SEL_8BIT (0 << 0) +#define ADV7604_OP_FORMAT_SEL_10BIT (1 << 0) +#define ADV7604_OP_FORMAT_SEL_12BIT (2 << 0) + +#define ADV7604_OP_MODE_SEL_SDR_422 (0 << 5) +#define ADV7604_OP_MODE_SEL_DDR_422 (1 << 5) +#define ADV7604_OP_MODE_SEL_SDR_444 (2 << 5) +#define ADV7604_OP_MODE_SEL_DDR_444 (3 << 5) +#define ADV7604_OP_MODE_SEL_SDR_422_2X (4 << 5) +#define ADV7604_OP_MODE_SEL_ADI_CM (5 << 5) + +#define ADV7604_OP_CH_SEL_GBR (0 << 5) +#define ADV7604_OP_CH_SEL_GRB (1 << 5) +#define ADV7604_OP_CH_SEL_BGR (2 << 5) +#define ADV7604_OP_CH_SEL_RGB (3 << 5) +#define ADV7604_OP_CH_SEL_BRG (4 << 5) +#define ADV7604_OP_CH_SEL_RBG (5 << 5) + +#define ADV7604_OP_SWAP_CB_CR (1 << 0) + +enum adv7604_type { + ADV7604, + ADV7611, +}; + +struct adv7604_reg_seq { + unsigned int reg; + u8 val; +}; + +struct adv7604_format_info { + enum v4l2_mbus_pixelcode code; + u8 op_ch_sel; + bool rgb_out; + bool swap_cb_cr; + u8 op_format_sel; +}; + +struct adv7604_chip_info { + enum adv7604_type type; + + bool has_afe; + unsigned int max_port; + unsigned int num_dv_ports; + + unsigned int edid_enable_reg; + unsigned int edid_status_reg; + unsigned int lcf_reg; + + unsigned int cable_det_mask; + unsigned int tdms_lock_mask; + unsigned int fmt_change_digital_mask; + + const struct adv7604_format_info *formats; + unsigned int nformats; + + void (*set_termination)(struct v4l2_subdev *sd, bool enable); + void (*setup_irqs)(struct v4l2_subdev *sd); + unsigned int (*read_hdmi_pixelclock)(struct v4l2_subdev *sd); + unsigned int (*read_cable_det)(struct v4l2_subdev *sd); + + /* 0 = AFE, 1 = HDMI */ + const struct adv7604_reg_seq *recommended_settings[2]; + unsigned int num_recommended_settings[2]; + + unsigned long page_mask; +}; + /* ********************************************************************** * @@ -60,13 +132,24 @@ MODULE_LICENSE("GPL"); * ********************************************************************** */ + struct adv7604_state { + const struct adv7604_chip_info *info; struct adv7604_platform_data pdata; + + struct gpio_desc *hpd_gpio[4]; + struct v4l2_subdev sd; - struct media_pad pad; + struct media_pad pads[ADV7604_PAD_MAX]; + unsigned int source_pad; + struct v4l2_ctrl_handler hdl; - enum adv7604_input_port selected_input; + + enum adv7604_pad selected_input; + struct v4l2_dv_timings timings; + const struct adv7604_format_info *format; + struct { u8 edid[256]; u32 present; @@ -80,18 +163,7 @@ struct adv7604_state { bool restart_stdi_once; /* i2c clients */ - struct i2c_client *i2c_avlink; - struct i2c_client *i2c_cec; - struct i2c_client *i2c_infoframe; - struct i2c_client *i2c_esdp; - struct i2c_client *i2c_dpp; - struct i2c_client *i2c_afe; - struct i2c_client *i2c_repeater; - struct i2c_client *i2c_edid; - struct i2c_client *i2c_hdmi; - struct i2c_client *i2c_test; - struct i2c_client *i2c_cp; - struct i2c_client *i2c_vdp; + struct i2c_client *i2c_clients[ADV7604_PAGE_MAX]; /* controls */ struct v4l2_ctrl *detect_tx_5v_ctrl; @@ -101,6 +173,11 @@ struct adv7604_state { struct v4l2_ctrl *rgb_quantization_range_ctrl; }; +static bool adv7604_has_afe(struct adv7604_state *state) +{ + return state->info->has_afe; +} + /* Supported CEA and DMT timings */ static const struct v4l2_dv_timings adv7604_timings[] = { V4L2_DV_BT_CEA_720X480P59_94, @@ -256,11 +333,6 @@ static inline struct adv7604_state *to_state(struct v4l2_subdev *sd) return container_of(sd, struct adv7604_state, sd); } -static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) -{ - return &container_of(ctrl->handler, struct adv7604_state, hdl)->sd; -} - static inline unsigned hblanking(const struct v4l2_bt_timings *t) { return V4L2_DV_BT_BLANKING_WIDTH(t); @@ -298,14 +370,18 @@ static s32 adv_smbus_read_byte_data_check(struct i2c_client *client, return -EIO; } -static s32 adv_smbus_read_byte_data(struct i2c_client *client, u8 command) +static s32 adv_smbus_read_byte_data(struct adv7604_state *state, + enum adv7604_page page, u8 command) { - return adv_smbus_read_byte_data_check(client, command, true); + return adv_smbus_read_byte_data_check(state->i2c_clients[page], + command, true); } -static s32 adv_smbus_write_byte_data(struct i2c_client *client, - u8 command, u8 value) +static s32 adv_smbus_write_byte_data(struct adv7604_state *state, + enum adv7604_page page, u8 command, + u8 value) { + struct i2c_client *client = state->i2c_clients[page]; union i2c_smbus_data data; int err; int i; @@ -325,9 +401,11 @@ static s32 adv_smbus_write_byte_data(struct i2c_client *client, return err; } -static s32 adv_smbus_write_i2c_block_data(struct i2c_client *client, - u8 command, unsigned length, const u8 *values) +static s32 adv_smbus_write_i2c_block_data(struct adv7604_state *state, + enum adv7604_page page, u8 command, + unsigned length, const u8 *values) { + struct i2c_client *client = state->i2c_clients[page]; union i2c_smbus_data data; if (length > I2C_SMBUS_BLOCK_MAX) @@ -343,149 +421,150 @@ static s32 adv_smbus_write_i2c_block_data(struct i2c_client *client, static inline int io_read(struct v4l2_subdev *sd, u8 reg) { - struct i2c_client *client = v4l2_get_subdevdata(sd); + struct adv7604_state *state = to_state(sd); - return adv_smbus_read_byte_data(client, reg); + return adv_smbus_read_byte_data(state, ADV7604_PAGE_IO, reg); } static inline int io_write(struct v4l2_subdev *sd, u8 reg, u8 val) { - struct i2c_client *client = v4l2_get_subdevdata(sd); + struct adv7604_state *state = to_state(sd); - return adv_smbus_write_byte_data(client, reg, val); + return adv_smbus_write_byte_data(state, ADV7604_PAGE_IO, reg, val); } -static inline int io_write_and_or(struct v4l2_subdev *sd, u8 reg, u8 mask, u8 val) +static inline int io_write_clr_set(struct v4l2_subdev *sd, u8 reg, u8 mask, u8 val) { - return io_write(sd, reg, (io_read(sd, reg) & mask) | val); + return io_write(sd, reg, (io_read(sd, reg) & ~mask) | val); } static inline int avlink_read(struct v4l2_subdev *sd, u8 reg) { struct adv7604_state *state = to_state(sd); - return adv_smbus_read_byte_data(state->i2c_avlink, reg); + return adv_smbus_read_byte_data(state, ADV7604_PAGE_AVLINK, reg); } static inline int avlink_write(struct v4l2_subdev *sd, u8 reg, u8 val) { struct adv7604_state *state = to_state(sd); - return adv_smbus_write_byte_data(state->i2c_avlink, reg, val); + return adv_smbus_write_byte_data(state, ADV7604_PAGE_AVLINK, reg, val); } static inline int cec_read(struct v4l2_subdev *sd, u8 reg) { struct adv7604_state *state = to_state(sd); - return adv_smbus_read_byte_data(state->i2c_cec, reg); + return adv_smbus_read_byte_data(state, ADV7604_PAGE_CEC, reg); } static inline int cec_write(struct v4l2_subdev *sd, u8 reg, u8 val) { struct adv7604_state *state = to_state(sd); - return adv_smbus_write_byte_data(state->i2c_cec, reg, val); + return adv_smbus_write_byte_data(state, ADV7604_PAGE_CEC, reg, val); } -static inline int cec_write_and_or(struct v4l2_subdev *sd, u8 reg, u8 mask, u8 val) +static inline int cec_write_clr_set(struct v4l2_subdev *sd, u8 reg, u8 mask, u8 val) { - return cec_write(sd, reg, (cec_read(sd, reg) & mask) | val); + return cec_write(sd, reg, (cec_read(sd, reg) & ~mask) | val); } static inline int infoframe_read(struct v4l2_subdev *sd, u8 reg) { struct adv7604_state *state = to_state(sd); - return adv_smbus_read_byte_data(state->i2c_infoframe, reg); + return adv_smbus_read_byte_data(state, ADV7604_PAGE_INFOFRAME, reg); } static inline int infoframe_write(struct v4l2_subdev *sd, u8 reg, u8 val) { struct adv7604_state *state = to_state(sd); - return adv_smbus_write_byte_data(state->i2c_infoframe, reg, val); + return adv_smbus_write_byte_data(state, ADV7604_PAGE_INFOFRAME, + reg, val); } static inline int esdp_read(struct v4l2_subdev *sd, u8 reg) { struct adv7604_state *state = to_state(sd); - return adv_smbus_read_byte_data(state->i2c_esdp, reg); + return adv_smbus_read_byte_data(state, ADV7604_PAGE_ESDP, reg); } static inline int esdp_write(struct v4l2_subdev *sd, u8 reg, u8 val) { struct adv7604_state *state = to_state(sd); - return adv_smbus_write_byte_data(state->i2c_esdp, reg, val); + return adv_smbus_write_byte_data(state, ADV7604_PAGE_ESDP, reg, val); } static inline int dpp_read(struct v4l2_subdev *sd, u8 reg) { struct adv7604_state *state = to_state(sd); - return adv_smbus_read_byte_data(state->i2c_dpp, reg); + return adv_smbus_read_byte_data(state, ADV7604_PAGE_DPP, reg); } static inline int dpp_write(struct v4l2_subdev *sd, u8 reg, u8 val) { struct adv7604_state *state = to_state(sd); - return adv_smbus_write_byte_data(state->i2c_dpp, reg, val); + return adv_smbus_write_byte_data(state, ADV7604_PAGE_DPP, reg, val); } static inline int afe_read(struct v4l2_subdev *sd, u8 reg) { struct adv7604_state *state = to_state(sd); - return adv_smbus_read_byte_data(state->i2c_afe, reg); + return adv_smbus_read_byte_data(state, ADV7604_PAGE_AFE, reg); } static inline int afe_write(struct v4l2_subdev *sd, u8 reg, u8 val) { struct adv7604_state *state = to_state(sd); - return adv_smbus_write_byte_data(state->i2c_afe, reg, val); + return adv_smbus_write_byte_data(state, ADV7604_PAGE_AFE, reg, val); } static inline int rep_read(struct v4l2_subdev *sd, u8 reg) { struct adv7604_state *state = to_state(sd); - return adv_smbus_read_byte_data(state->i2c_repeater, reg); + return adv_smbus_read_byte_data(state, ADV7604_PAGE_REP, reg); } static inline int rep_write(struct v4l2_subdev *sd, u8 reg, u8 val) { struct adv7604_state *state = to_state(sd); - return adv_smbus_write_byte_data(state->i2c_repeater, reg, val); + return adv_smbus_write_byte_data(state, ADV7604_PAGE_REP, reg, val); } -static inline int rep_write_and_or(struct v4l2_subdev *sd, u8 reg, u8 mask, u8 val) +static inline int rep_write_clr_set(struct v4l2_subdev *sd, u8 reg, u8 mask, u8 val) { - return rep_write(sd, reg, (rep_read(sd, reg) & mask) | val); + return rep_write(sd, reg, (rep_read(sd, reg) & ~mask) | val); } static inline int edid_read(struct v4l2_subdev *sd, u8 reg) { struct adv7604_state *state = to_state(sd); - return adv_smbus_read_byte_data(state->i2c_edid, reg); + return adv_smbus_read_byte_data(state, ADV7604_PAGE_EDID, reg); } static inline int edid_write(struct v4l2_subdev *sd, u8 reg, u8 val) { struct adv7604_state *state = to_state(sd); - return adv_smbus_write_byte_data(state->i2c_edid, reg, val); + return adv_smbus_write_byte_data(state, ADV7604_PAGE_EDID, reg, val); } static inline int edid_read_block(struct v4l2_subdev *sd, unsigned len, u8 *val) { struct adv7604_state *state = to_state(sd); - struct i2c_client *client = state->i2c_edid; + struct i2c_client *client = state->i2c_clients[ADV7604_PAGE_EDID]; u8 msgbuf0[1] = { 0 }; u8 msgbuf1[256]; struct i2c_msg msg[2] = { @@ -518,11 +597,25 @@ static inline int edid_write_block(struct v4l2_subdev *sd, v4l2_dbg(2, debug, sd, "%s: write EDID block (%d byte)\n", __func__, len); for (i = 0; !err && i < len; i += I2C_SMBUS_BLOCK_MAX) - err = adv_smbus_write_i2c_block_data(state->i2c_edid, i, - I2C_SMBUS_BLOCK_MAX, val + i); + err = adv_smbus_write_i2c_block_data(state, ADV7604_PAGE_EDID, + i, I2C_SMBUS_BLOCK_MAX, val + i); return err; } +static void adv7604_set_hpd(struct adv7604_state *state, unsigned int hpd) +{ + unsigned int i; + + for (i = 0; i < state->info->num_dv_ports; ++i) { + if (IS_ERR(state->hpd_gpio[i])) + continue; + + gpiod_set_value_cansleep(state->hpd_gpio[i], hpd & BIT(i)); + } + + v4l2_subdev_notify(&state->sd, ADV7604_HOTPLUG, &hpd); +} + static void adv7604_delayed_work_enable_hotplug(struct work_struct *work) { struct delayed_work *dwork = to_delayed_work(work); @@ -532,73 +625,210 @@ static void adv7604_delayed_work_enable_hotplug(struct work_struct *work) v4l2_dbg(2, debug, sd, "%s: enable hotplug\n", __func__); - v4l2_subdev_notify(sd, ADV7604_HOTPLUG, (void *)&state->edid.present); + adv7604_set_hpd(state, state->edid.present); } static inline int hdmi_read(struct v4l2_subdev *sd, u8 reg) { struct adv7604_state *state = to_state(sd); - return adv_smbus_read_byte_data(state->i2c_hdmi, reg); + return adv_smbus_read_byte_data(state, ADV7604_PAGE_HDMI, reg); +} + +static u16 hdmi_read16(struct v4l2_subdev *sd, u8 reg, u16 mask) +{ + return ((hdmi_read(sd, reg) << 8) | hdmi_read(sd, reg + 1)) & mask; } static inline int hdmi_write(struct v4l2_subdev *sd, u8 reg, u8 val) { struct adv7604_state *state = to_state(sd); - return adv_smbus_write_byte_data(state->i2c_hdmi, reg, val); + return adv_smbus_write_byte_data(state, ADV7604_PAGE_HDMI, reg, val); } -static inline int hdmi_write_and_or(struct v4l2_subdev *sd, u8 reg, u8 mask, u8 val) +static inline int hdmi_write_clr_set(struct v4l2_subdev *sd, u8 reg, u8 mask, u8 val) { - return hdmi_write(sd, reg, (hdmi_read(sd, reg) & mask) | val); + return hdmi_write(sd, reg, (hdmi_read(sd, reg) & ~mask) | val); } static inline int test_read(struct v4l2_subdev *sd, u8 reg) { struct adv7604_state *state = to_state(sd); - return adv_smbus_read_byte_data(state->i2c_test, reg); + return adv_smbus_read_byte_data(state, ADV7604_PAGE_TEST, reg); } static inline int test_write(struct v4l2_subdev *sd, u8 reg, u8 val) { struct adv7604_state *state = to_state(sd); - return adv_smbus_write_byte_data(state->i2c_test, reg, val); + return adv_smbus_write_byte_data(state, ADV7604_PAGE_TEST, reg, val); } static inline int cp_read(struct v4l2_subdev *sd, u8 reg) { struct adv7604_state *state = to_state(sd); - return adv_smbus_read_byte_data(state->i2c_cp, reg); + return adv_smbus_read_byte_data(state, ADV7604_PAGE_CP, reg); +} + +static u16 cp_read16(struct v4l2_subdev *sd, u8 reg, u16 mask) +{ + return ((cp_read(sd, reg) << 8) | cp_read(sd, reg + 1)) & mask; } static inline int cp_write(struct v4l2_subdev *sd, u8 reg, u8 val) { struct adv7604_state *state = to_state(sd); - return adv_smbus_write_byte_data(state->i2c_cp, reg, val); + return adv_smbus_write_byte_data(state, ADV7604_PAGE_CP, reg, val); } -static inline int cp_write_and_or(struct v4l2_subdev *sd, u8 reg, u8 mask, u8 val) +static inline int cp_write_clr_set(struct v4l2_subdev *sd, u8 reg, u8 mask, u8 val) { - return cp_write(sd, reg, (cp_read(sd, reg) & mask) | val); + return cp_write(sd, reg, (cp_read(sd, reg) & ~mask) | val); } static inline int vdp_read(struct v4l2_subdev *sd, u8 reg) { struct adv7604_state *state = to_state(sd); - return adv_smbus_read_byte_data(state->i2c_vdp, reg); + return adv_smbus_read_byte_data(state, ADV7604_PAGE_VDP, reg); } static inline int vdp_write(struct v4l2_subdev *sd, u8 reg, u8 val) { struct adv7604_state *state = to_state(sd); - return adv_smbus_write_byte_data(state->i2c_vdp, reg, val); + return adv_smbus_write_byte_data(state, ADV7604_PAGE_VDP, reg, val); +} + +#define ADV7604_REG(page, offset) (((page) << 8) | (offset)) +#define ADV7604_REG_SEQ_TERM 0xffff + +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int adv7604_read_reg(struct v4l2_subdev *sd, unsigned int reg) +{ + struct adv7604_state *state = to_state(sd); + unsigned int page = reg >> 8; + + if (!(BIT(page) & state->info->page_mask)) + return -EINVAL; + + reg &= 0xff; + + return adv_smbus_read_byte_data(state, page, reg); +} +#endif + +static int adv7604_write_reg(struct v4l2_subdev *sd, unsigned int reg, u8 val) +{ + struct adv7604_state *state = to_state(sd); + unsigned int page = reg >> 8; + + if (!(BIT(page) & state->info->page_mask)) + return -EINVAL; + + reg &= 0xff; + + return adv_smbus_write_byte_data(state, page, reg, val); +} + +static void adv7604_write_reg_seq(struct v4l2_subdev *sd, + const struct adv7604_reg_seq *reg_seq) +{ + unsigned int i; + + for (i = 0; reg_seq[i].reg != ADV7604_REG_SEQ_TERM; i++) + adv7604_write_reg(sd, reg_seq[i].reg, reg_seq[i].val); +} + +/* ----------------------------------------------------------------------------- + * Format helpers + */ + +static const struct adv7604_format_info adv7604_formats[] = { + { V4L2_MBUS_FMT_RGB888_1X24, ADV7604_OP_CH_SEL_RGB, true, false, + ADV7604_OP_MODE_SEL_SDR_444 | ADV7604_OP_FORMAT_SEL_8BIT }, + { V4L2_MBUS_FMT_YUYV8_2X8, ADV7604_OP_CH_SEL_RGB, false, false, + ADV7604_OP_MODE_SEL_SDR_422 | ADV7604_OP_FORMAT_SEL_8BIT }, + { V4L2_MBUS_FMT_YVYU8_2X8, ADV7604_OP_CH_SEL_RGB, false, true, + ADV7604_OP_MODE_SEL_SDR_422 | ADV7604_OP_FORMAT_SEL_8BIT }, + { V4L2_MBUS_FMT_YUYV10_2X10, ADV7604_OP_CH_SEL_RGB, false, false, + ADV7604_OP_MODE_SEL_SDR_422 | ADV7604_OP_FORMAT_SEL_10BIT }, + { V4L2_MBUS_FMT_YVYU10_2X10, ADV7604_OP_CH_SEL_RGB, false, true, + ADV7604_OP_MODE_SEL_SDR_422 | ADV7604_OP_FORMAT_SEL_10BIT }, + { V4L2_MBUS_FMT_YUYV12_2X12, ADV7604_OP_CH_SEL_RGB, false, false, + ADV7604_OP_MODE_SEL_SDR_422 | ADV7604_OP_FORMAT_SEL_12BIT }, + { V4L2_MBUS_FMT_YVYU12_2X12, ADV7604_OP_CH_SEL_RGB, false, true, + ADV7604_OP_MODE_SEL_SDR_422 | ADV7604_OP_FORMAT_SEL_12BIT }, + { V4L2_MBUS_FMT_UYVY8_1X16, ADV7604_OP_CH_SEL_RBG, false, false, + ADV7604_OP_MODE_SEL_SDR_422_2X | ADV7604_OP_FORMAT_SEL_8BIT }, + { V4L2_MBUS_FMT_VYUY8_1X16, ADV7604_OP_CH_SEL_RBG, false, true, + ADV7604_OP_MODE_SEL_SDR_422_2X | ADV7604_OP_FORMAT_SEL_8BIT }, + { V4L2_MBUS_FMT_YUYV8_1X16, ADV7604_OP_CH_SEL_RGB, false, false, + ADV7604_OP_MODE_SEL_SDR_422_2X | ADV7604_OP_FORMAT_SEL_8BIT }, + { V4L2_MBUS_FMT_YVYU8_1X16, ADV7604_OP_CH_SEL_RGB, false, true, + ADV7604_OP_MODE_SEL_SDR_422_2X | ADV7604_OP_FORMAT_SEL_8BIT }, + { V4L2_MBUS_FMT_UYVY10_1X20, ADV7604_OP_CH_SEL_RBG, false, false, + ADV7604_OP_MODE_SEL_SDR_422_2X | ADV7604_OP_FORMAT_SEL_10BIT }, + { V4L2_MBUS_FMT_VYUY10_1X20, ADV7604_OP_CH_SEL_RBG, false, true, + ADV7604_OP_MODE_SEL_SDR_422_2X | ADV7604_OP_FORMAT_SEL_10BIT }, + { V4L2_MBUS_FMT_YUYV10_1X20, ADV7604_OP_CH_SEL_RGB, false, false, + ADV7604_OP_MODE_SEL_SDR_422_2X | ADV7604_OP_FORMAT_SEL_10BIT }, + { V4L2_MBUS_FMT_YVYU10_1X20, ADV7604_OP_CH_SEL_RGB, false, true, + ADV7604_OP_MODE_SEL_SDR_422_2X | ADV7604_OP_FORMAT_SEL_10BIT }, + { V4L2_MBUS_FMT_UYVY12_1X24, ADV7604_OP_CH_SEL_RBG, false, false, + ADV7604_OP_MODE_SEL_SDR_422_2X | ADV7604_OP_FORMAT_SEL_12BIT }, + { V4L2_MBUS_FMT_VYUY12_1X24, ADV7604_OP_CH_SEL_RBG, false, true, + ADV7604_OP_MODE_SEL_SDR_422_2X | ADV7604_OP_FORMAT_SEL_12BIT }, + { V4L2_MBUS_FMT_YUYV12_1X24, ADV7604_OP_CH_SEL_RGB, false, false, + ADV7604_OP_MODE_SEL_SDR_422_2X | ADV7604_OP_FORMAT_SEL_12BIT }, + { V4L2_MBUS_FMT_YVYU12_1X24, ADV7604_OP_CH_SEL_RGB, false, true, + ADV7604_OP_MODE_SEL_SDR_422_2X | ADV7604_OP_FORMAT_SEL_12BIT }, +}; + +static const struct adv7604_format_info adv7611_formats[] = { + { V4L2_MBUS_FMT_RGB888_1X24, ADV7604_OP_CH_SEL_RGB, true, false, + ADV7604_OP_MODE_SEL_SDR_444 | ADV7604_OP_FORMAT_SEL_8BIT }, + { V4L2_MBUS_FMT_YUYV8_2X8, ADV7604_OP_CH_SEL_RGB, false, false, + ADV7604_OP_MODE_SEL_SDR_422 | ADV7604_OP_FORMAT_SEL_8BIT }, + { V4L2_MBUS_FMT_YVYU8_2X8, ADV7604_OP_CH_SEL_RGB, false, true, + ADV7604_OP_MODE_SEL_SDR_422 | ADV7604_OP_FORMAT_SEL_8BIT }, + { V4L2_MBUS_FMT_YUYV12_2X12, ADV7604_OP_CH_SEL_RGB, false, false, + ADV7604_OP_MODE_SEL_SDR_422 | ADV7604_OP_FORMAT_SEL_12BIT }, + { V4L2_MBUS_FMT_YVYU12_2X12, ADV7604_OP_CH_SEL_RGB, false, true, + ADV7604_OP_MODE_SEL_SDR_422 | ADV7604_OP_FORMAT_SEL_12BIT }, + { V4L2_MBUS_FMT_UYVY8_1X16, ADV7604_OP_CH_SEL_RBG, false, false, + ADV7604_OP_MODE_SEL_SDR_422_2X | ADV7604_OP_FORMAT_SEL_8BIT }, + { V4L2_MBUS_FMT_VYUY8_1X16, ADV7604_OP_CH_SEL_RBG, false, true, + ADV7604_OP_MODE_SEL_SDR_422_2X | ADV7604_OP_FORMAT_SEL_8BIT }, + { V4L2_MBUS_FMT_YUYV8_1X16, ADV7604_OP_CH_SEL_RGB, false, false, + ADV7604_OP_MODE_SEL_SDR_422_2X | ADV7604_OP_FORMAT_SEL_8BIT }, + { V4L2_MBUS_FMT_YVYU8_1X16, ADV7604_OP_CH_SEL_RGB, false, true, + ADV7604_OP_MODE_SEL_SDR_422_2X | ADV7604_OP_FORMAT_SEL_8BIT }, + { V4L2_MBUS_FMT_UYVY12_1X24, ADV7604_OP_CH_SEL_RBG, false, false, + ADV7604_OP_MODE_SEL_SDR_422_2X | ADV7604_OP_FORMAT_SEL_12BIT }, + { V4L2_MBUS_FMT_VYUY12_1X24, ADV7604_OP_CH_SEL_RBG, false, true, + ADV7604_OP_MODE_SEL_SDR_422_2X | ADV7604_OP_FORMAT_SEL_12BIT }, + { V4L2_MBUS_FMT_YUYV12_1X24, ADV7604_OP_CH_SEL_RGB, false, false, + ADV7604_OP_MODE_SEL_SDR_422_2X | ADV7604_OP_FORMAT_SEL_12BIT }, + { V4L2_MBUS_FMT_YVYU12_1X24, ADV7604_OP_CH_SEL_RGB, false, true, + ADV7604_OP_MODE_SEL_SDR_422_2X | ADV7604_OP_FORMAT_SEL_12BIT }, +}; + +static const struct adv7604_format_info * +adv7604_format_info(struct adv7604_state *state, enum v4l2_mbus_pixelcode code) +{ + unsigned int i; + + for (i = 0; i < state->info->nformats; ++i) { + if (state->info->formats[i].code == code) + return &state->info->formats[i]; + } + + return NULL; } /* ----------------------------------------------------------------------- */ @@ -607,18 +837,18 @@ static inline bool is_analog_input(struct v4l2_subdev *sd) { struct adv7604_state *state = to_state(sd); - return state->selected_input == ADV7604_INPUT_VGA_RGB || - state->selected_input == ADV7604_INPUT_VGA_COMP; + return state->selected_input == ADV7604_PAD_VGA_RGB || + state->selected_input == ADV7604_PAD_VGA_COMP; } static inline bool is_digital_input(struct v4l2_subdev *sd) { struct adv7604_state *state = to_state(sd); - return state->selected_input == ADV7604_INPUT_HDMI_PORT_A || - state->selected_input == ADV7604_INPUT_HDMI_PORT_B || - state->selected_input == ADV7604_INPUT_HDMI_PORT_C || - state->selected_input == ADV7604_INPUT_HDMI_PORT_D; + return state->selected_input == ADV7604_PAD_HDMI_PORT_A || + state->selected_input == ADV7604_PAD_HDMI_PORT_B || + state->selected_input == ADV7604_PAD_HDMI_PORT_C || + state->selected_input == ADV7604_PAD_HDMI_PORT_D; } /* ----------------------------------------------------------------------- */ @@ -644,119 +874,61 @@ static void adv7604_inv_register(struct v4l2_subdev *sd) static int adv7604_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) { - reg->size = 1; - switch (reg->reg >> 8) { - case 0: - reg->val = io_read(sd, reg->reg & 0xff); - break; - case 1: - reg->val = avlink_read(sd, reg->reg & 0xff); - break; - case 2: - reg->val = cec_read(sd, reg->reg & 0xff); - break; - case 3: - reg->val = infoframe_read(sd, reg->reg & 0xff); - break; - case 4: - reg->val = esdp_read(sd, reg->reg & 0xff); - break; - case 5: - reg->val = dpp_read(sd, reg->reg & 0xff); - break; - case 6: - reg->val = afe_read(sd, reg->reg & 0xff); - break; - case 7: - reg->val = rep_read(sd, reg->reg & 0xff); - break; - case 8: - reg->val = edid_read(sd, reg->reg & 0xff); - break; - case 9: - reg->val = hdmi_read(sd, reg->reg & 0xff); - break; - case 0xa: - reg->val = test_read(sd, reg->reg & 0xff); - break; - case 0xb: - reg->val = cp_read(sd, reg->reg & 0xff); - break; - case 0xc: - reg->val = vdp_read(sd, reg->reg & 0xff); - break; - default: + int ret; + + ret = adv7604_read_reg(sd, reg->reg); + if (ret < 0) { v4l2_info(sd, "Register %03llx not supported\n", reg->reg); adv7604_inv_register(sd); - break; + return ret; } + + reg->size = 1; + reg->val = ret; + return 0; } static int adv7604_s_register(struct v4l2_subdev *sd, const struct v4l2_dbg_register *reg) { - u8 val = reg->val & 0xff; + int ret; - switch (reg->reg >> 8) { - case 0: - io_write(sd, reg->reg & 0xff, val); - break; - case 1: - avlink_write(sd, reg->reg & 0xff, val); - break; - case 2: - cec_write(sd, reg->reg & 0xff, val); - break; - case 3: - infoframe_write(sd, reg->reg & 0xff, val); - break; - case 4: - esdp_write(sd, reg->reg & 0xff, val); - break; - case 5: - dpp_write(sd, reg->reg & 0xff, val); - break; - case 6: - afe_write(sd, reg->reg & 0xff, val); - break; - case 7: - rep_write(sd, reg->reg & 0xff, val); - break; - case 8: - edid_write(sd, reg->reg & 0xff, val); - break; - case 9: - hdmi_write(sd, reg->reg & 0xff, val); - break; - case 0xa: - test_write(sd, reg->reg & 0xff, val); - break; - case 0xb: - cp_write(sd, reg->reg & 0xff, val); - break; - case 0xc: - vdp_write(sd, reg->reg & 0xff, val); - break; - default: + ret = adv7604_write_reg(sd, reg->reg, reg->val); + if (ret < 0) { v4l2_info(sd, "Register %03llx not supported\n", reg->reg); adv7604_inv_register(sd); - break; + return ret; } + return 0; } #endif +static unsigned int adv7604_read_cable_det(struct v4l2_subdev *sd) +{ + u8 value = io_read(sd, 0x6f); + + return ((value & 0x10) >> 4) + | ((value & 0x08) >> 2) + | ((value & 0x04) << 0) + | ((value & 0x02) << 2); +} + +static unsigned int adv7611_read_cable_det(struct v4l2_subdev *sd) +{ + u8 value = io_read(sd, 0x6f); + + return value & 1; +} + static int adv7604_s_detect_tx_5v_ctrl(struct v4l2_subdev *sd) { struct adv7604_state *state = to_state(sd); - u8 reg_io_6f = io_read(sd, 0x6f); + const struct adv7604_chip_info *info = state->info; return v4l2_ctrl_s_ctrl(state->detect_tx_5v_ctrl, - ((reg_io_6f & 0x10) >> 4) | - ((reg_io_6f & 0x08) >> 2) | - (reg_io_6f & 0x04) | - ((reg_io_6f & 0x02) << 2)); + info->read_cable_det(sd)); } static int find_and_set_predefined_video_timings(struct v4l2_subdev *sd, @@ -787,11 +959,13 @@ static int configure_predefined_video_timings(struct v4l2_subdev *sd, v4l2_dbg(1, debug, sd, "%s", __func__); - /* reset to default values */ - io_write(sd, 0x16, 0x43); - io_write(sd, 0x17, 0x5a); + if (adv7604_has_afe(state)) { + /* reset to default values */ + io_write(sd, 0x16, 0x43); + io_write(sd, 0x17, 0x5a); + } /* disable embedded syncs for auto graphics mode */ - cp_write_and_or(sd, 0x81, 0xef, 0x00); + cp_write_clr_set(sd, 0x81, 0x10, 0x00); cp_write(sd, 0x8f, 0x00); cp_write(sd, 0x90, 0x00); cp_write(sd, 0xa2, 0x00); @@ -829,7 +1003,6 @@ static void configure_custom_video_timings(struct v4l2_subdev *sd, const struct v4l2_bt_timings *bt) { struct adv7604_state *state = to_state(sd); - struct i2c_client *client = v4l2_get_subdevdata(sd); u32 width = htotal(bt); u32 height = vtotal(bt); u16 cp_start_sav = bt->hsync + bt->hbackporch - 4; @@ -850,12 +1023,13 @@ static void configure_custom_video_timings(struct v4l2_subdev *sd, io_write(sd, 0x00, 0x07); /* video std */ io_write(sd, 0x01, 0x02); /* prim mode */ /* enable embedded syncs for auto graphics mode */ - cp_write_and_or(sd, 0x81, 0xef, 0x10); + cp_write_clr_set(sd, 0x81, 0x10, 0x10); /* Should only be set in auto-graphics mode [REF_02, p. 91-92] */ /* setup PLL_DIV_MAN_EN and PLL_DIV_RATIO */ /* IO-map reg. 0x16 and 0x17 should be written in sequence */ - if (adv_smbus_write_i2c_block_data(client, 0x16, 2, pll)) + if (adv_smbus_write_i2c_block_data(state, ADV7604_PAGE_IO, + 0x16, 2, pll)) v4l2_err(sd, "writing to reg 0x16 and 0x17 failed\n"); /* active video - horizontal timing */ @@ -906,7 +1080,8 @@ static void adv7604_set_offset(struct v4l2_subdev *sd, bool auto_offset, u16 off offset_buf[3] = offset_c & 0x0ff; /* Registers must be written in this order with no i2c access in between */ - if (adv_smbus_write_i2c_block_data(state->i2c_cp, 0x77, 4, offset_buf)) + if (adv_smbus_write_i2c_block_data(state, ADV7604_PAGE_CP, + 0x77, 4, offset_buf)) v4l2_err(sd, "%s: i2c error writing to CP reg 0x77, 0x78, 0x79, 0x7a\n", __func__); } @@ -935,7 +1110,8 @@ static void adv7604_set_gain(struct v4l2_subdev *sd, bool auto_gain, u16 gain_a, gain_buf[3] = ((gain_c & 0x0ff)); /* Registers must be written in this order with no i2c access in between */ - if (adv_smbus_write_i2c_block_data(state->i2c_cp, 0x73, 4, gain_buf)) + if (adv_smbus_write_i2c_block_data(state, ADV7604_PAGE_CP, + 0x73, 4, gain_buf)) v4l2_err(sd, "%s: i2c error writing to CP reg 0x73, 0x74, 0x75, 0x76\n", __func__); } @@ -954,24 +1130,24 @@ static void set_rgb_quantization_range(struct v4l2_subdev *sd) switch (state->rgb_quantization_range) { case V4L2_DV_RGB_RANGE_AUTO: - if (state->selected_input == ADV7604_INPUT_VGA_RGB) { + if (state->selected_input == ADV7604_PAD_VGA_RGB) { /* Receiving analog RGB signal * Set RGB full range (0-255) */ - io_write_and_or(sd, 0x02, 0x0f, 0x10); + io_write_clr_set(sd, 0x02, 0xf0, 0x10); break; } - if (state->selected_input == ADV7604_INPUT_VGA_COMP) { + if (state->selected_input == ADV7604_PAD_VGA_COMP) { /* Receiving analog YPbPr signal * Set automode */ - io_write_and_or(sd, 0x02, 0x0f, 0xf0); + io_write_clr_set(sd, 0x02, 0xf0, 0xf0); break; } if (hdmi_signal) { /* Receiving HDMI signal * Set automode */ - io_write_and_or(sd, 0x02, 0x0f, 0xf0); + io_write_clr_set(sd, 0x02, 0xf0, 0xf0); break; } @@ -980,10 +1156,10 @@ static void set_rgb_quantization_range(struct v4l2_subdev *sd) * input format (CE/IT) in automatic mode */ if (state->timings.bt.standards & V4L2_DV_BT_STD_CEA861) { /* RGB limited range (16-235) */ - io_write_and_or(sd, 0x02, 0x0f, 0x00); + io_write_clr_set(sd, 0x02, 0xf0, 0x00); } else { /* RGB full range (0-255) */ - io_write_and_or(sd, 0x02, 0x0f, 0x10); + io_write_clr_set(sd, 0x02, 0xf0, 0x10); if (is_digital_input(sd) && rgb_output) { adv7604_set_offset(sd, false, 0x40, 0x40, 0x40); @@ -994,25 +1170,25 @@ static void set_rgb_quantization_range(struct v4l2_subdev *sd) } break; case V4L2_DV_RGB_RANGE_LIMITED: - if (state->selected_input == ADV7604_INPUT_VGA_COMP) { + if (state->selected_input == ADV7604_PAD_VGA_COMP) { /* YCrCb limited range (16-235) */ - io_write_and_or(sd, 0x02, 0x0f, 0x20); + io_write_clr_set(sd, 0x02, 0xf0, 0x20); break; } /* RGB limited range (16-235) */ - io_write_and_or(sd, 0x02, 0x0f, 0x00); + io_write_clr_set(sd, 0x02, 0xf0, 0x00); break; case V4L2_DV_RGB_RANGE_FULL: - if (state->selected_input == ADV7604_INPUT_VGA_COMP) { + if (state->selected_input == ADV7604_PAD_VGA_COMP) { /* YCrCb full range (0-255) */ - io_write_and_or(sd, 0x02, 0x0f, 0x60); + io_write_clr_set(sd, 0x02, 0xf0, 0x60); break; } /* RGB full range (0-255) */ - io_write_and_or(sd, 0x02, 0x0f, 0x10); + io_write_clr_set(sd, 0x02, 0xf0, 0x10); if (is_analog_input(sd) || hdmi_signal) break; @@ -1030,7 +1206,9 @@ static void set_rgb_quantization_range(struct v4l2_subdev *sd) static int adv7604_s_ctrl(struct v4l2_ctrl *ctrl) { - struct v4l2_subdev *sd = to_sd(ctrl); + struct v4l2_subdev *sd = + &container_of(ctrl->handler, struct adv7604_state, hdl)->sd; + struct adv7604_state *state = to_state(sd); switch (ctrl->id) { @@ -1051,6 +1229,8 @@ static int adv7604_s_ctrl(struct v4l2_ctrl *ctrl) set_rgb_quantization_range(sd); return 0; case V4L2_CID_ADV_RX_ANALOG_SAMPLING_PHASE: + if (!adv7604_has_afe(state)) + return -EINVAL; /* Set the analog sampling phase. This is needed to find the best sampling phase for analog video: an application or driver has to try a number of phases and analyze the picture @@ -1060,7 +1240,7 @@ static int adv7604_s_ctrl(struct v4l2_ctrl *ctrl) case V4L2_CID_ADV_RX_FREE_RUN_COLOR_MANUAL: /* Use the default blue color for free running mode, or supply your own. */ - cp_write_and_or(sd, 0xbf, ~0x04, (ctrl->val << 2)); + cp_write_clr_set(sd, 0xbf, 0x04, ctrl->val << 2); return 0; case V4L2_CID_ADV_RX_FREE_RUN_COLOR: cp_write(sd, 0xc0, (ctrl->val & 0xff0000) >> 16); @@ -1088,7 +1268,10 @@ static inline bool no_signal_tmds(struct v4l2_subdev *sd) static inline bool no_lock_tmds(struct v4l2_subdev *sd) { - return (io_read(sd, 0x6a) & 0xe0) != 0xe0; + struct adv7604_state *state = to_state(sd); + const struct adv7604_chip_info *info = state->info; + + return (io_read(sd, 0x6a) & info->tdms_lock_mask) != info->tdms_lock_mask; } static inline bool is_hdmi(struct v4l2_subdev *sd) @@ -1098,6 +1281,15 @@ static inline bool is_hdmi(struct v4l2_subdev *sd) static inline bool no_lock_sspd(struct v4l2_subdev *sd) { + struct adv7604_state *state = to_state(sd); + + /* + * Chips without a AFE don't expose registers for the SSPD, so just assume + * that we have a lock. + */ + if (adv7604_has_afe(state)) + return false; + /* TODO channel 2 */ return ((cp_read(sd, 0xb5) & 0xd0) != 0xd0); } @@ -1127,6 +1319,11 @@ static inline bool no_signal(struct v4l2_subdev *sd) static inline bool no_lock_cp(struct v4l2_subdev *sd) { + struct adv7604_state *state = to_state(sd); + + if (!adv7604_has_afe(state)) + return false; + /* CP has detected a non standard number of lines on the incoming video compared to what it is configured to receive by s_dv_timings */ return io_read(sd, 0x12) & 0x01; @@ -1195,28 +1392,40 @@ static int stdi2dv_timings(struct v4l2_subdev *sd, return -1; } + static int read_stdi(struct v4l2_subdev *sd, struct stdi_readback *stdi) { + struct adv7604_state *state = to_state(sd); + const struct adv7604_chip_info *info = state->info; + u8 polarity; + if (no_lock_stdi(sd) || no_lock_sspd(sd)) { v4l2_dbg(2, debug, sd, "%s: STDI and/or SSPD not locked\n", __func__); return -1; } /* read STDI */ - stdi->bl = ((cp_read(sd, 0xb1) & 0x3f) << 8) | cp_read(sd, 0xb2); - stdi->lcf = ((cp_read(sd, 0xb3) & 0x7) << 8) | cp_read(sd, 0xb4); + stdi->bl = cp_read16(sd, 0xb1, 0x3fff); + stdi->lcf = cp_read16(sd, info->lcf_reg, 0x7ff); stdi->lcvs = cp_read(sd, 0xb3) >> 3; stdi->interlaced = io_read(sd, 0x12) & 0x10; - /* read SSPD */ - if ((cp_read(sd, 0xb5) & 0x03) == 0x01) { - stdi->hs_pol = ((cp_read(sd, 0xb5) & 0x10) ? - ((cp_read(sd, 0xb5) & 0x08) ? '+' : '-') : 'x'); - stdi->vs_pol = ((cp_read(sd, 0xb5) & 0x40) ? - ((cp_read(sd, 0xb5) & 0x20) ? '+' : '-') : 'x'); + if (adv7604_has_afe(state)) { + /* read SSPD */ + polarity = cp_read(sd, 0xb5); + if ((polarity & 0x03) == 0x01) { + stdi->hs_pol = polarity & 0x10 + ? (polarity & 0x08 ? '+' : '-') : 'x'; + stdi->vs_pol = polarity & 0x40 + ? (polarity & 0x20 ? '+' : '-') : 'x'; + } else { + stdi->hs_pol = 'x'; + stdi->vs_pol = 'x'; + } } else { - stdi->hs_pol = 'x'; - stdi->vs_pol = 'x'; + polarity = hdmi_read(sd, 0x05); + stdi->hs_pol = polarity & 0x20 ? '+' : '-'; + stdi->vs_pol = polarity & 0x10 ? '+' : '-'; } if (no_lock_stdi(sd) || no_lock_sspd(sd)) { @@ -1243,8 +1452,14 @@ static int read_stdi(struct v4l2_subdev *sd, struct stdi_readback *stdi) static int adv7604_enum_dv_timings(struct v4l2_subdev *sd, struct v4l2_enum_dv_timings *timings) { + struct adv7604_state *state = to_state(sd); + if (timings->index >= ARRAY_SIZE(adv7604_timings) - 1) return -EINVAL; + + if (timings->pad >= state->source_pad) + return -EINVAL; + memset(timings->reserved, 0, sizeof(timings->reserved)); timings->timings = adv7604_timings[timings->index]; return 0; @@ -1253,14 +1468,30 @@ static int adv7604_enum_dv_timings(struct v4l2_subdev *sd, static int adv7604_dv_timings_cap(struct v4l2_subdev *sd, struct v4l2_dv_timings_cap *cap) { + struct adv7604_state *state = to_state(sd); + + if (cap->pad >= state->source_pad) + return -EINVAL; + cap->type = V4L2_DV_BT_656_1120; cap->bt.max_width = 1920; cap->bt.max_height = 1200; cap->bt.min_pixelclock = 25000000; - if (is_digital_input(sd)) + + switch (cap->pad) { + case ADV7604_PAD_HDMI_PORT_A: + case ADV7604_PAD_HDMI_PORT_B: + case ADV7604_PAD_HDMI_PORT_C: + case ADV7604_PAD_HDMI_PORT_D: cap->bt.max_pixelclock = 225000000; - else + break; + case ADV7604_PAD_VGA_RGB: + case ADV7604_PAD_VGA_COMP: + default: cap->bt.max_pixelclock = 170000000; + break; + } + cap->bt.standards = V4L2_DV_BT_STD_CEA861 | V4L2_DV_BT_STD_DMT | V4L2_DV_BT_STD_GTF | V4L2_DV_BT_STD_CVT; cap->bt.capabilities = V4L2_DV_BT_CAP_PROGRESSIVE | @@ -1284,10 +1515,43 @@ static void adv7604_fill_optional_dv_timings_fields(struct v4l2_subdev *sd, } } +static unsigned int adv7604_read_hdmi_pixelclock(struct v4l2_subdev *sd) +{ + unsigned int freq; + int a, b; + + a = hdmi_read(sd, 0x06); + b = hdmi_read(sd, 0x3b); + if (a < 0 || b < 0) + return 0; + freq = a * 1000000 + ((b & 0x30) >> 4) * 250000; + + if (is_hdmi(sd)) { + /* adjust for deep color mode */ + unsigned bits_per_channel = ((hdmi_read(sd, 0x0b) & 0x60) >> 4) + 8; + + freq = freq * 8 / bits_per_channel; + } + + return freq; +} + +static unsigned int adv7611_read_hdmi_pixelclock(struct v4l2_subdev *sd) +{ + int a, b; + + a = hdmi_read(sd, 0x51); + b = hdmi_read(sd, 0x52); + if (a < 0 || b < 0) + return 0; + return ((a << 1) | (b >> 7)) * 1000000 + (b & 0x7f) * 1000000 / 128; +} + static int adv7604_query_dv_timings(struct v4l2_subdev *sd, struct v4l2_dv_timings *timings) { struct adv7604_state *state = to_state(sd); + const struct adv7604_chip_info *info = state->info; struct v4l2_bt_timings *bt = &timings->bt; struct stdi_readback stdi; @@ -1311,44 +1575,25 @@ static int adv7604_query_dv_timings(struct v4l2_subdev *sd, V4L2_DV_INTERLACED : V4L2_DV_PROGRESSIVE; if (is_digital_input(sd)) { - uint32_t freq; - timings->type = V4L2_DV_BT_656_1120; - bt->width = (hdmi_read(sd, 0x07) & 0x0f) * 256 + hdmi_read(sd, 0x08); - bt->height = (hdmi_read(sd, 0x09) & 0x0f) * 256 + hdmi_read(sd, 0x0a); - freq = (hdmi_read(sd, 0x06) * 1000000) + - ((hdmi_read(sd, 0x3b) & 0x30) >> 4) * 250000; - if (is_hdmi(sd)) { - /* adjust for deep color mode */ - unsigned bits_per_channel = ((hdmi_read(sd, 0x0b) & 0x60) >> 4) + 8; - - freq = freq * 8 / bits_per_channel; - } - bt->pixelclock = freq; - bt->hfrontporch = (hdmi_read(sd, 0x20) & 0x03) * 256 + - hdmi_read(sd, 0x21); - bt->hsync = (hdmi_read(sd, 0x22) & 0x03) * 256 + - hdmi_read(sd, 0x23); - bt->hbackporch = (hdmi_read(sd, 0x24) & 0x03) * 256 + - hdmi_read(sd, 0x25); - bt->vfrontporch = ((hdmi_read(sd, 0x2a) & 0x1f) * 256 + - hdmi_read(sd, 0x2b)) / 2; - bt->vsync = ((hdmi_read(sd, 0x2e) & 0x1f) * 256 + - hdmi_read(sd, 0x2f)) / 2; - bt->vbackporch = ((hdmi_read(sd, 0x32) & 0x1f) * 256 + - hdmi_read(sd, 0x33)) / 2; + /* FIXME: All masks are incorrect for ADV7611 */ + bt->width = hdmi_read16(sd, 0x07, 0xfff); + bt->height = hdmi_read16(sd, 0x09, 0xfff); + bt->pixelclock = info->read_hdmi_pixelclock(sd); + bt->hfrontporch = hdmi_read16(sd, 0x20, 0x3ff); + bt->hsync = hdmi_read16(sd, 0x22, 0x3ff); + bt->hbackporch = hdmi_read16(sd, 0x24, 0x3ff); + bt->vfrontporch = hdmi_read16(sd, 0x2a, 0x1fff) / 2; + bt->vsync = hdmi_read16(sd, 0x2e, 0x1fff) / 2; + bt->vbackporch = hdmi_read16(sd, 0x32, 0x1fff) / 2; bt->polarities = ((hdmi_read(sd, 0x05) & 0x10) ? V4L2_DV_VSYNC_POS_POL : 0) | ((hdmi_read(sd, 0x05) & 0x20) ? V4L2_DV_HSYNC_POS_POL : 0); if (bt->interlaced == V4L2_DV_INTERLACED) { - bt->height += (hdmi_read(sd, 0x0b) & 0x0f) * 256 + - hdmi_read(sd, 0x0c); - bt->il_vfrontporch = ((hdmi_read(sd, 0x2c) & 0x1f) * 256 + - hdmi_read(sd, 0x2d)) / 2; - bt->il_vsync = ((hdmi_read(sd, 0x30) & 0x1f) * 256 + - hdmi_read(sd, 0x31)) / 2; - bt->vbackporch = ((hdmi_read(sd, 0x34) & 0x1f) * 256 + - hdmi_read(sd, 0x35)) / 2; + bt->height += hdmi_read16(sd, 0x0b, 0xfff); + bt->il_vfrontporch = hdmi_read16(sd, 0x2c, 0x1fff) / 2; + bt->il_vsync = hdmi_read16(sd, 0x30, 0x1fff) / 2; + bt->vbackporch = hdmi_read16(sd, 0x34, 0x1fff) / 2; } adv7604_fill_optional_dv_timings_fields(sd, timings); } else { @@ -1378,11 +1623,11 @@ static int adv7604_query_dv_timings(struct v4l2_subdev *sd, v4l2_dbg(1, debug, sd, "%s: restart STDI\n", __func__); /* TODO restart STDI for Sync Channel 2 */ /* enter one-shot mode */ - cp_write_and_or(sd, 0x86, 0xf9, 0x00); + cp_write_clr_set(sd, 0x86, 0x06, 0x00); /* trigger STDI restart */ - cp_write_and_or(sd, 0x86, 0xf9, 0x04); + cp_write_clr_set(sd, 0x86, 0x06, 0x04); /* reset to continuous mode */ - cp_write_and_or(sd, 0x86, 0xf9, 0x02); + cp_write_clr_set(sd, 0x86, 0x06, 0x02); state->restart_stdi_once = false; return -ENOLINK; } @@ -1441,7 +1686,7 @@ static int adv7604_s_dv_timings(struct v4l2_subdev *sd, state->timings = *timings; - cp_write(sd, 0x91, bt->interlaced ? 0x50 : 0x10); + cp_write_clr_set(sd, 0x91, 0x40, bt->interlaced ? 0x40 : 0x00); /* Use prim_mode and vid_std when available */ err = configure_predefined_video_timings(sd, timings); @@ -1468,6 +1713,16 @@ static int adv7604_g_dv_timings(struct v4l2_subdev *sd, return 0; } +static void adv7604_set_termination(struct v4l2_subdev *sd, bool enable) +{ + hdmi_write(sd, 0x01, enable ? 0x00 : 0x78); +} + +static void adv7611_set_termination(struct v4l2_subdev *sd, bool enable) +{ + hdmi_write(sd, 0x83, enable ? 0xfe : 0xff); +} + static void enable_input(struct v4l2_subdev *sd) { struct adv7604_state *state = to_state(sd); @@ -1475,10 +1730,10 @@ static void enable_input(struct v4l2_subdev *sd) if (is_analog_input(sd)) { io_write(sd, 0x15, 0xb0); /* Disable Tristate of Pins (no audio) */ } else if (is_digital_input(sd)) { - hdmi_write_and_or(sd, 0x00, 0xfc, state->selected_input); - hdmi_write(sd, 0x01, 0x00); /* Enable HDMI clock terminators */ + hdmi_write_clr_set(sd, 0x00, 0x03, state->selected_input); + state->info->set_termination(sd, true); io_write(sd, 0x15, 0xa0); /* Disable Tristate of Pins */ - hdmi_write_and_or(sd, 0x1a, 0xef, 0x00); /* Unmute audio */ + hdmi_write_clr_set(sd, 0x1a, 0x10, 0x00); /* Unmute audio */ } else { v4l2_dbg(2, debug, sd, "%s: Unknown port %d selected\n", __func__, state->selected_input); @@ -1487,67 +1742,36 @@ static void enable_input(struct v4l2_subdev *sd) static void disable_input(struct v4l2_subdev *sd) { - hdmi_write_and_or(sd, 0x1a, 0xef, 0x10); /* Mute audio */ + struct adv7604_state *state = to_state(sd); + + hdmi_write_clr_set(sd, 0x1a, 0x10, 0x10); /* Mute audio */ msleep(16); /* 512 samples with >= 32 kHz sample rate [REF_03, c. 7.16.10] */ io_write(sd, 0x15, 0xbe); /* Tristate all outputs from video core */ - hdmi_write(sd, 0x01, 0x78); /* Disable HDMI clock terminators */ + state->info->set_termination(sd, false); } static void select_input(struct v4l2_subdev *sd) { struct adv7604_state *state = to_state(sd); + const struct adv7604_chip_info *info = state->info; if (is_analog_input(sd)) { - /* reset ADI recommended settings for HDMI: */ - /* "ADV7604 Register Settings Recommendations (rev. 2.5, June 2010)" p. 4. */ - hdmi_write(sd, 0x0d, 0x04); /* HDMI filter optimization */ - hdmi_write(sd, 0x3d, 0x00); /* DDC bus active pull-up control */ - hdmi_write(sd, 0x3e, 0x74); /* TMDS PLL optimization */ - hdmi_write(sd, 0x4e, 0x3b); /* TMDS PLL optimization */ - hdmi_write(sd, 0x57, 0x74); /* TMDS PLL optimization */ - hdmi_write(sd, 0x58, 0x63); /* TMDS PLL optimization */ - hdmi_write(sd, 0x8d, 0x18); /* equaliser */ - hdmi_write(sd, 0x8e, 0x34); /* equaliser */ - hdmi_write(sd, 0x93, 0x88); /* equaliser */ - hdmi_write(sd, 0x94, 0x2e); /* equaliser */ - hdmi_write(sd, 0x96, 0x00); /* enable automatic EQ changing */ + adv7604_write_reg_seq(sd, info->recommended_settings[0]); afe_write(sd, 0x00, 0x08); /* power up ADC */ afe_write(sd, 0x01, 0x06); /* power up Analog Front End */ afe_write(sd, 0xc8, 0x00); /* phase control */ - - /* set ADI recommended settings for digitizer */ - /* "ADV7604 Register Settings Recommendations (rev. 2.5, June 2010)" p. 17. */ - afe_write(sd, 0x12, 0x7b); /* ADC noise shaping filter controls */ - afe_write(sd, 0x0c, 0x1f); /* CP core gain controls */ - cp_write(sd, 0x3e, 0x04); /* CP core pre-gain control */ - cp_write(sd, 0xc3, 0x39); /* CP coast control. Graphics mode */ - cp_write(sd, 0x40, 0x5c); /* CP core pre-gain control. Graphics mode */ } else if (is_digital_input(sd)) { hdmi_write(sd, 0x00, state->selected_input & 0x03); - /* set ADI recommended settings for HDMI: */ - /* "ADV7604 Register Settings Recommendations (rev. 2.5, June 2010)" p. 4. */ - hdmi_write(sd, 0x0d, 0x84); /* HDMI filter optimization */ - hdmi_write(sd, 0x3d, 0x10); /* DDC bus active pull-up control */ - hdmi_write(sd, 0x3e, 0x39); /* TMDS PLL optimization */ - hdmi_write(sd, 0x4e, 0x3b); /* TMDS PLL optimization */ - hdmi_write(sd, 0x57, 0xb6); /* TMDS PLL optimization */ - hdmi_write(sd, 0x58, 0x03); /* TMDS PLL optimization */ - hdmi_write(sd, 0x8d, 0x18); /* equaliser */ - hdmi_write(sd, 0x8e, 0x34); /* equaliser */ - hdmi_write(sd, 0x93, 0x8b); /* equaliser */ - hdmi_write(sd, 0x94, 0x2d); /* equaliser */ - hdmi_write(sd, 0x96, 0x01); /* enable automatic EQ changing */ - - afe_write(sd, 0x00, 0xff); /* power down ADC */ - afe_write(sd, 0x01, 0xfe); /* power down Analog Front End */ - afe_write(sd, 0xc8, 0x40); /* phase control */ - - /* reset ADI recommended settings for digitizer */ - /* "ADV7604 Register Settings Recommendations (rev. 2.5, June 2010)" p. 17. */ - afe_write(sd, 0x12, 0xfb); /* ADC noise shaping filter controls */ - afe_write(sd, 0x0c, 0x0d); /* CP core gain controls */ + adv7604_write_reg_seq(sd, info->recommended_settings[1]); + + if (adv7604_has_afe(state)) { + afe_write(sd, 0x00, 0xff); /* power down ADC */ + afe_write(sd, 0x01, 0xfe); /* power down Analog Front End */ + afe_write(sd, 0xc8, 0x40); /* phase control */ + } + cp_write(sd, 0x3e, 0x00); /* CP core pre-gain control */ cp_write(sd, 0xc3, 0x39); /* CP coast control. Graphics mode */ cp_write(sd, 0x40, 0x80); /* CP core pre-gain control. Graphics mode */ @@ -1568,6 +1792,9 @@ static int adv7604_s_routing(struct v4l2_subdev *sd, if (input == state->selected_input) return 0; + if (input > state->info->max_port) + return -EINVAL; + state->selected_input = input; disable_input(sd); @@ -1579,34 +1806,139 @@ static int adv7604_s_routing(struct v4l2_subdev *sd, return 0; } -static int adv7604_enum_mbus_fmt(struct v4l2_subdev *sd, unsigned int index, - enum v4l2_mbus_pixelcode *code) +static int adv7604_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_mbus_code_enum *code) { - if (index) + struct adv7604_state *state = to_state(sd); + + if (code->index >= state->info->nformats) return -EINVAL; - /* Good enough for now */ - *code = V4L2_MBUS_FMT_FIXED; + + code->code = state->info->formats[code->index].code; + return 0; } -static int adv7604_g_mbus_fmt(struct v4l2_subdev *sd, - struct v4l2_mbus_framefmt *fmt) +static void adv7604_fill_format(struct adv7604_state *state, + struct v4l2_mbus_framefmt *format) { - struct adv7604_state *state = to_state(sd); + memset(format, 0, sizeof(*format)); + + format->width = state->timings.bt.width; + format->height = state->timings.bt.height; + format->field = V4L2_FIELD_NONE; - fmt->width = state->timings.bt.width; - fmt->height = state->timings.bt.height; - fmt->code = V4L2_MBUS_FMT_FIXED; - fmt->field = V4L2_FIELD_NONE; - if (state->timings.bt.standards & V4L2_DV_BT_STD_CEA861) { - fmt->colorspace = (state->timings.bt.height <= 576) ? + if (state->timings.bt.standards & V4L2_DV_BT_STD_CEA861) + format->colorspace = (state->timings.bt.height <= 576) ? V4L2_COLORSPACE_SMPTE170M : V4L2_COLORSPACE_REC709; +} + +/* + * Compute the op_ch_sel value required to obtain on the bus the component order + * corresponding to the selected format taking into account bus reordering + * applied by the board at the output of the device. + * + * The following table gives the op_ch_value from the format component order + * (expressed as op_ch_sel value in column) and the bus reordering (expressed as + * adv7604_bus_order value in row). + * + * | GBR(0) GRB(1) BGR(2) RGB(3) BRG(4) RBG(5) + * ----------+------------------------------------------------- + * RGB (NOP) | GBR GRB BGR RGB BRG RBG + * GRB (1-2) | BGR RGB GBR GRB RBG BRG + * RBG (2-3) | GRB GBR BRG RBG BGR RGB + * BGR (1-3) | RBG BRG RGB BGR GRB GBR + * BRG (ROR) | BRG RBG GRB GBR RGB BGR + * GBR (ROL) | RGB BGR RBG BRG GBR GRB + */ +static unsigned int adv7604_op_ch_sel(struct adv7604_state *state) +{ +#define _SEL(a,b,c,d,e,f) { \ + ADV7604_OP_CH_SEL_##a, ADV7604_OP_CH_SEL_##b, ADV7604_OP_CH_SEL_##c, \ + ADV7604_OP_CH_SEL_##d, ADV7604_OP_CH_SEL_##e, ADV7604_OP_CH_SEL_##f } +#define _BUS(x) [ADV7604_BUS_ORDER_##x] + + static const unsigned int op_ch_sel[6][6] = { + _BUS(RGB) /* NOP */ = _SEL(GBR, GRB, BGR, RGB, BRG, RBG), + _BUS(GRB) /* 1-2 */ = _SEL(BGR, RGB, GBR, GRB, RBG, BRG), + _BUS(RBG) /* 2-3 */ = _SEL(GRB, GBR, BRG, RBG, BGR, RGB), + _BUS(BGR) /* 1-3 */ = _SEL(RBG, BRG, RGB, BGR, GRB, GBR), + _BUS(BRG) /* ROR */ = _SEL(BRG, RBG, GRB, GBR, RGB, BGR), + _BUS(GBR) /* ROL */ = _SEL(RGB, BGR, RBG, BRG, GBR, GRB), + }; + + return op_ch_sel[state->pdata.bus_order][state->format->op_ch_sel >> 5]; +} + +static void adv7604_setup_format(struct adv7604_state *state) +{ + struct v4l2_subdev *sd = &state->sd; + + io_write_clr_set(sd, 0x02, 0x02, + state->format->rgb_out ? ADV7604_RGB_OUT : 0); + io_write(sd, 0x03, state->format->op_format_sel | + state->pdata.op_format_mode_sel); + io_write_clr_set(sd, 0x04, 0xe0, adv7604_op_ch_sel(state)); + io_write_clr_set(sd, 0x05, 0x01, + state->format->swap_cb_cr ? ADV7604_OP_SWAP_CB_CR : 0); +} + +static int adv7604_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *format) +{ + struct adv7604_state *state = to_state(sd); + + if (format->pad != state->source_pad) + return -EINVAL; + + adv7604_fill_format(state, &format->format); + + if (format->which == V4L2_SUBDEV_FORMAT_TRY) { + struct v4l2_mbus_framefmt *fmt; + + fmt = v4l2_subdev_get_try_format(fh, format->pad); + format->format.code = fmt->code; + } else { + format->format.code = state->format->code; } + + return 0; +} + +static int adv7604_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *format) +{ + struct adv7604_state *state = to_state(sd); + const struct adv7604_format_info *info; + + if (format->pad != state->source_pad) + return -EINVAL; + + info = adv7604_format_info(state, format->format.code); + if (info == NULL) + info = adv7604_format_info(state, V4L2_MBUS_FMT_YUYV8_2X8); + + adv7604_fill_format(state, &format->format); + format->format.code = info->code; + + if (format->which == V4L2_SUBDEV_FORMAT_TRY) { + struct v4l2_mbus_framefmt *fmt; + + fmt = v4l2_subdev_get_try_format(fh, format->pad); + fmt->code = format->format.code; + } else { + state->format = info; + adv7604_setup_format(state); + } + return 0; } static int adv7604_isr(struct v4l2_subdev *sd, u32 status, bool *handled) { + struct adv7604_state *state = to_state(sd); + const struct adv7604_chip_info *info = state->info; const u8 irq_reg_0x43 = io_read(sd, 0x43); const u8 irq_reg_0x6b = io_read(sd, 0x6b); const u8 irq_reg_0x70 = io_read(sd, 0x70); @@ -1625,7 +1957,9 @@ static int adv7604_isr(struct v4l2_subdev *sd, u32 status, bool *handled) /* format change */ fmt_change = irq_reg_0x43 & 0x98; - fmt_change_digital = is_digital_input(sd) ? (irq_reg_0x6b & 0xc0) : 0; + fmt_change_digital = is_digital_input(sd) + ? irq_reg_0x6b & info->fmt_change_digital_mask + : 0; if (fmt_change || fmt_change_digital) { v4l2_dbg(1, debug, sd, @@ -1647,7 +1981,7 @@ static int adv7604_isr(struct v4l2_subdev *sd, u32 status, bool *handled) } /* tx 5v detect */ - tx_5v = io_read(sd, 0x70) & 0x1e; + tx_5v = io_read(sd, 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); @@ -1663,7 +1997,7 @@ static int adv7604_get_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid) struct adv7604_state *state = to_state(sd); u8 *data = NULL; - if (edid->pad > ADV7604_EDID_PORT_D) + if (edid->pad > ADV7604_PAD_HDMI_PORT_D) return -EINVAL; if (edid->blocks == 0) return -EINVAL; @@ -1673,17 +2007,15 @@ static int adv7604_get_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid) return -EINVAL; if (edid->start_block == 1) edid->blocks = 1; - if (!edid->edid) - return -EINVAL; if (edid->blocks > state->edid.blocks) edid->blocks = state->edid.blocks; switch (edid->pad) { - case ADV7604_EDID_PORT_A: - case ADV7604_EDID_PORT_B: - case ADV7604_EDID_PORT_C: - case ADV7604_EDID_PORT_D: + case ADV7604_PAD_HDMI_PORT_A: + case ADV7604_PAD_HDMI_PORT_B: + case ADV7604_PAD_HDMI_PORT_C: + case ADV7604_PAD_HDMI_PORT_D: if (state->edid.present & (1 << edid->pad)) data = state->edid.edid; break; @@ -1731,20 +2063,20 @@ static int get_edid_spa_location(const u8 *edid) static int adv7604_set_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid) { struct adv7604_state *state = to_state(sd); + const struct adv7604_chip_info *info = state->info; int spa_loc; - int tmp = 0; int err; int i; - if (edid->pad > ADV7604_EDID_PORT_D) + if (edid->pad > ADV7604_PAD_HDMI_PORT_D) return -EINVAL; if (edid->start_block != 0) return -EINVAL; if (edid->blocks == 0) { /* Disable hotplug and I2C access to EDID RAM from DDC port */ state->edid.present &= ~(1 << edid->pad); - v4l2_subdev_notify(sd, ADV7604_HOTPLUG, (void *)&state->edid.present); - rep_write_and_or(sd, 0x77, 0xf0, state->edid.present); + adv7604_set_hpd(state, state->edid.present); + rep_write_clr_set(sd, info->edid_enable_reg, 0x0f, state->edid.present); /* Fall back to a 16:9 aspect ratio */ state->aspect_ratio.numerator = 16; @@ -1761,43 +2093,47 @@ static int adv7604_set_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid) edid->blocks = 2; return -E2BIG; } - if (!edid->edid) - return -EINVAL; v4l2_dbg(2, debug, sd, "%s: write EDID pad %d, edid.present = 0x%x\n", __func__, edid->pad, state->edid.present); /* Disable hotplug and I2C access to EDID RAM from DDC port */ cancel_delayed_work_sync(&state->delayed_work_enable_hotplug); - v4l2_subdev_notify(sd, ADV7604_HOTPLUG, (void *)&tmp); - rep_write_and_or(sd, 0x77, 0xf0, 0x00); + adv7604_set_hpd(state, 0); + rep_write_clr_set(sd, info->edid_enable_reg, 0x0f, 0x00); spa_loc = get_edid_spa_location(edid->edid); if (spa_loc < 0) spa_loc = 0xc0; /* Default value [REF_02, p. 116] */ switch (edid->pad) { - case ADV7604_EDID_PORT_A: + case ADV7604_PAD_HDMI_PORT_A: state->spa_port_a[0] = edid->edid[spa_loc]; state->spa_port_a[1] = edid->edid[spa_loc + 1]; break; - case ADV7604_EDID_PORT_B: + case ADV7604_PAD_HDMI_PORT_B: rep_write(sd, 0x70, edid->edid[spa_loc]); rep_write(sd, 0x71, edid->edid[spa_loc + 1]); break; - case ADV7604_EDID_PORT_C: + case ADV7604_PAD_HDMI_PORT_C: rep_write(sd, 0x72, edid->edid[spa_loc]); rep_write(sd, 0x73, edid->edid[spa_loc + 1]); break; - case ADV7604_EDID_PORT_D: + case ADV7604_PAD_HDMI_PORT_D: rep_write(sd, 0x74, edid->edid[spa_loc]); rep_write(sd, 0x75, edid->edid[spa_loc + 1]); break; default: return -EINVAL; } - rep_write(sd, 0x76, spa_loc & 0xff); - rep_write_and_or(sd, 0x77, 0xbf, (spa_loc >> 2) & 0x40); + + if (info->type == ADV7604) { + 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 ? */ + rep_write_clr_set(sd, 0x71, 0x01, (spa_loc & 0x100) >> 8); + } edid->edid[spa_loc] = state->spa_port_a[0]; edid->edid[spa_loc + 1] = state->spa_port_a[1]; @@ -1816,10 +2152,10 @@ static int adv7604_set_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid) /* adv7604 calculates the checksums and enables I2C access to internal EDID RAM from DDC port. */ - rep_write_and_or(sd, 0x77, 0xf0, state->edid.present); + rep_write_clr_set(sd, info->edid_enable_reg, 0x0f, state->edid.present); for (i = 0; i < 1000; i++) { - if (rep_read(sd, 0x7d) & state->edid.present) + if (rep_read(sd, info->edid_status_reg) & state->edid.present) break; mdelay(1); } @@ -1882,17 +2218,20 @@ static void print_avi_infoframe(struct v4l2_subdev *sd) static int adv7604_log_status(struct v4l2_subdev *sd) { struct adv7604_state *state = to_state(sd); + const struct adv7604_chip_info *info = state->info; struct v4l2_dv_timings timings; struct stdi_readback stdi; u8 reg_io_0x02 = io_read(sd, 0x02); + u8 edid_enabled; + u8 cable_det; - char *csc_coeff_sel_rb[16] = { + static const char * const csc_coeff_sel_rb[16] = { "bypassed", "YPbPr601 -> RGB", "reserved", "YPbPr709 -> RGB", "reserved", "RGB -> YPbPr601", "reserved", "RGB -> YPbPr709", "reserved", "YPbPr709 -> YPbPr601", "YPbPr601 -> YPbPr709", "reserved", "reserved", "reserved", "reserved", "manual" }; - char *input_color_space_txt[16] = { + static const char * const input_color_space_txt[16] = { "RGB limited range (16-235)", "RGB full range (0-255)", "YCbCr Bt.601 (16-235)", "YCbCr Bt.709 (16-235)", "xvYCC Bt.601", "xvYCC Bt.709", @@ -1900,12 +2239,12 @@ static int adv7604_log_status(struct v4l2_subdev *sd) "invalid", "invalid", "invalid", "invalid", "invalid", "invalid", "invalid", "automatic" }; - char *rgb_quantization_range_txt[] = { + static const char * const rgb_quantization_range_txt[] = { "Automatic", "RGB limited range (16-235)", "RGB full range (0-255)", }; - char *deep_color_mode_txt[4] = { + static const char * const deep_color_mode_txt[4] = { "8-bits per channel", "10-bits per channel", "12-bits per channel", @@ -1914,20 +2253,22 @@ static int adv7604_log_status(struct v4l2_subdev *sd) v4l2_info(sd, "-----Chip status-----\n"); v4l2_info(sd, "Chip power: %s\n", no_power(sd) ? "off" : "on"); + edid_enabled = rep_read(sd, info->edid_status_reg); v4l2_info(sd, "EDID enabled port A: %s, B: %s, C: %s, D: %s\n", - ((rep_read(sd, 0x7d) & 0x01) ? "Yes" : "No"), - ((rep_read(sd, 0x7d) & 0x02) ? "Yes" : "No"), - ((rep_read(sd, 0x7d) & 0x04) ? "Yes" : "No"), - ((rep_read(sd, 0x7d) & 0x08) ? "Yes" : "No")); + ((edid_enabled & 0x01) ? "Yes" : "No"), + ((edid_enabled & 0x02) ? "Yes" : "No"), + ((edid_enabled & 0x04) ? "Yes" : "No"), + ((edid_enabled & 0x08) ? "Yes" : "No")); v4l2_info(sd, "CEC: %s\n", !!(cec_read(sd, 0x2a) & 0x01) ? "enabled" : "disabled"); v4l2_info(sd, "-----Signal status-----\n"); + cable_det = info->read_cable_det(sd); v4l2_info(sd, "Cable detected (+5V power) port A: %s, B: %s, C: %s, D: %s\n", - ((io_read(sd, 0x6f) & 0x10) ? "Yes" : "No"), - ((io_read(sd, 0x6f) & 0x08) ? "Yes" : "No"), - ((io_read(sd, 0x6f) & 0x04) ? "Yes" : "No"), - ((io_read(sd, 0x6f) & 0x02) ? "Yes" : "No")); + ((cable_det & 0x01) ? "Yes" : "No"), + ((cable_det & 0x02) ? "Yes" : "No"), + ((cable_det & 0x04) ? "Yes" : "No"), + ((cable_det & 0x08) ? "Yes" : "No")); v4l2_info(sd, "TMDS signal detected: %s\n", no_signal_tmds(sd) ? "false" : "true"); v4l2_info(sd, "TMDS signal locked: %s\n", @@ -2021,13 +2362,6 @@ static const struct v4l2_ctrl_ops adv7604_ctrl_ops = { static const struct v4l2_subdev_core_ops adv7604_core_ops = { .log_status = adv7604_log_status, - .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, - .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, - .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, - .g_ctrl = v4l2_subdev_g_ctrl, - .s_ctrl = v4l2_subdev_s_ctrl, - .queryctrl = v4l2_subdev_queryctrl, - .querymenu = v4l2_subdev_querymenu, .interrupt_service_routine = adv7604_isr, #ifdef CONFIG_VIDEO_ADV_DEBUG .g_register = adv7604_g_register, @@ -2041,17 +2375,16 @@ static const struct v4l2_subdev_video_ops adv7604_video_ops = { .s_dv_timings = adv7604_s_dv_timings, .g_dv_timings = adv7604_g_dv_timings, .query_dv_timings = adv7604_query_dv_timings, - .enum_dv_timings = adv7604_enum_dv_timings, - .dv_timings_cap = adv7604_dv_timings_cap, - .enum_mbus_fmt = adv7604_enum_mbus_fmt, - .g_mbus_fmt = adv7604_g_mbus_fmt, - .try_mbus_fmt = adv7604_g_mbus_fmt, - .s_mbus_fmt = adv7604_g_mbus_fmt, }; static const struct v4l2_subdev_pad_ops adv7604_pad_ops = { + .enum_mbus_code = adv7604_enum_mbus_code, + .get_fmt = adv7604_get_format, + .set_fmt = adv7604_set_format, .get_edid = adv7604_get_edid, .set_edid = adv7604_set_edid, + .dv_timings_cap = adv7604_dv_timings_cap, + .enum_dv_timings = adv7604_enum_dv_timings, }; static const struct v4l2_subdev_ops adv7604_ops = { @@ -2100,6 +2433,7 @@ static const struct v4l2_ctrl_config adv7604_ctrl_free_run_color = { static int adv7604_core_init(struct v4l2_subdev *sd) { struct adv7604_state *state = to_state(sd); + const struct adv7604_chip_info *info = state->info; struct adv7604_platform_data *pdata = &state->pdata; hdmi_write(sd, 0x48, @@ -2108,28 +2442,33 @@ static int adv7604_core_init(struct v4l2_subdev *sd) disable_input(sd); + if (pdata->default_input >= 0 && + pdata->default_input < state->source_pad) { + state->selected_input = pdata->default_input; + select_input(sd); + enable_input(sd); + } + /* power */ io_write(sd, 0x0c, 0x42); /* Power up part and power down VDP */ io_write(sd, 0x0b, 0x44); /* Power down ESDP block */ cp_write(sd, 0xcf, 0x01); /* Power down macrovision */ /* video format */ - io_write_and_or(sd, 0x02, 0xf0, + io_write_clr_set(sd, 0x02, 0x0f, pdata->alt_gamma << 3 | pdata->op_656_range << 2 | - pdata->rgb_out << 1 | pdata->alt_data_sat << 0); - io_write(sd, 0x03, pdata->op_format_sel); - io_write_and_or(sd, 0x04, 0x1f, pdata->op_ch_sel << 5); - io_write_and_or(sd, 0x05, 0xf0, pdata->blank_data << 3 | - pdata->insert_av_codes << 2 | - pdata->replicate_av_codes << 1 | - pdata->invert_cbcr << 0); + io_write_clr_set(sd, 0x05, 0x0e, pdata->blank_data << 3 | + pdata->insert_av_codes << 2 | + pdata->replicate_av_codes << 1); + adv7604_setup_format(state); cp_write(sd, 0x69, 0x30); /* Enable CP CSC */ /* VS, HS polarities */ - io_write(sd, 0x06, 0xa0 | pdata->inv_vs_pol << 2 | pdata->inv_hs_pol << 1); + io_write(sd, 0x06, 0xa0 | pdata->inv_vs_pol << 2 | + pdata->inv_hs_pol << 1 | pdata->inv_llc_pol); /* Adjust drive strength */ io_write(sd, 0x14, 0x40 | pdata->dr_str_data << 4 | @@ -2146,52 +2485,46 @@ static int adv7604_core_init(struct v4l2_subdev *sd) for digital formats */ /* HDMI audio */ - hdmi_write_and_or(sd, 0x15, 0xfc, 0x03); /* Mute on FIFO over-/underflow [REF_01, c. 1.2.18] */ - hdmi_write_and_or(sd, 0x1a, 0xf1, 0x08); /* Wait 1 s before unmute */ - hdmi_write_and_or(sd, 0x68, 0xf9, 0x06); /* FIFO reset on over-/underflow [REF_01, c. 1.2.19] */ + hdmi_write_clr_set(sd, 0x15, 0x03, 0x03); /* Mute on FIFO over-/underflow [REF_01, c. 1.2.18] */ + hdmi_write_clr_set(sd, 0x1a, 0x0e, 0x08); /* Wait 1 s before unmute */ + hdmi_write_clr_set(sd, 0x68, 0x06, 0x06); /* FIFO reset on over-/underflow [REF_01, c. 1.2.19] */ /* TODO from platform data */ afe_write(sd, 0xb5, 0x01); /* Setting MCLK to 256Fs */ - afe_write(sd, 0x02, pdata->ain_sel); /* Select analog input muxing mode */ - io_write_and_or(sd, 0x30, ~(1 << 4), pdata->output_bus_lsb_to_msb << 4); + if (adv7604_has_afe(state)) { + afe_write(sd, 0x02, pdata->ain_sel); /* Select analog input muxing mode */ + io_write_clr_set(sd, 0x30, 1 << 4, pdata->output_bus_lsb_to_msb << 4); + } /* interrupts */ - io_write(sd, 0x40, 0xc2); /* Configure INT1 */ - io_write(sd, 0x41, 0xd7); /* STDI irq for any change, disable INT2 */ + io_write(sd, 0x40, 0xc0 | pdata->int1_config); /* Configure INT1 */ io_write(sd, 0x46, 0x98); /* Enable SSPD, STDI and CP unlocked interrupts */ - io_write(sd, 0x6e, 0xc1); /* Enable V_LOCKED, DE_REGEN_LCK, HDMI_MODE interrupts */ - io_write(sd, 0x73, 0x1e); /* Enable CABLE_DET_A_ST (+5v) interrupts */ + io_write(sd, 0x6e, info->fmt_change_digital_mask); /* Enable V_LOCKED and DE_REGEN_LCK interrupts */ + io_write(sd, 0x73, info->cable_det_mask); /* Enable cable detection (+5v) interrupts */ + info->setup_irqs(sd); return v4l2_ctrl_handler_setup(sd->ctrl_handler); } +static void adv7604_setup_irqs(struct v4l2_subdev *sd) +{ + io_write(sd, 0x41, 0xd7); /* STDI irq for any change, disable INT2 */ +} + +static void adv7611_setup_irqs(struct v4l2_subdev *sd) +{ + io_write(sd, 0x41, 0xd0); /* STDI irq for any change, disable INT2 */ +} + static void adv7604_unregister_clients(struct adv7604_state *state) { - if (state->i2c_avlink) - i2c_unregister_device(state->i2c_avlink); - if (state->i2c_cec) - i2c_unregister_device(state->i2c_cec); - if (state->i2c_infoframe) - i2c_unregister_device(state->i2c_infoframe); - if (state->i2c_esdp) - i2c_unregister_device(state->i2c_esdp); - if (state->i2c_dpp) - i2c_unregister_device(state->i2c_dpp); - if (state->i2c_afe) - i2c_unregister_device(state->i2c_afe); - if (state->i2c_repeater) - i2c_unregister_device(state->i2c_repeater); - if (state->i2c_edid) - i2c_unregister_device(state->i2c_edid); - if (state->i2c_hdmi) - i2c_unregister_device(state->i2c_hdmi); - if (state->i2c_test) - i2c_unregister_device(state->i2c_test); - if (state->i2c_cp) - i2c_unregister_device(state->i2c_cp); - if (state->i2c_vdp) - i2c_unregister_device(state->i2c_vdp); + unsigned int i; + + for (i = 1; i < ARRAY_SIZE(state->i2c_clients); ++i) { + if (state->i2c_clients[i]) + i2c_unregister_device(state->i2c_clients[i]); + } } static struct i2c_client *adv7604_dummy_client(struct v4l2_subdev *sd, @@ -2204,15 +2537,219 @@ static struct i2c_client *adv7604_dummy_client(struct v4l2_subdev *sd, return i2c_new_dummy(client->adapter, io_read(sd, io_reg) >> 1); } +static const struct adv7604_reg_seq adv7604_recommended_settings_afe[] = { + /* reset ADI recommended settings for HDMI: */ + /* "ADV7604 Register Settings Recommendations (rev. 2.5, June 2010)" p. 4. */ + { ADV7604_REG(ADV7604_PAGE_HDMI, 0x0d), 0x04 }, /* HDMI filter optimization */ + { ADV7604_REG(ADV7604_PAGE_HDMI, 0x0d), 0x04 }, /* HDMI filter optimization */ + { ADV7604_REG(ADV7604_PAGE_HDMI, 0x3d), 0x00 }, /* DDC bus active pull-up control */ + { ADV7604_REG(ADV7604_PAGE_HDMI, 0x3e), 0x74 }, /* TMDS PLL optimization */ + { ADV7604_REG(ADV7604_PAGE_HDMI, 0x4e), 0x3b }, /* TMDS PLL optimization */ + { ADV7604_REG(ADV7604_PAGE_HDMI, 0x57), 0x74 }, /* TMDS PLL optimization */ + { ADV7604_REG(ADV7604_PAGE_HDMI, 0x58), 0x63 }, /* TMDS PLL optimization */ + { ADV7604_REG(ADV7604_PAGE_HDMI, 0x8d), 0x18 }, /* equaliser */ + { ADV7604_REG(ADV7604_PAGE_HDMI, 0x8e), 0x34 }, /* equaliser */ + { ADV7604_REG(ADV7604_PAGE_HDMI, 0x93), 0x88 }, /* equaliser */ + { ADV7604_REG(ADV7604_PAGE_HDMI, 0x94), 0x2e }, /* equaliser */ + { ADV7604_REG(ADV7604_PAGE_HDMI, 0x96), 0x00 }, /* enable automatic EQ changing */ + + /* set ADI recommended settings for digitizer */ + /* "ADV7604 Register Settings Recommendations (rev. 2.5, June 2010)" p. 17. */ + { ADV7604_REG(ADV7604_PAGE_AFE, 0x12), 0x7b }, /* ADC noise shaping filter controls */ + { ADV7604_REG(ADV7604_PAGE_AFE, 0x0c), 0x1f }, /* CP core gain controls */ + { ADV7604_REG(ADV7604_PAGE_CP, 0x3e), 0x04 }, /* CP core pre-gain control */ + { ADV7604_REG(ADV7604_PAGE_CP, 0xc3), 0x39 }, /* CP coast control. Graphics mode */ + { ADV7604_REG(ADV7604_PAGE_CP, 0x40), 0x5c }, /* CP core pre-gain control. Graphics mode */ + + { ADV7604_REG_SEQ_TERM, 0 }, +}; + +static const struct adv7604_reg_seq adv7604_recommended_settings_hdmi[] = { + /* set ADI recommended settings for HDMI: */ + /* "ADV7604 Register Settings Recommendations (rev. 2.5, June 2010)" p. 4. */ + { ADV7604_REG(ADV7604_PAGE_HDMI, 0x0d), 0x84 }, /* HDMI filter optimization */ + { ADV7604_REG(ADV7604_PAGE_HDMI, 0x3d), 0x10 }, /* DDC bus active pull-up control */ + { ADV7604_REG(ADV7604_PAGE_HDMI, 0x3e), 0x39 }, /* TMDS PLL optimization */ + { ADV7604_REG(ADV7604_PAGE_HDMI, 0x4e), 0x3b }, /* TMDS PLL optimization */ + { ADV7604_REG(ADV7604_PAGE_HDMI, 0x57), 0xb6 }, /* TMDS PLL optimization */ + { ADV7604_REG(ADV7604_PAGE_HDMI, 0x58), 0x03 }, /* TMDS PLL optimization */ + { ADV7604_REG(ADV7604_PAGE_HDMI, 0x8d), 0x18 }, /* equaliser */ + { ADV7604_REG(ADV7604_PAGE_HDMI, 0x8e), 0x34 }, /* equaliser */ + { ADV7604_REG(ADV7604_PAGE_HDMI, 0x93), 0x8b }, /* equaliser */ + { ADV7604_REG(ADV7604_PAGE_HDMI, 0x94), 0x2d }, /* equaliser */ + { ADV7604_REG(ADV7604_PAGE_HDMI, 0x96), 0x01 }, /* enable automatic EQ changing */ + + /* reset ADI recommended settings for digitizer */ + /* "ADV7604 Register Settings Recommendations (rev. 2.5, June 2010)" p. 17. */ + { ADV7604_REG(ADV7604_PAGE_AFE, 0x12), 0xfb }, /* ADC noise shaping filter controls */ + { ADV7604_REG(ADV7604_PAGE_AFE, 0x0c), 0x0d }, /* CP core gain controls */ + + { ADV7604_REG_SEQ_TERM, 0 }, +}; + +static const struct adv7604_reg_seq adv7611_recommended_settings_hdmi[] = { + { ADV7604_REG(ADV7604_PAGE_CP, 0x6c), 0x00 }, + { ADV7604_REG(ADV7604_PAGE_HDMI, 0x6f), 0x0c }, + { ADV7604_REG(ADV7604_PAGE_HDMI, 0x87), 0x70 }, + { ADV7604_REG(ADV7604_PAGE_HDMI, 0x57), 0xda }, + { ADV7604_REG(ADV7604_PAGE_HDMI, 0x58), 0x01 }, + { ADV7604_REG(ADV7604_PAGE_HDMI, 0x03), 0x98 }, + { ADV7604_REG(ADV7604_PAGE_HDMI, 0x4c), 0x44 }, + { ADV7604_REG(ADV7604_PAGE_HDMI, 0x8d), 0x04 }, + { ADV7604_REG(ADV7604_PAGE_HDMI, 0x8e), 0x1e }, + + { ADV7604_REG_SEQ_TERM, 0 }, +}; + +static const struct adv7604_chip_info adv7604_chip_info[] = { + [ADV7604] = { + .type = ADV7604, + .has_afe = true, + .max_port = ADV7604_PAD_VGA_COMP, + .num_dv_ports = 4, + .edid_enable_reg = 0x77, + .edid_status_reg = 0x7d, + .lcf_reg = 0xb3, + .tdms_lock_mask = 0xe0, + .cable_det_mask = 0x1e, + .fmt_change_digital_mask = 0xc1, + .formats = adv7604_formats, + .nformats = ARRAY_SIZE(adv7604_formats), + .set_termination = adv7604_set_termination, + .setup_irqs = adv7604_setup_irqs, + .read_hdmi_pixelclock = adv7604_read_hdmi_pixelclock, + .read_cable_det = adv7604_read_cable_det, + .recommended_settings = { + [0] = adv7604_recommended_settings_afe, + [1] = adv7604_recommended_settings_hdmi, + }, + .num_recommended_settings = { + [0] = ARRAY_SIZE(adv7604_recommended_settings_afe), + [1] = ARRAY_SIZE(adv7604_recommended_settings_hdmi), + }, + .page_mask = BIT(ADV7604_PAGE_IO) | BIT(ADV7604_PAGE_AVLINK) | + BIT(ADV7604_PAGE_CEC) | BIT(ADV7604_PAGE_INFOFRAME) | + BIT(ADV7604_PAGE_ESDP) | BIT(ADV7604_PAGE_DPP) | + BIT(ADV7604_PAGE_AFE) | BIT(ADV7604_PAGE_REP) | + BIT(ADV7604_PAGE_EDID) | BIT(ADV7604_PAGE_HDMI) | + BIT(ADV7604_PAGE_TEST) | BIT(ADV7604_PAGE_CP) | + BIT(ADV7604_PAGE_VDP), + }, + [ADV7611] = { + .type = ADV7611, + .has_afe = false, + .max_port = ADV7604_PAD_HDMI_PORT_A, + .num_dv_ports = 1, + .edid_enable_reg = 0x74, + .edid_status_reg = 0x76, + .lcf_reg = 0xa3, + .tdms_lock_mask = 0x43, + .cable_det_mask = 0x01, + .fmt_change_digital_mask = 0x03, + .formats = adv7611_formats, + .nformats = ARRAY_SIZE(adv7611_formats), + .set_termination = adv7611_set_termination, + .setup_irqs = adv7611_setup_irqs, + .read_hdmi_pixelclock = adv7611_read_hdmi_pixelclock, + .read_cable_det = adv7611_read_cable_det, + .recommended_settings = { + [1] = adv7611_recommended_settings_hdmi, + }, + .num_recommended_settings = { + [1] = ARRAY_SIZE(adv7611_recommended_settings_hdmi), + }, + .page_mask = BIT(ADV7604_PAGE_IO) | BIT(ADV7604_PAGE_CEC) | + BIT(ADV7604_PAGE_INFOFRAME) | BIT(ADV7604_PAGE_AFE) | + BIT(ADV7604_PAGE_REP) | BIT(ADV7604_PAGE_EDID) | + BIT(ADV7604_PAGE_HDMI) | BIT(ADV7604_PAGE_CP), + }, +}; + +static struct i2c_device_id adv7604_i2c_id[] = { + { "adv7604", (kernel_ulong_t)&adv7604_chip_info[ADV7604] }, + { "adv7611", (kernel_ulong_t)&adv7604_chip_info[ADV7611] }, + { } +}; +MODULE_DEVICE_TABLE(i2c, adv7604_i2c_id); + +static struct of_device_id adv7604_of_id[] __maybe_unused = { + { .compatible = "adi,adv7611", .data = &adv7604_chip_info[ADV7611] }, + { } +}; +MODULE_DEVICE_TABLE(of, adv7604_of_id); + +static int adv7604_parse_dt(struct adv7604_state *state) +{ + struct v4l2_of_endpoint bus_cfg; + struct device_node *endpoint; + struct device_node *np; + unsigned int flags; + + np = state->i2c_clients[ADV7604_PAGE_IO]->dev.of_node; + + /* Parse the endpoint. */ + endpoint = of_graph_get_next_endpoint(np, NULL); + if (!endpoint) + return -EINVAL; + + v4l2_of_parse_endpoint(endpoint, &bus_cfg); + of_node_put(endpoint); + + flags = bus_cfg.bus.parallel.flags; + + if (flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH) + state->pdata.inv_hs_pol = 1; + + if (flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH) + state->pdata.inv_vs_pol = 1; + + if (flags & V4L2_MBUS_PCLK_SAMPLE_RISING) + state->pdata.inv_llc_pol = 1; + + if (bus_cfg.bus_type == V4L2_MBUS_BT656) { + state->pdata.insert_av_codes = 1; + state->pdata.op_656_range = 1; + } + + /* Disable the interrupt for now as no DT-based board uses it. */ + state->pdata.int1_config = ADV7604_INT1_CONFIG_DISABLED; + + /* Use the default I2C addresses. */ + state->pdata.i2c_addresses[ADV7604_PAGE_AVLINK] = 0x42; + state->pdata.i2c_addresses[ADV7604_PAGE_CEC] = 0x40; + state->pdata.i2c_addresses[ADV7604_PAGE_INFOFRAME] = 0x3e; + state->pdata.i2c_addresses[ADV7604_PAGE_ESDP] = 0x38; + state->pdata.i2c_addresses[ADV7604_PAGE_DPP] = 0x3c; + state->pdata.i2c_addresses[ADV7604_PAGE_AFE] = 0x26; + state->pdata.i2c_addresses[ADV7604_PAGE_REP] = 0x32; + state->pdata.i2c_addresses[ADV7604_PAGE_EDID] = 0x36; + state->pdata.i2c_addresses[ADV7604_PAGE_HDMI] = 0x34; + state->pdata.i2c_addresses[ADV7604_PAGE_TEST] = 0x30; + state->pdata.i2c_addresses[ADV7604_PAGE_CP] = 0x22; + state->pdata.i2c_addresses[ADV7604_PAGE_VDP] = 0x24; + + /* Hardcode the remaining platform data fields. */ + state->pdata.disable_pwrdnb = 0; + state->pdata.disable_cable_det_rst = 0; + state->pdata.default_input = -1; + state->pdata.blank_data = 1; + state->pdata.alt_data_sat = 1; + state->pdata.op_format_mode_sel = ADV7604_OP_FORMAT_MODE0; + state->pdata.bus_order = ADV7604_BUS_ORDER_RGB; + + return 0; +} + static int adv7604_probe(struct i2c_client *client, const struct i2c_device_id *id) { static const struct v4l2_dv_timings cea640x480 = V4L2_DV_BT_CEA_640X480P59_94; struct adv7604_state *state; - struct adv7604_platform_data *pdata = client->dev.platform_data; struct v4l2_ctrl_handler *hdl; struct v4l2_subdev *sd; + unsigned int i; + u16 val; int err; /* Check if the adapter supports the needed features */ @@ -2227,32 +2764,80 @@ static int adv7604_probe(struct i2c_client *client, return -ENOMEM; } + state->i2c_clients[ADV7604_PAGE_IO] = client; + /* initialize variables */ state->restart_stdi_once = true; state->selected_input = ~0; - /* platform data */ - if (!pdata) { + if (IS_ENABLED(CONFIG_OF) && client->dev.of_node) { + const struct of_device_id *oid; + + oid = of_match_node(adv7604_of_id, client->dev.of_node); + state->info = oid->data; + + err = adv7604_parse_dt(state); + if (err < 0) { + v4l_err(client, "DT parsing error\n"); + return err; + } + } else if (client->dev.platform_data) { + struct adv7604_platform_data *pdata = client->dev.platform_data; + + state->info = (const struct adv7604_chip_info *)id->driver_data; + state->pdata = *pdata; + } else { v4l_err(client, "No platform data!\n"); return -ENODEV; } - state->pdata = *pdata; + + /* Request GPIOs. */ + for (i = 0; i < state->info->num_dv_ports; ++i) { + state->hpd_gpio[i] = + devm_gpiod_get_index(&client->dev, "hpd", i); + if (IS_ERR(state->hpd_gpio[i])) + continue; + + gpiod_direction_output(state->hpd_gpio[i], 0); + + v4l_info(client, "Handling HPD %u GPIO\n", i); + } + state->timings = cea640x480; + state->format = adv7604_format_info(state, V4L2_MBUS_FMT_YUYV8_2X8); sd = &state->sd; v4l2_i2c_subdev_init(sd, client, &adv7604_ops); + snprintf(sd->name, sizeof(sd->name), "%s %d-%04x", + id->name, i2c_adapter_id(client->adapter), + client->addr); sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; - /* i2c access to adv7604? */ - if (adv_smbus_read_byte_data_check(client, 0xfb, false) != 0x68) { - v4l2_info(sd, "not an adv7604 on address 0x%x\n", - client->addr << 1); - return -ENODEV; + /* + * Verify that the chip is present. On ADV7604 the RD_INFO register only + * identifies the revision, while on ADV7611 it identifies the model as + * well. Use the HDMI slave address on ADV7604 and RD_INFO on ADV7611. + */ + if (state->info->type == ADV7604) { + val = adv_smbus_read_byte_data_check(client, 0xfb, false); + if (val != 0x68) { + v4l2_info(sd, "not an adv7604 on address 0x%x\n", + client->addr << 1); + return -ENODEV; + } + } else { + val = (adv_smbus_read_byte_data_check(client, 0xea, false) << 8) + | (adv_smbus_read_byte_data_check(client, 0xeb, false) << 0); + if (val != 0x2051) { + v4l2_info(sd, "not an adv7611 on address 0x%x\n", + client->addr << 1); + return -ENODEV; + } } /* control handlers */ hdl = &state->hdl; - v4l2_ctrl_handler_init(hdl, 9); + v4l2_ctrl_handler_init(hdl, adv7604_has_afe(state) ? 9 : 8); v4l2_ctrl_new_std(hdl, &adv7604_ctrl_ops, V4L2_CID_BRIGHTNESS, -128, 127, 1, 0); @@ -2265,15 +2850,17 @@ static int adv7604_probe(struct i2c_client *client, /* private controls */ state->detect_tx_5v_ctrl = v4l2_ctrl_new_std(hdl, NULL, - V4L2_CID_DV_RX_POWER_PRESENT, 0, 0x0f, 0, 0); + V4L2_CID_DV_RX_POWER_PRESENT, 0, + (1 << state->info->num_dv_ports) - 1, 0, 0); state->rgb_quantization_range_ctrl = v4l2_ctrl_new_std_menu(hdl, &adv7604_ctrl_ops, V4L2_CID_DV_RX_RGB_RANGE, V4L2_DV_RGB_RANGE_FULL, 0, V4L2_DV_RGB_RANGE_AUTO); /* custom controls */ - state->analog_sampling_phase_ctrl = - v4l2_ctrl_new_custom(hdl, &adv7604_ctrl_analog_sampling_phase, NULL); + if (adv7604_has_afe(state)) + state->analog_sampling_phase_ctrl = + v4l2_ctrl_new_custom(hdl, &adv7604_ctrl_analog_sampling_phase, NULL); state->free_run_color_manual_ctrl = v4l2_ctrl_new_custom(hdl, &adv7604_ctrl_free_run_color_manual, NULL); state->free_run_color_ctrl = @@ -2286,7 +2873,8 @@ static int adv7604_probe(struct i2c_client *client, } state->detect_tx_5v_ctrl->is_private = true; state->rgb_quantization_range_ctrl->is_private = true; - state->analog_sampling_phase_ctrl->is_private = true; + if (adv7604_has_afe(state)) + state->analog_sampling_phase_ctrl->is_private = true; state->free_run_color_manual_ctrl->is_private = true; state->free_run_color_ctrl->is_private = true; @@ -2295,25 +2883,18 @@ static int adv7604_probe(struct i2c_client *client, goto err_hdl; } - state->i2c_avlink = adv7604_dummy_client(sd, pdata->i2c_avlink, 0xf3); - state->i2c_cec = adv7604_dummy_client(sd, pdata->i2c_cec, 0xf4); - state->i2c_infoframe = adv7604_dummy_client(sd, pdata->i2c_infoframe, 0xf5); - state->i2c_esdp = adv7604_dummy_client(sd, pdata->i2c_esdp, 0xf6); - state->i2c_dpp = adv7604_dummy_client(sd, pdata->i2c_dpp, 0xf7); - state->i2c_afe = adv7604_dummy_client(sd, pdata->i2c_afe, 0xf8); - state->i2c_repeater = adv7604_dummy_client(sd, pdata->i2c_repeater, 0xf9); - state->i2c_edid = adv7604_dummy_client(sd, pdata->i2c_edid, 0xfa); - state->i2c_hdmi = adv7604_dummy_client(sd, pdata->i2c_hdmi, 0xfb); - state->i2c_test = adv7604_dummy_client(sd, pdata->i2c_test, 0xfc); - state->i2c_cp = adv7604_dummy_client(sd, pdata->i2c_cp, 0xfd); - state->i2c_vdp = adv7604_dummy_client(sd, pdata->i2c_vdp, 0xfe); - if (!state->i2c_avlink || !state->i2c_cec || !state->i2c_infoframe || - !state->i2c_esdp || !state->i2c_dpp || !state->i2c_afe || - !state->i2c_repeater || !state->i2c_edid || !state->i2c_hdmi || - !state->i2c_test || !state->i2c_cp || !state->i2c_vdp) { - err = -ENOMEM; - v4l2_err(sd, "failed to create all i2c clients\n"); - goto err_i2c; + for (i = 1; i < ADV7604_PAGE_MAX; ++i) { + if (!(BIT(i) & state->info->page_mask)) + continue; + + state->i2c_clients[i] = + adv7604_dummy_client(sd, state->pdata.i2c_addresses[i], + 0xf2 + i); + if (state->i2c_clients[i] == NULL) { + err = -ENOMEM; + v4l2_err(sd, "failed to create i2c client %u\n", i); + goto err_i2c; + } } /* work queues */ @@ -2327,8 +2908,14 @@ static int adv7604_probe(struct i2c_client *client, INIT_DELAYED_WORK(&state->delayed_work_enable_hotplug, adv7604_delayed_work_enable_hotplug); - state->pad.flags = MEDIA_PAD_FL_SOURCE; - err = media_entity_init(&sd->entity, 1, &state->pad, 0); + state->source_pad = state->info->num_dv_ports + + (state->info->has_afe ? 2 : 0); + for (i = 0; i < state->source_pad; ++i) + state->pads[i].flags = MEDIA_PAD_FL_SINK; + state->pads[state->source_pad].flags = MEDIA_PAD_FL_SOURCE; + + err = media_entity_init(&sd->entity, state->source_pad + 1, + state->pads, 0); if (err) goto err_work_queues; @@ -2337,6 +2924,11 @@ static int adv7604_probe(struct i2c_client *client, goto err_entity; v4l2_info(sd, "%s found @ 0x%x (%s)\n", client->name, client->addr << 1, client->adapter->name); + + err = v4l2_async_register_subdev(sd); + if (err) + goto err_entity; + return 0; err_entity: @@ -2360,6 +2952,7 @@ static int adv7604_remove(struct i2c_client *client) cancel_delayed_work(&state->delayed_work_enable_hotplug); destroy_workqueue(state->work_queues); + v4l2_async_unregister_subdev(sd); v4l2_device_unregister_subdev(sd); media_entity_cleanup(&sd->entity); adv7604_unregister_clients(to_state(sd)); @@ -2369,20 +2962,15 @@ static int adv7604_remove(struct i2c_client *client) /* ----------------------------------------------------------------------- */ -static struct i2c_device_id adv7604_id[] = { - { "adv7604", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, adv7604_id); - static struct i2c_driver adv7604_driver = { .driver = { .owner = THIS_MODULE, .name = "adv7604", + .of_match_table = of_match_ptr(adv7604_of_id), }, .probe = adv7604_probe, .remove = adv7604_remove, - .id_table = adv7604_id, + .id_table = adv7604_i2c_id, }; module_i2c_driver(adv7604_driver); diff --git a/drivers/media/i2c/adv7842.c b/drivers/media/i2c/adv7842.c index 636ac08925f6..0d554919cdd5 100644 --- a/drivers/media/i2c/adv7842.c +++ b/drivers/media/i2c/adv7842.c @@ -1399,6 +1399,9 @@ static int read_stdi(struct v4l2_subdev *sd, struct stdi_readback *stdi) static int adv7842_enum_dv_timings(struct v4l2_subdev *sd, struct v4l2_enum_dv_timings *timings) { + if (timings->pad != 0) + return -EINVAL; + return v4l2_enum_dv_timings_cap(timings, adv7842_get_dv_timings_cap(sd), adv7842_check_dv_timings, NULL); } @@ -1406,6 +1409,9 @@ static int adv7842_enum_dv_timings(struct v4l2_subdev *sd, static int adv7842_dv_timings_cap(struct v4l2_subdev *sd, struct v4l2_dv_timings_cap *cap) { + if (cap->pad != 0) + return -EINVAL; + *cap = *adv7842_get_dv_timings_cap(sd); return 0; } @@ -2000,6 +2006,7 @@ static int adv7842_isr(struct v4l2_subdev *sd, u32 status, bool *handled) if (irq_status[5] & 0x08) { v4l2_dbg(1, debug, sd, "%s: irq %s mode\n", __func__, (io_read(sd, 0x65) & 0x08) ? "HDMI" : "DVI"); + set_rgb_quantization_range(sd); if (handled) *handled = true; } @@ -2029,8 +2036,6 @@ static int adv7842_get_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid) return -EINVAL; if (edid->start_block == 1) edid->blocks = 1; - if (!edid->edid) - return -EINVAL; switch (edid->pad) { case ADV7842_EDID_PORT_A: @@ -2065,8 +2070,6 @@ static int adv7842_set_edid(struct v4l2_subdev *sd, struct v4l2_edid *e) return -EINVAL; if (e->blocks > 2) return -E2BIG; - if (!e->edid) - return -EINVAL; /* todo, per edid */ state->aspect_ratio = v4l2_calc_aspect_ratio(e->edid[0x15], @@ -2610,6 +2613,12 @@ static int adv7842_core_init(struct v4l2_subdev *sd) disable_input(sd); + /* + * Disable I2C access to internal EDID ram from HDMI DDC ports + * Disable auto edid enable when leaving powerdown mode + */ + rep_write_and_or(sd, 0x77, 0xd3, 0x20); + /* power */ io_write(sd, 0x0c, 0x42); /* Power up part and power down VDP */ io_write(sd, 0x15, 0x80); /* Power up pads */ @@ -2690,9 +2699,6 @@ static int adv7842_core_init(struct v4l2_subdev *sd) enable_input(sd); - /* disable I2C access to internal EDID ram from HDMI DDC ports */ - rep_write_and_or(sd, 0x77, 0xf3, 0x00); - if (pdata->hpa_auto) { /* HPA auto, HPA 0.5s after Edid set and Cable detect */ hdmi_write(sd, 0x69, 0x5c); @@ -2869,8 +2875,6 @@ static const struct v4l2_ctrl_ops adv7842_ctrl_ops = { static const struct v4l2_subdev_core_ops adv7842_core_ops = { .log_status = adv7842_log_status, - .g_std = adv7842_g_std, - .s_std = adv7842_s_std, .ioctl = adv7842_ioctl, .interrupt_service_routine = adv7842_isr, #ifdef CONFIG_VIDEO_ADV_DEBUG @@ -2880,14 +2884,14 @@ static const struct v4l2_subdev_core_ops adv7842_core_ops = { }; static const struct v4l2_subdev_video_ops adv7842_video_ops = { + .g_std = adv7842_g_std, + .s_std = adv7842_s_std, .s_routing = adv7842_s_routing, .querystd = adv7842_querystd, .g_input_status = adv7842_g_input_status, .s_dv_timings = adv7842_s_dv_timings, .g_dv_timings = adv7842_g_dv_timings, .query_dv_timings = adv7842_query_dv_timings, - .enum_dv_timings = adv7842_enum_dv_timings, - .dv_timings_cap = adv7842_dv_timings_cap, .enum_mbus_fmt = adv7842_enum_mbus_fmt, .g_mbus_fmt = adv7842_g_mbus_fmt, .try_mbus_fmt = adv7842_g_mbus_fmt, @@ -2897,6 +2901,8 @@ static const struct v4l2_subdev_video_ops adv7842_video_ops = { static const struct v4l2_subdev_pad_ops adv7842_pad_ops = { .get_edid = adv7842_get_edid, .set_edid = adv7842_set_edid, + .enum_dv_timings = adv7842_enum_dv_timings, + .dv_timings_cap = adv7842_dv_timings_cap, }; static const struct v4l2_subdev_ops adv7842_ops = { diff --git a/drivers/media/i2c/bt819.c b/drivers/media/i2c/bt819.c index 369cf6ff88f7..76b334a6a56d 100644 --- a/drivers/media/i2c/bt819.c +++ b/drivers/media/i2c/bt819.c @@ -387,10 +387,10 @@ static const struct v4l2_subdev_core_ops bt819_core_ops = { .s_ctrl = v4l2_subdev_s_ctrl, .queryctrl = v4l2_subdev_queryctrl, .querymenu = v4l2_subdev_querymenu, - .s_std = bt819_s_std, }; static const struct v4l2_subdev_video_ops bt819_video_ops = { + .s_std = bt819_s_std, .s_routing = bt819_s_routing, .s_stream = bt819_s_stream, .querystd = bt819_querystd, diff --git a/drivers/media/i2c/cx25840/cx25840-core.c b/drivers/media/i2c/cx25840/cx25840-core.c index 2e3771d57354..e453a3ffe7d1 100644 --- a/drivers/media/i2c/cx25840/cx25840-core.c +++ b/drivers/media/i2c/cx25840/cx25840-core.c @@ -5041,8 +5041,6 @@ static const struct v4l2_subdev_core_ops cx25840_core_ops = { .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, .queryctrl = v4l2_subdev_queryctrl, .querymenu = v4l2_subdev_querymenu, - .s_std = cx25840_s_std, - .g_std = cx25840_g_std, .reset = cx25840_reset, .load_fw = cx25840_load_fw, .s_io_pin_config = common_s_io_pin_config, @@ -5067,6 +5065,8 @@ static const struct v4l2_subdev_audio_ops cx25840_audio_ops = { }; static const struct v4l2_subdev_video_ops cx25840_video_ops = { + .s_std = cx25840_s_std, + .g_std = cx25840_g_std, .s_routing = cx25840_s_video_routing, .s_mbus_fmt = cx25840_s_mbus_fmt, .s_stream = cx25840_s_stream, diff --git a/drivers/media/i2c/ks0127.c b/drivers/media/i2c/ks0127.c index c3e94ae82c03..25b81bc58c81 100644 --- a/drivers/media/i2c/ks0127.c +++ b/drivers/media/i2c/ks0127.c @@ -648,11 +648,8 @@ static int ks0127_g_input_status(struct v4l2_subdev *sd, u32 *status) /* ----------------------------------------------------------------------- */ -static const struct v4l2_subdev_core_ops ks0127_core_ops = { - .s_std = ks0127_s_std, -}; - static const struct v4l2_subdev_video_ops ks0127_video_ops = { + .s_std = ks0127_s_std, .s_routing = ks0127_s_routing, .s_stream = ks0127_s_stream, .querystd = ks0127_querystd, @@ -660,7 +657,6 @@ static const struct v4l2_subdev_video_ops ks0127_video_ops = { }; static const struct v4l2_subdev_ops ks0127_ops = { - .core = &ks0127_core_ops, .video = &ks0127_video_ops, }; diff --git a/drivers/media/i2c/m5mols/m5mols_capture.c b/drivers/media/i2c/m5mols/m5mols_capture.c index ab34ccedf31e..1a03d02bd4d1 100644 --- a/drivers/media/i2c/m5mols/m5mols_capture.c +++ b/drivers/media/i2c/m5mols/m5mols_capture.c @@ -26,7 +26,7 @@ #include <media/v4l2-device.h> #include <media/v4l2-subdev.h> #include <media/m5mols.h> -#include <media/s5p_fimc.h> +#include <media/exynos-fimc.h> #include "m5mols.h" #include "m5mols_reg.h" diff --git a/drivers/media/i2c/ml86v7667.c b/drivers/media/i2c/ml86v7667.c index a9110d8bbbcd..2cace7313a22 100644 --- a/drivers/media/i2c/ml86v7667.c +++ b/drivers/media/i2c/ml86v7667.c @@ -276,6 +276,7 @@ static const struct v4l2_ctrl_ops ml86v7667_ctrl_ops = { }; static struct v4l2_subdev_video_ops ml86v7667_subdev_video_ops = { + .s_std = ml86v7667_s_std, .querystd = ml86v7667_querystd, .g_input_status = ml86v7667_g_input_status, .enum_mbus_fmt = ml86v7667_enum_mbus_fmt, @@ -286,7 +287,6 @@ static struct v4l2_subdev_video_ops ml86v7667_subdev_video_ops = { }; static struct v4l2_subdev_core_ops ml86v7667_subdev_core_ops = { - .s_std = ml86v7667_s_std, #ifdef CONFIG_VIDEO_ADV_DEBUG .g_register = ml86v7667_g_register, .s_register = ml86v7667_s_register, diff --git a/drivers/media/i2c/msp3400-driver.c b/drivers/media/i2c/msp3400-driver.c index 8190fec68080..4d9c6bc34265 100644 --- a/drivers/media/i2c/msp3400-driver.c +++ b/drivers/media/i2c/msp3400-driver.c @@ -649,10 +649,10 @@ static const struct v4l2_subdev_core_ops msp_core_ops = { .s_ctrl = v4l2_subdev_s_ctrl, .queryctrl = v4l2_subdev_queryctrl, .querymenu = v4l2_subdev_querymenu, - .s_std = msp_s_std, }; static const struct v4l2_subdev_video_ops msp_video_ops = { + .s_std = msp_s_std, .querystd = msp_querystd, }; diff --git a/drivers/media/i2c/mt9p031.c b/drivers/media/i2c/mt9p031.c index 33daace81297..e18797ff7faf 100644 --- a/drivers/media/i2c/mt9p031.c +++ b/drivers/media/i2c/mt9p031.c @@ -647,6 +647,28 @@ static int mt9p031_set_crop(struct v4l2_subdev *subdev, #define V4L2_CID_BLC_ANALOG_OFFSET (V4L2_CID_USER_BASE | 0x1004) #define V4L2_CID_BLC_DIGITAL_OFFSET (V4L2_CID_USER_BASE | 0x1005) +static int mt9p031_restore_blc(struct mt9p031 *mt9p031) +{ + struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev); + int ret; + + if (mt9p031->blc_auto->cur.val != 0) { + ret = mt9p031_set_mode2(mt9p031, 0, + MT9P031_READ_MODE_2_ROW_BLC); + if (ret < 0) + return ret; + } + + if (mt9p031->blc_offset->cur.val != 0) { + ret = mt9p031_write(client, MT9P031_ROW_BLACK_TARGET, + mt9p031->blc_offset->cur.val); + if (ret < 0) + return ret; + } + + return 0; +} + static int mt9p031_s_ctrl(struct v4l2_ctrl *ctrl) { struct mt9p031 *mt9p031 = @@ -655,6 +677,9 @@ static int mt9p031_s_ctrl(struct v4l2_ctrl *ctrl) u16 data; int ret; + if (ctrl->flags & V4L2_CTRL_FLAG_INACTIVE) + return 0; + switch (ctrl->id) { case V4L2_CID_EXPOSURE: ret = mt9p031_write(client, MT9P031_SHUTTER_WIDTH_UPPER, @@ -709,18 +734,20 @@ static int mt9p031_s_ctrl(struct v4l2_ctrl *ctrl) MT9P031_READ_MODE_2_ROW_MIR, 0); case V4L2_CID_TEST_PATTERN: + /* The digital side of the Black Level Calibration function must + * be disabled when generating a test pattern to avoid artifacts + * in the image. Activate (deactivate) the BLC-related controls + * when the test pattern is enabled (disabled). + */ + v4l2_ctrl_activate(mt9p031->blc_auto, ctrl->val == 0); + v4l2_ctrl_activate(mt9p031->blc_offset, ctrl->val == 0); + if (!ctrl->val) { - /* Restore the black level compensation settings. */ - if (mt9p031->blc_auto->cur.val != 0) { - ret = mt9p031_s_ctrl(mt9p031->blc_auto); - if (ret < 0) - return ret; - } - if (mt9p031->blc_offset->cur.val != 0) { - ret = mt9p031_s_ctrl(mt9p031->blc_offset); - if (ret < 0) - return ret; - } + /* Restore the BLC settings. */ + ret = mt9p031_restore_blc(mt9p031); + if (ret < 0) + return ret; + return mt9p031_write(client, MT9P031_TEST_PATTERN, MT9P031_TEST_PATTERN_DISABLE); } @@ -735,9 +762,7 @@ static int mt9p031_s_ctrl(struct v4l2_ctrl *ctrl) if (ret < 0) return ret; - /* Disable digital black level compensation when using a test - * pattern. - */ + /* Disable digital BLC when generating a test pattern. */ ret = mt9p031_set_mode2(mt9p031, MT9P031_READ_MODE_2_ROW_BLC, 0); if (ret < 0) diff --git a/drivers/media/i2c/saa6752hs.c b/drivers/media/i2c/saa6752hs.c index 8272c0b9c5bf..04e9e55018a5 100644 --- a/drivers/media/i2c/saa6752hs.c +++ b/drivers/media/i2c/saa6752hs.c @@ -643,10 +643,10 @@ static const struct v4l2_ctrl_ops saa6752hs_ctrl_ops = { static const struct v4l2_subdev_core_ops saa6752hs_core_ops = { .init = saa6752hs_init, - .s_std = saa6752hs_s_std, }; static const struct v4l2_subdev_video_ops saa6752hs_video_ops = { + .s_std = saa6752hs_s_std, .s_mbus_fmt = saa6752hs_s_mbus_fmt, .try_mbus_fmt = saa6752hs_try_mbus_fmt, .g_mbus_fmt = saa6752hs_g_mbus_fmt, diff --git a/drivers/media/i2c/saa7110.c b/drivers/media/i2c/saa7110.c index ac43e929a1d6..99689ee57d7e 100644 --- a/drivers/media/i2c/saa7110.c +++ b/drivers/media/i2c/saa7110.c @@ -365,10 +365,10 @@ static const struct v4l2_subdev_core_ops saa7110_core_ops = { .s_ctrl = v4l2_subdev_s_ctrl, .queryctrl = v4l2_subdev_queryctrl, .querymenu = v4l2_subdev_querymenu, - .s_std = saa7110_s_std, }; static const struct v4l2_subdev_video_ops saa7110_video_ops = { + .s_std = saa7110_s_std, .s_routing = saa7110_s_routing, .s_stream = saa7110_s_stream, .querystd = saa7110_querystd, diff --git a/drivers/media/i2c/saa7115.c b/drivers/media/i2c/saa7115.c index afdbcb045cee..35a44648150e 100644 --- a/drivers/media/i2c/saa7115.c +++ b/drivers/media/i2c/saa7115.c @@ -1582,7 +1582,6 @@ static const struct v4l2_subdev_core_ops saa711x_core_ops = { .s_ctrl = v4l2_subdev_s_ctrl, .queryctrl = v4l2_subdev_queryctrl, .querymenu = v4l2_subdev_querymenu, - .s_std = saa711x_s_std, .reset = saa711x_reset, .s_gpio = saa711x_s_gpio, #ifdef CONFIG_VIDEO_ADV_DEBUG @@ -1601,6 +1600,7 @@ static const struct v4l2_subdev_audio_ops saa711x_audio_ops = { }; static const struct v4l2_subdev_video_ops saa711x_video_ops = { + .s_std = saa711x_s_std, .s_routing = saa711x_s_routing, .s_crystal_freq = saa711x_s_crystal_freq, .s_mbus_fmt = saa711x_s_mbus_fmt, diff --git a/drivers/media/i2c/saa717x.c b/drivers/media/i2c/saa717x.c index 401ca114ab99..6922a9f9a5cd 100644 --- a/drivers/media/i2c/saa717x.c +++ b/drivers/media/i2c/saa717x.c @@ -1198,7 +1198,6 @@ static const struct v4l2_subdev_core_ops saa717x_core_ops = { .g_register = saa717x_g_register, .s_register = saa717x_s_register, #endif - .s_std = saa717x_s_std, .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, @@ -1216,6 +1215,7 @@ static const struct v4l2_subdev_tuner_ops saa717x_tuner_ops = { }; static const struct v4l2_subdev_video_ops saa717x_video_ops = { + .s_std = saa717x_s_std, .s_routing = saa717x_s_video_routing, .s_mbus_fmt = saa717x_s_mbus_fmt, .s_stream = saa717x_s_stream, diff --git a/drivers/media/i2c/saa7191.c b/drivers/media/i2c/saa7191.c index 606a4baf944d..8e9699268a63 100644 --- a/drivers/media/i2c/saa7191.c +++ b/drivers/media/i2c/saa7191.c @@ -573,10 +573,10 @@ static int saa7191_g_input_status(struct v4l2_subdev *sd, u32 *status) static const struct v4l2_subdev_core_ops saa7191_core_ops = { .g_ctrl = saa7191_g_ctrl, .s_ctrl = saa7191_s_ctrl, - .s_std = saa7191_s_std, }; static const struct v4l2_subdev_video_ops saa7191_video_ops = { + .s_std = saa7191_s_std, .s_routing = saa7191_s_routing, .querystd = saa7191_querystd, .g_input_status = saa7191_g_input_status, diff --git a/drivers/media/i2c/smiapp-pll.h b/drivers/media/i2c/smiapp-pll.h index a4a649834a18..5ce2b61da3c5 100644 --- a/drivers/media/i2c/smiapp-pll.h +++ b/drivers/media/i2c/smiapp-pll.h @@ -46,7 +46,7 @@ struct smiapp_pll { uint8_t bus_width; } parallel; }; - uint8_t flags; + unsigned long flags; uint8_t binning_horizontal; uint8_t binning_vertical; uint8_t scale_m; diff --git a/drivers/media/i2c/smiapp/smiapp-core.c b/drivers/media/i2c/smiapp/smiapp-core.c index 8741cae9c9f2..06fb03291d59 100644 --- a/drivers/media/i2c/smiapp/smiapp-core.c +++ b/drivers/media/i2c/smiapp/smiapp-core.c @@ -606,7 +606,7 @@ static int smiapp_get_limits(struct smiapp_sensor *sensor, int const *limit, if (rval) return rval; sensor->limits[limit[i]] = val; - dev_dbg(&client->dev, "0x%8.8x \"%s\" = %d, 0x%x\n", + dev_dbg(&client->dev, "0x%8.8x \"%s\" = %u, 0x%x\n", smiapp_reg_limits[limit[i]].addr, smiapp_reg_limits[limit[i]].what, val, val); } @@ -741,8 +741,8 @@ static int smiapp_get_mbus_formats(struct smiapp_sensor *sensor) if (rval) return rval; - dev_dbg(&client->dev, "bpp %d, compressed %d\n", - fmt >> 8, (u8)fmt); + dev_dbg(&client->dev, "%u: bpp %u, compressed %u\n", + i, fmt >> 8, (u8)fmt); for (j = 0; j < ARRAY_SIZE(smiapp_csi_data_formats); j++) { const struct smiapp_csi_data_format *f = @@ -1128,7 +1128,7 @@ static int smiapp_power_on(struct smiapp_sensor *sensor) } usleep_range(1000, 1000); - if (sensor->platform_data->xshutdown != SMIAPP_NO_XSHUTDOWN) + if (gpio_is_valid(sensor->platform_data->xshutdown)) gpio_set_value(sensor->platform_data->xshutdown, 1); sleep = SMIAPP_RESET_DELAY(sensor->platform_data->ext_clk); @@ -1238,7 +1238,7 @@ static int smiapp_power_on(struct smiapp_sensor *sensor) return 0; out_cci_addr_fail: - if (sensor->platform_data->xshutdown != SMIAPP_NO_XSHUTDOWN) + if (gpio_is_valid(sensor->platform_data->xshutdown)) gpio_set_value(sensor->platform_data->xshutdown, 0); if (sensor->platform_data->set_xclk) sensor->platform_data->set_xclk(&sensor->src->sd, 0); @@ -1264,7 +1264,7 @@ static void smiapp_power_off(struct smiapp_sensor *sensor) SMIAPP_REG_U8_SOFTWARE_RESET, SMIAPP_SOFTWARE_RESET); - if (sensor->platform_data->xshutdown != SMIAPP_NO_XSHUTDOWN) + if (gpio_is_valid(sensor->platform_data->xshutdown)) gpio_set_value(sensor->platform_data->xshutdown, 0); if (sensor->platform_data->set_xclk) sensor->platform_data->set_xclk(&sensor->src->sd, 0); @@ -1766,7 +1766,7 @@ static void smiapp_set_compose_binner(struct v4l2_subdev *subdev, struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); unsigned int i; unsigned int binh = 1, binv = 1; - unsigned int best = scaling_goodness( + int best = scaling_goodness( subdev, crops[SMIAPP_PAD_SINK]->width, sel->r.width, crops[SMIAPP_PAD_SINK]->height, sel->r.height, sel->flags); @@ -2355,17 +2355,17 @@ static int smiapp_registered(struct v4l2_subdev *subdev) unsigned int i; int rval; - sensor->vana = devm_regulator_get(&client->dev, "VANA"); + sensor->vana = devm_regulator_get(&client->dev, "vana"); if (IS_ERR(sensor->vana)) { dev_err(&client->dev, "could not get regulator for vana\n"); - return -ENODEV; + return PTR_ERR(sensor->vana); } if (!sensor->platform_data->set_xclk) { sensor->ext_clk = devm_clk_get(&client->dev, "ext_clk"); if (IS_ERR(sensor->ext_clk)) { dev_err(&client->dev, "could not get clock\n"); - return -ENODEV; + return PTR_ERR(sensor->ext_clk); } rval = clk_set_rate(sensor->ext_clk, @@ -2374,18 +2374,19 @@ static int smiapp_registered(struct v4l2_subdev *subdev) dev_err(&client->dev, "unable to set clock freq to %u\n", sensor->platform_data->ext_clk); - return -ENODEV; + return rval; } } - if (sensor->platform_data->xshutdown != SMIAPP_NO_XSHUTDOWN) { - if (devm_gpio_request_one(&client->dev, - sensor->platform_data->xshutdown, 0, - "SMIA++ xshutdown") != 0) { + if (gpio_is_valid(sensor->platform_data->xshutdown)) { + rval = devm_gpio_request_one( + &client->dev, sensor->platform_data->xshutdown, 0, + "SMIA++ xshutdown"); + if (rval < 0) { dev_err(&client->dev, "unable to acquire reset gpio %d\n", sensor->platform_data->xshutdown); - return -ENODEV; + return rval; } } @@ -2423,6 +2424,12 @@ static int smiapp_registered(struct v4l2_subdev *subdev) sensor->hvflip_inv_mask = SMIAPP_IMAGE_ORIENTATION_HFLIP | SMIAPP_IMAGE_ORIENTATION_VFLIP; + rval = smiapp_call_quirk(sensor, limits); + if (rval) { + dev_err(&client->dev, "limits quirks failed\n"); + goto out_power_off; + } + rval = smiapp_get_mbus_formats(sensor); if (rval) { rval = -ENODEV; @@ -2483,12 +2490,6 @@ static int smiapp_registered(struct v4l2_subdev *subdev) } } - rval = smiapp_call_quirk(sensor, limits); - if (rval) { - dev_err(&client->dev, "limits quirks failed\n"); - goto out_nvm_release; - } - /* We consider this as profile 0 sensor if any of these are zero. */ if (!sensor->limits[SMIAPP_LIMIT_MIN_OP_SYS_CLK_DIV] || !sensor->limits[SMIAPP_LIMIT_MAX_OP_SYS_CLK_DIV] || @@ -2543,8 +2544,9 @@ static int smiapp_registered(struct v4l2_subdev *subdev) } snprintf(this->sd.name, - sizeof(this->sd.name), "%s %s", - sensor->minfo.name, _this->name); + sizeof(this->sd.name), "%s %d-%4.4x %s", + sensor->minfo.name, i2c_adapter_id(client->adapter), + client->addr, _this->name); this->sink_fmt.width = sensor->limits[SMIAPP_LIMIT_X_ADDR_MAX] + 1; @@ -2616,12 +2618,11 @@ static int smiapp_registered(struct v4l2_subdev *subdev) pll->bus_type = SMIAPP_PLL_BUS_TYPE_CSI2; pll->csi2.lanes = sensor->platform_data->lanes; pll->ext_clk_freq_hz = sensor->platform_data->ext_clk; + pll->flags = smiapp_call_quirk(sensor, pll_flags); + /* Profile 0 sensors have no separate OP clock branch. */ if (sensor->minfo.smiapp_profile == SMIAPP_PROFILE_0) pll->flags |= SMIAPP_PLL_FLAG_NO_OP_CLOCKS; - if (smiapp_needs_quirk(sensor, - SMIAPP_QUIRK_FLAG_OP_PIX_CLOCK_PER_LANE)) - pll->flags |= SMIAPP_PLL_FLAG_OP_PIX_CLOCK_PER_LANE; pll->scale_n = sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN]; rval = smiapp_update_mode(sensor); @@ -2830,7 +2831,7 @@ static int smiapp_remove(struct i2c_client *client) unsigned int i; if (sensor->power_count) { - if (sensor->platform_data->xshutdown != SMIAPP_NO_XSHUTDOWN) + if (gpio_is_valid(sensor->platform_data->xshutdown)) gpio_set_value(sensor->platform_data->xshutdown, 0); if (sensor->platform_data->set_xclk) sensor->platform_data->set_xclk(&sensor->src->sd, 0); diff --git a/drivers/media/i2c/smiapp/smiapp-quirk.c b/drivers/media/i2c/smiapp/smiapp-quirk.c index bb8c506e0e3d..e0bee8752122 100644 --- a/drivers/media/i2c/smiapp/smiapp-quirk.c +++ b/drivers/media/i2c/smiapp/smiapp-quirk.c @@ -28,7 +28,7 @@ static int smiapp_write_8(struct smiapp_sensor *sensor, u16 reg, u8 val) { - return smiapp_write(sensor, (SMIA_REG_8BIT << 16) | reg, val); + return smiapp_write(sensor, SMIAPP_REG_MK_U8(reg), val); } static int smiapp_write_8s(struct smiapp_sensor *sensor, @@ -61,52 +61,6 @@ void smiapp_replace_limit(struct smiapp_sensor *sensor, sensor->limits[limit] = val; } -bool smiapp_quirk_reg(struct smiapp_sensor *sensor, - u32 reg, u32 *val) -{ - struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); - const struct smia_reg *sreg; - - if (!sensor->minfo.quirk) - return false; - - sreg = sensor->minfo.quirk->regs; - - if (!sreg) - return false; - - while (sreg->type) { - u16 type = reg >> 16; - u16 reg16 = reg; - - if (sreg->type != type || sreg->reg != reg16) { - sreg++; - continue; - } - - switch ((u8)type) { - case SMIA_REG_8BIT: - dev_dbg(&client->dev, "quirk: 0x%8.8x: 0x%2.2x\n", - reg, sreg->val); - break; - case SMIA_REG_16BIT: - dev_dbg(&client->dev, "quirk: 0x%8.8x: 0x%4.4x\n", - reg, sreg->val); - break; - case SMIA_REG_32BIT: - dev_dbg(&client->dev, "quirk: 0x%8.8x: 0x%8.8x\n", - reg, sreg->val); - break; - } - - *val = sreg->val; - - return true; - } - - return false; -} - static int jt8ew9_limits(struct smiapp_sensor *sensor) { if (sensor->minfo.revision_number_major < 0x03) @@ -266,12 +220,17 @@ static int jt8ev1_post_streamoff(struct smiapp_sensor *sensor) return smiapp_write_8(sensor, 0x3328, 0x80); } +static unsigned long jt8ev1_pll_flags(struct smiapp_sensor *sensor) +{ + return SMIAPP_PLL_FLAG_OP_PIX_CLOCK_PER_LANE; +} + const struct smiapp_quirk smiapp_jt8ev1_quirk = { .limits = jt8ev1_limits, .post_poweron = jt8ev1_post_poweron, .pre_streamon = jt8ev1_pre_streamon, .post_streamoff = jt8ev1_post_streamoff, - .flags = SMIAPP_QUIRK_FLAG_OP_PIX_CLOCK_PER_LANE, + .pll_flags = jt8ev1_pll_flags, }; static int tcm8500md_limits(struct smiapp_sensor *sensor) diff --git a/drivers/media/i2c/smiapp/smiapp-quirk.h b/drivers/media/i2c/smiapp/smiapp-quirk.h index 504a6d80ced5..46e9ea8bfa08 100644 --- a/drivers/media/i2c/smiapp/smiapp-quirk.h +++ b/drivers/media/i2c/smiapp/smiapp-quirk.h @@ -35,19 +35,30 @@ struct smiapp_sensor; * @post_poweron: Called always after the sensor has been fully powered on. * @pre_streamon: Called just before streaming is enabled. * @post_streamon: Called right after stopping streaming. + * @reg_access: Register access quirk. The quirk may divert the access + * to another register, or no register at all. + * + * @write: Is this read (false) or write (true) access? + * @reg: Pointer to the register to access + * @value: Register value, set by the caller on write, or + * by the quirk on read + * + * @return: 0 on success, -ENOIOCTLCMD if no register + * access may be done by the caller (default read + * value is zero), else negative error code on error */ struct smiapp_quirk { int (*limits)(struct smiapp_sensor *sensor); int (*post_poweron)(struct smiapp_sensor *sensor); int (*pre_streamon)(struct smiapp_sensor *sensor); int (*post_streamoff)(struct smiapp_sensor *sensor); - const struct smia_reg *regs; + unsigned long (*pll_flags)(struct smiapp_sensor *sensor); + int (*reg_access)(struct smiapp_sensor *sensor, bool write, u32 *reg, + u32 *val); unsigned long flags; }; -/* op pix clock is for all lanes in total normally */ -#define SMIAPP_QUIRK_FLAG_OP_PIX_CLOCK_PER_LANE (1 << 0) -#define SMIAPP_QUIRK_FLAG_8BIT_READ_ONLY (1 << 1) +#define SMIAPP_QUIRK_FLAG_8BIT_READ_ONLY (1 << 0) struct smiapp_reg_8 { u16 reg; @@ -56,12 +67,9 @@ struct smiapp_reg_8 { void smiapp_replace_limit(struct smiapp_sensor *sensor, u32 limit, u32 val); -bool smiapp_quirk_reg(struct smiapp_sensor *sensor, - u32 reg, u32 *val); -#define SMIAPP_MK_QUIRK_REG(_reg, _val) \ +#define SMIAPP_MK_QUIRK_REG_8(_reg, _val) \ { \ - .type = (_reg >> 16), \ .reg = (u16)_reg, \ .val = _val, \ } diff --git a/drivers/media/i2c/smiapp/smiapp-reg-defs.h b/drivers/media/i2c/smiapp/smiapp-reg-defs.h index 3aa0ca948d87..c488ef028074 100644 --- a/drivers/media/i2c/smiapp/smiapp-reg-defs.h +++ b/drivers/media/i2c/smiapp/smiapp-reg-defs.h @@ -21,11 +21,11 @@ * 02110-1301 USA * */ -#define SMIAPP_REG_MK_U8(r) ((SMIA_REG_8BIT << 16) | (r)) -#define SMIAPP_REG_MK_U16(r) ((SMIA_REG_16BIT << 16) | (r)) -#define SMIAPP_REG_MK_U32(r) ((SMIA_REG_32BIT << 16) | (r)) +#define SMIAPP_REG_MK_U8(r) ((SMIAPP_REG_8BIT << 16) | (r)) +#define SMIAPP_REG_MK_U16(r) ((SMIAPP_REG_16BIT << 16) | (r)) +#define SMIAPP_REG_MK_U32(r) ((SMIAPP_REG_32BIT << 16) | (r)) -#define SMIAPP_REG_MK_F32(r) (SMIA_REG_FLAG_FLOAT | (SMIA_REG_32BIT << 16) | (r)) +#define SMIAPP_REG_MK_F32(r) (SMIAPP_REG_FLAG_FLOAT | (SMIAPP_REG_32BIT << 16) | (r)) #define SMIAPP_REG_U16_MODEL_ID SMIAPP_REG_MK_U16(0x0000) #define SMIAPP_REG_U8_REVISION_NUMBER_MAJOR SMIAPP_REG_MK_U8(0x0002) diff --git a/drivers/media/i2c/smiapp/smiapp-regs.c b/drivers/media/i2c/smiapp/smiapp-regs.c index 4fac32cfcb3f..a2098007fb70 100644 --- a/drivers/media/i2c/smiapp/smiapp-regs.c +++ b/drivers/media/i2c/smiapp/smiapp-regs.c @@ -114,14 +114,14 @@ static int ____smiapp_read(struct smiapp_sensor *sensor, u16 reg, *val = 0; /* high byte comes first */ switch (len) { - case SMIA_REG_32BIT: + case SMIAPP_REG_32BIT: *val = (data[0] << 24) + (data[1] << 16) + (data[2] << 8) + data[3]; break; - case SMIA_REG_16BIT: + case SMIAPP_REG_16BIT: *val = (data[0] << 8) + data[1]; break; - case SMIA_REG_8BIT: + case SMIAPP_REG_8BIT: *val = data[0]; break; default: @@ -165,31 +165,28 @@ static int __smiapp_read(struct smiapp_sensor *sensor, u32 reg, u32 *val, bool only8) { struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); - unsigned int len = (u8)(reg >> 16); + u8 len = SMIAPP_REG_WIDTH(reg); int rval; - if (len != SMIA_REG_8BIT && len != SMIA_REG_16BIT - && len != SMIA_REG_32BIT) + if (len != SMIAPP_REG_8BIT && len != SMIAPP_REG_16BIT + && len != SMIAPP_REG_32BIT) return -EINVAL; - if (smiapp_quirk_reg(sensor, reg, val)) - goto found_quirk; - - if (len == SMIA_REG_8BIT && !only8) - rval = ____smiapp_read(sensor, (u16)reg, len, val); + if (len == SMIAPP_REG_8BIT || !only8) + rval = ____smiapp_read(sensor, SMIAPP_REG_ADDR(reg), len, val); else - rval = ____smiapp_read_8only(sensor, (u16)reg, len, val); + rval = ____smiapp_read_8only(sensor, SMIAPP_REG_ADDR(reg), len, + val); if (rval < 0) return rval; -found_quirk: - if (reg & SMIA_REG_FLAG_FLOAT) + if (reg & SMIAPP_REG_FLAG_FLOAT) *val = float_to_u32_mul_1000000(client, *val); return 0; } -int smiapp_read(struct smiapp_sensor *sensor, u32 reg, u32 *val) +int smiapp_read_no_quirk(struct smiapp_sensor *sensor, u32 reg, u32 *val) { return __smiapp_read( sensor, reg, val, @@ -197,28 +194,47 @@ int smiapp_read(struct smiapp_sensor *sensor, u32 reg, u32 *val) SMIAPP_QUIRK_FLAG_8BIT_READ_ONLY)); } +int smiapp_read(struct smiapp_sensor *sensor, u32 reg, u32 *val) +{ + int rval; + + *val = 0; + rval = smiapp_call_quirk(sensor, reg_access, false, ®, val); + if (rval == -ENOIOCTLCMD) + return 0; + if (rval < 0) + return rval; + + return smiapp_read_no_quirk(sensor, reg, val); +} + int smiapp_read_8only(struct smiapp_sensor *sensor, u32 reg, u32 *val) { + int rval; + + *val = 0; + rval = smiapp_call_quirk(sensor, reg_access, false, ®, val); + if (rval == -ENOIOCTLCMD) + return 0; + if (rval < 0) + return rval; + return __smiapp_read(sensor, reg, val, true); } -/* - * Write to a 8/16-bit register. - * Returns zero if successful, or non-zero otherwise. - */ -int smiapp_write(struct smiapp_sensor *sensor, u32 reg, u32 val) +int smiapp_write_no_quirk(struct smiapp_sensor *sensor, u32 reg, u32 val) { struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); struct i2c_msg msg; unsigned char data[6]; unsigned int retries; - unsigned int flags = reg >> 24; - unsigned int len = (u8)(reg >> 16); - u16 offset = reg; + u8 flags = SMIAPP_REG_FLAGS(reg); + u8 len = SMIAPP_REG_WIDTH(reg); + u16 offset = SMIAPP_REG_ADDR(reg); int r; - if ((len != SMIA_REG_8BIT && len != SMIA_REG_16BIT && - len != SMIA_REG_32BIT) || flags) + if ((len != SMIAPP_REG_8BIT && len != SMIAPP_REG_16BIT && + len != SMIAPP_REG_32BIT) || flags) return -EINVAL; msg.addr = client->addr; @@ -231,14 +247,14 @@ int smiapp_write(struct smiapp_sensor *sensor, u32 reg, u32 val) data[1] = (u8) (reg & 0xff); switch (len) { - case SMIA_REG_8BIT: + case SMIAPP_REG_8BIT: data[2] = val; break; - case SMIA_REG_16BIT: + case SMIAPP_REG_16BIT: data[2] = val >> 8; data[3] = val; break; - case SMIA_REG_32BIT: + case SMIAPP_REG_32BIT: data[2] = val >> 24; data[3] = val >> 16; data[4] = val >> 8; @@ -271,3 +287,20 @@ int smiapp_write(struct smiapp_sensor *sensor, u32 reg, u32 val) return r; } + +/* + * Write to a 8/16-bit register. + * Returns zero if successful, or non-zero otherwise. + */ +int smiapp_write(struct smiapp_sensor *sensor, u32 reg, u32 val) +{ + int rval; + + rval = smiapp_call_quirk(sensor, reg_access, true, ®, &val); + if (rval == -ENOIOCTLCMD) + return 0; + if (rval < 0) + return rval; + + return smiapp_write_no_quirk(sensor, reg, val); +} diff --git a/drivers/media/i2c/smiapp/smiapp-regs.h b/drivers/media/i2c/smiapp/smiapp-regs.h index eefc6c84d5fe..35521125a2cc 100644 --- a/drivers/media/i2c/smiapp/smiapp-regs.h +++ b/drivers/media/i2c/smiapp/smiapp-regs.h @@ -28,22 +28,23 @@ #include <linux/i2c.h> #include <linux/types.h> +#define SMIAPP_REG_ADDR(reg) ((u16)reg) +#define SMIAPP_REG_WIDTH(reg) ((u8)(reg >> 16)) +#define SMIAPP_REG_FLAGS(reg) ((u8)(reg >> 24)) + /* Use upper 8 bits of the type field for flags */ -#define SMIA_REG_FLAG_FLOAT (1 << 24) +#define SMIAPP_REG_FLAG_FLOAT (1 << 24) -#define SMIA_REG_8BIT 1 -#define SMIA_REG_16BIT 2 -#define SMIA_REG_32BIT 4 -struct smia_reg { - u16 type; - u16 reg; /* 16-bit offset */ - u32 val; /* 8/16/32-bit value */ -}; +#define SMIAPP_REG_8BIT 1 +#define SMIAPP_REG_16BIT 2 +#define SMIAPP_REG_32BIT 4 struct smiapp_sensor; +int smiapp_read_no_quirk(struct smiapp_sensor *sensor, u32 reg, u32 *val); int smiapp_read(struct smiapp_sensor *sensor, u32 reg, u32 *val); int smiapp_read_8only(struct smiapp_sensor *sensor, u32 reg, u32 *val); +int smiapp_write_no_quirk(struct smiapp_sensor *sensor, u32 reg, u32 val); int smiapp_write(struct smiapp_sensor *sensor, u32 reg, u32 val); #endif diff --git a/drivers/media/i2c/soc_camera/tw9910.c b/drivers/media/i2c/soc_camera/tw9910.c index ab54628d9411..416402eb4f82 100644 --- a/drivers/media/i2c/soc_camera/tw9910.c +++ b/drivers/media/i2c/soc_camera/tw9910.c @@ -814,8 +814,6 @@ done: } static struct v4l2_subdev_core_ops tw9910_subdev_core_ops = { - .s_std = tw9910_s_std, - .g_std = tw9910_g_std, #ifdef CONFIG_VIDEO_ADV_DEBUG .g_register = tw9910_g_register, .s_register = tw9910_s_register, @@ -872,7 +870,15 @@ static int tw9910_s_mbus_config(struct v4l2_subdev *sd, return i2c_smbus_write_byte_data(client, OUTCTR1, val); } +static int tw9910_g_tvnorms(struct v4l2_subdev *sd, v4l2_std_id *norm) +{ + *norm = V4L2_STD_NTSC | V4L2_STD_PAL; + return 0; +} + static struct v4l2_subdev_video_ops tw9910_subdev_video_ops = { + .s_std = tw9910_s_std, + .g_std = tw9910_g_std, .s_stream = tw9910_s_stream, .g_mbus_fmt = tw9910_g_fmt, .s_mbus_fmt = tw9910_s_fmt, @@ -882,6 +888,7 @@ static struct v4l2_subdev_video_ops tw9910_subdev_video_ops = { .enum_mbus_fmt = tw9910_enum_fmt, .g_mbus_config = tw9910_g_mbus_config, .s_mbus_config = tw9910_s_mbus_config, + .g_tvnorms = tw9910_g_tvnorms, }; static struct v4l2_subdev_ops tw9910_subdev_ops = { diff --git a/drivers/media/i2c/sony-btf-mpx.c b/drivers/media/i2c/sony-btf-mpx.c index 32d82320b485..1da8004f5a8e 100644 --- a/drivers/media/i2c/sony-btf-mpx.c +++ b/drivers/media/i2c/sony-btf-mpx.c @@ -327,18 +327,18 @@ static int sony_btf_mpx_s_tuner(struct v4l2_subdev *sd, const struct v4l2_tuner /* --------------------------------------------------------------------------*/ -static const struct v4l2_subdev_core_ops sony_btf_mpx_core_ops = { - .s_std = sony_btf_mpx_s_std, -}; - static const struct v4l2_subdev_tuner_ops sony_btf_mpx_tuner_ops = { .s_tuner = sony_btf_mpx_s_tuner, .g_tuner = sony_btf_mpx_g_tuner, }; +static const struct v4l2_subdev_video_ops sony_btf_mpx_video_ops = { + .s_std = sony_btf_mpx_s_std, +}; + static const struct v4l2_subdev_ops sony_btf_mpx_ops = { - .core = &sony_btf_mpx_core_ops, .tuner = &sony_btf_mpx_tuner_ops, + .video = &sony_btf_mpx_video_ops, }; /* --------------------------------------------------------------------------*/ diff --git a/drivers/media/i2c/ths8200.c b/drivers/media/i2c/ths8200.c index f72561e79739..656d889c1c79 100644 --- a/drivers/media/i2c/ths8200.c +++ b/drivers/media/i2c/ths8200.c @@ -410,6 +410,9 @@ static int ths8200_g_dv_timings(struct v4l2_subdev *sd, static int ths8200_enum_dv_timings(struct v4l2_subdev *sd, struct v4l2_enum_dv_timings *timings) { + if (timings->pad != 0) + return -EINVAL; + return v4l2_enum_dv_timings_cap(timings, &ths8200_timings_cap, NULL, NULL); } @@ -417,6 +420,9 @@ static int ths8200_enum_dv_timings(struct v4l2_subdev *sd, static int ths8200_dv_timings_cap(struct v4l2_subdev *sd, struct v4l2_dv_timings_cap *cap) { + if (cap->pad != 0) + return -EINVAL; + *cap = ths8200_timings_cap; return 0; } @@ -426,6 +432,9 @@ static const struct v4l2_subdev_video_ops ths8200_video_ops = { .s_stream = ths8200_s_stream, .s_dv_timings = ths8200_s_dv_timings, .g_dv_timings = ths8200_g_dv_timings, +}; + +static const struct v4l2_subdev_pad_ops ths8200_pad_ops = { .enum_dv_timings = ths8200_enum_dv_timings, .dv_timings_cap = ths8200_dv_timings_cap, }; @@ -434,6 +443,7 @@ static const struct v4l2_subdev_video_ops ths8200_video_ops = { static const struct v4l2_subdev_ops ths8200_ops = { .core = &ths8200_core_ops, .video = &ths8200_video_ops, + .pad = &ths8200_pad_ops, }; static int ths8200_probe(struct i2c_client *client, diff --git a/drivers/media/i2c/tvaudio.c b/drivers/media/i2c/tvaudio.c index d76c53a8f027..070c152da95a 100644 --- a/drivers/media/i2c/tvaudio.c +++ b/drivers/media/i2c/tvaudio.c @@ -1862,7 +1862,6 @@ static const struct v4l2_subdev_core_ops tvaudio_core_ops = { .s_ctrl = v4l2_subdev_s_ctrl, .queryctrl = v4l2_subdev_queryctrl, .querymenu = v4l2_subdev_querymenu, - .s_std = tvaudio_s_std, }; static const struct v4l2_subdev_tuner_ops tvaudio_tuner_ops = { @@ -1876,10 +1875,15 @@ static const struct v4l2_subdev_audio_ops tvaudio_audio_ops = { .s_routing = tvaudio_s_routing, }; +static const struct v4l2_subdev_video_ops tvaudio_video_ops = { + .s_std = tvaudio_s_std, +}; + static const struct v4l2_subdev_ops tvaudio_ops = { .core = &tvaudio_core_ops, .tuner = &tvaudio_tuner_ops, .audio = &tvaudio_audio_ops, + .video = &tvaudio_video_ops, }; /* ----------------------------------------------------------------------- */ diff --git a/drivers/media/i2c/tvp514x.c b/drivers/media/i2c/tvp514x.c index ca001178c5bf..b9dabc9f4050 100644 --- a/drivers/media/i2c/tvp514x.c +++ b/drivers/media/i2c/tvp514x.c @@ -1010,10 +1010,10 @@ static const struct v4l2_subdev_core_ops tvp514x_core_ops = { .s_ctrl = v4l2_subdev_s_ctrl, .queryctrl = v4l2_subdev_queryctrl, .querymenu = v4l2_subdev_querymenu, - .s_std = tvp514x_s_std, }; static const struct v4l2_subdev_video_ops tvp514x_video_ops = { + .s_std = tvp514x_s_std, .s_routing = tvp514x_s_routing, .querystd = tvp514x_querystd, .enum_mbus_fmt = tvp514x_enum_mbus_fmt, diff --git a/drivers/media/i2c/tvp5150.c b/drivers/media/i2c/tvp5150.c index 4fd3688e1164..a9121254e37a 100644 --- a/drivers/media/i2c/tvp5150.c +++ b/drivers/media/i2c/tvp5150.c @@ -913,7 +913,7 @@ static int tvp5150_s_crop(struct v4l2_subdev *sd, const struct v4l2_crop *a) static int tvp5150_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a) { - struct tvp5150 *decoder = container_of(sd, struct tvp5150, sd); + struct tvp5150 *decoder = to_tvp5150(sd); a->c = decoder->rect; a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; @@ -923,7 +923,7 @@ static int tvp5150_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a) static int tvp5150_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a) { - struct tvp5150 *decoder = container_of(sd, struct tvp5150, sd); + struct tvp5150 *decoder = to_tvp5150(sd); v4l2_std_id std; if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) @@ -1063,7 +1063,6 @@ static const struct v4l2_ctrl_ops tvp5150_ctrl_ops = { static const struct v4l2_subdev_core_ops tvp5150_core_ops = { .log_status = tvp5150_log_status, - .s_std = tvp5150_s_std, .reset = tvp5150_reset, #ifdef CONFIG_VIDEO_ADV_DEBUG .g_register = tvp5150_g_register, @@ -1076,6 +1075,7 @@ static const struct v4l2_subdev_tuner_ops tvp5150_tuner_ops = { }; static const struct v4l2_subdev_video_ops tvp5150_video_ops = { + .s_std = tvp5150_s_std, .s_routing = tvp5150_s_routing, .enum_mbus_fmt = tvp5150_enum_mbus_fmt, .s_mbus_fmt = tvp5150_mbus_fmt, diff --git a/drivers/media/i2c/tvp7002.c b/drivers/media/i2c/tvp7002.c index c4e1e2cb3094..11f2387e1dab 100644 --- a/drivers/media/i2c/tvp7002.c +++ b/drivers/media/i2c/tvp7002.c @@ -833,6 +833,9 @@ static int tvp7002_log_status(struct v4l2_subdev *sd) static int tvp7002_enum_dv_timings(struct v4l2_subdev *sd, struct v4l2_enum_dv_timings *timings) { + if (timings->pad != 0) + return -EINVAL; + /* Check requested format index is within range */ if (timings->index >= NUM_TIMINGS) return -EINVAL; @@ -924,7 +927,6 @@ static const struct v4l2_subdev_core_ops tvp7002_core_ops = { static const struct v4l2_subdev_video_ops tvp7002_video_ops = { .g_dv_timings = tvp7002_g_dv_timings, .s_dv_timings = tvp7002_s_dv_timings, - .enum_dv_timings = tvp7002_enum_dv_timings, .query_dv_timings = tvp7002_query_dv_timings, .s_stream = tvp7002_s_stream, .g_mbus_fmt = tvp7002_mbus_fmt, @@ -938,6 +940,7 @@ static const struct v4l2_subdev_pad_ops tvp7002_pad_ops = { .enum_mbus_code = tvp7002_enum_mbus_code, .get_fmt = tvp7002_get_pad_format, .set_fmt = tvp7002_set_pad_format, + .enum_dv_timings = tvp7002_enum_dv_timings, }; /* V4L2 top level operation handlers */ diff --git a/drivers/media/i2c/tw2804.c b/drivers/media/i2c/tw2804.c index f58607df6193..7347480c0b0c 100644 --- a/drivers/media/i2c/tw2804.c +++ b/drivers/media/i2c/tw2804.c @@ -342,12 +342,12 @@ static const struct v4l2_ctrl_ops tw2804_ctrl_ops = { }; static const struct v4l2_subdev_video_ops tw2804_video_ops = { + .s_std = tw2804_s_std, .s_routing = tw2804_s_video_routing, }; static const struct v4l2_subdev_core_ops tw2804_core_ops = { .log_status = tw2804_log_status, - .s_std = tw2804_s_std, }; static const struct v4l2_subdev_ops tw2804_ops = { diff --git a/drivers/media/i2c/tw9903.c b/drivers/media/i2c/tw9903.c index 285b759a5f7f..12c7d211a4a4 100644 --- a/drivers/media/i2c/tw9903.c +++ b/drivers/media/i2c/tw9903.c @@ -187,10 +187,10 @@ static const struct v4l2_ctrl_ops tw9903_ctrl_ops = { static const struct v4l2_subdev_core_ops tw9903_core_ops = { .log_status = tw9903_log_status, - .s_std = tw9903_s_std, }; static const struct v4l2_subdev_video_ops tw9903_video_ops = { + .s_std = tw9903_s_std, .s_routing = tw9903_s_video_routing, }; diff --git a/drivers/media/i2c/tw9906.c b/drivers/media/i2c/tw9906.c index f6bef25bd9ce..2672d89265ff 100644 --- a/drivers/media/i2c/tw9906.c +++ b/drivers/media/i2c/tw9906.c @@ -157,10 +157,10 @@ static const struct v4l2_ctrl_ops tw9906_ctrl_ops = { static const struct v4l2_subdev_core_ops tw9906_core_ops = { .log_status = tw9906_log_status, - .s_std = tw9906_s_std, }; static const struct v4l2_subdev_video_ops tw9906_video_ops = { + .s_std = tw9906_s_std, .s_routing = tw9906_s_video_routing, }; diff --git a/drivers/media/i2c/vp27smpx.c b/drivers/media/i2c/vp27smpx.c index 6a3a3ff7ee6a..819ab6d12989 100644 --- a/drivers/media/i2c/vp27smpx.c +++ b/drivers/media/i2c/vp27smpx.c @@ -124,7 +124,6 @@ static int vp27smpx_log_status(struct v4l2_subdev *sd) static const struct v4l2_subdev_core_ops vp27smpx_core_ops = { .log_status = vp27smpx_log_status, - .s_std = vp27smpx_s_std, }; static const struct v4l2_subdev_tuner_ops vp27smpx_tuner_ops = { @@ -133,9 +132,14 @@ static const struct v4l2_subdev_tuner_ops vp27smpx_tuner_ops = { .g_tuner = vp27smpx_g_tuner, }; +static const struct v4l2_subdev_video_ops vp27smpx_video_ops = { + .s_std = vp27smpx_s_std, +}; + static const struct v4l2_subdev_ops vp27smpx_ops = { .core = &vp27smpx_core_ops, .tuner = &vp27smpx_tuner_ops, + .video = &vp27smpx_video_ops, }; /* ----------------------------------------------------------------------- */ diff --git a/drivers/media/i2c/vpx3220.c b/drivers/media/i2c/vpx3220.c index ece90df6a043..016e766e72ba 100644 --- a/drivers/media/i2c/vpx3220.c +++ b/drivers/media/i2c/vpx3220.c @@ -457,10 +457,10 @@ static const struct v4l2_subdev_core_ops vpx3220_core_ops = { .s_ctrl = v4l2_subdev_s_ctrl, .queryctrl = v4l2_subdev_queryctrl, .querymenu = v4l2_subdev_querymenu, - .s_std = vpx3220_s_std, }; static const struct v4l2_subdev_video_ops vpx3220_video_ops = { + .s_std = vpx3220_s_std, .s_routing = vpx3220_s_routing, .s_stream = vpx3220_s_stream, .querystd = vpx3220_querystd, diff --git a/drivers/media/media-device.c b/drivers/media/media-device.c index 703560fa5e73..88b97c9e64ac 100644 --- a/drivers/media/media-device.c +++ b/drivers/media/media-device.c @@ -373,7 +373,8 @@ static void media_device_release(struct media_devnode *mdev) * - dev must point to the parent device * - model must be filled with the device model name */ -int __must_check media_device_register(struct media_device *mdev) +int __must_check __media_device_register(struct media_device *mdev, + struct module *owner) { int ret; @@ -389,7 +390,7 @@ int __must_check media_device_register(struct media_device *mdev) mdev->devnode.fops = &media_device_fops; mdev->devnode.parent = mdev->dev; mdev->devnode.release = media_device_release; - ret = media_devnode_register(&mdev->devnode); + ret = media_devnode_register(&mdev->devnode, owner); if (ret < 0) return ret; @@ -401,7 +402,7 @@ int __must_check media_device_register(struct media_device *mdev) return 0; } -EXPORT_SYMBOL_GPL(media_device_register); +EXPORT_SYMBOL_GPL(__media_device_register); /** * media_device_unregister - unregister a media device diff --git a/drivers/media/media-devnode.c b/drivers/media/media-devnode.c index fb0f0469fad7..7acd19c881de 100644 --- a/drivers/media/media-devnode.c +++ b/drivers/media/media-devnode.c @@ -232,7 +232,8 @@ static const struct file_operations media_devnode_fops = { * the media_devnode structure is *not* called, so the caller is responsible for * freeing any data. */ -int __must_check media_devnode_register(struct media_devnode *mdev) +int __must_check media_devnode_register(struct media_devnode *mdev, + struct module *owner) { int minor; int ret; @@ -253,7 +254,7 @@ int __must_check media_devnode_register(struct media_devnode *mdev) /* Part 2: Initialize and register the character device */ cdev_init(&mdev->cdev, &media_devnode_fops); - mdev->cdev.owner = mdev->fops->owner; + mdev->cdev.owner = owner; ret = cdev_add(&mdev->cdev, MKDEV(MAJOR(media_dev_t), mdev->minor), 1); if (ret < 0) { diff --git a/drivers/media/parport/bw-qcam.c b/drivers/media/parport/bw-qcam.c index 8a0e84c7d495..416507a83668 100644 --- a/drivers/media/parport/bw-qcam.c +++ b/drivers/media/parport/bw-qcam.c @@ -937,7 +937,7 @@ static struct qcam *qcam_init(struct parport *port) return NULL; v4l2_dev = &qcam->v4l2_dev; - snprintf(v4l2_dev->name, sizeof(v4l2_dev->name), "bw-qcam%d", num_cams); + snprintf(v4l2_dev->name, sizeof(v4l2_dev->name), "bw-qcam%u", num_cams); if (v4l2_device_register(port->dev, v4l2_dev) < 0) { v4l2_err(v4l2_dev, "Could not register v4l2_device\n"); diff --git a/drivers/media/pci/bt8xx/bttv-cards.c b/drivers/media/pci/bt8xx/bttv-cards.c index d06963b3dcf3..d8ec583c154c 100644 --- a/drivers/media/pci/bt8xx/bttv-cards.c +++ b/drivers/media/pci/bt8xx/bttv-cards.c @@ -52,6 +52,7 @@ static void osprey_eeprom(struct bttv *btv, const u8 ee[256]); static void modtec_eeprom(struct bttv *btv); static void init_PXC200(struct bttv *btv); static void init_RTV24(struct bttv *btv); +static void init_PCI8604PW(struct bttv *btv); static void rv605_muxsel(struct bttv *btv, unsigned int input); static void eagle_muxsel(struct bttv *btv, unsigned int input); @@ -2871,6 +2872,22 @@ struct tvcard bttv_tvcards[] = { .has_remote = 1, .has_radio = 1, }, + /* ---- card 0xa6---------------------------------- */ + [BTTV_BOARD_PCI_8604PW] = { + /* PCI-8604PW with special unlock sequence */ + .name = "PCI-8604PW", + .video_inputs = 2, + /* .audio_inputs= 0, */ + .svhs = NO_SVHS, + /* The second input is available on CN4, if populated. + * The other 5x2 header (CN2?) connects to the same inputs + * as the on-board BNCs */ + .muxsel = MUXSEL(2, 3), + .tuner_type = TUNER_ABSENT, + .no_msp34xx = 1, + .no_tda7432 = 1, + .pll = PLL_35, + }, }; static const unsigned int bttv_num_tvcards = ARRAY_SIZE(bttv_tvcards); @@ -3305,6 +3322,9 @@ void bttv_init_card1(struct bttv *btv) case BTTV_BOARD_ADLINK_RTV24: init_RTV24( btv ); break; + case BTTV_BOARD_PCI_8604PW: + init_PCI8604PW(btv); + break; } if (!bttv_tvcards[btv->c.type].has_dvb) @@ -4185,6 +4205,96 @@ init_RTV24 (struct bttv *btv) /* ----------------------------------------------------------------------- */ +/* + * The PCI-8604PW contains a CPLD, probably an ispMACH 4A, that filters + * the PCI REQ signals comming from the four BT878 chips. After power + * up, the CPLD does not forward requests to the bus, which prevents + * the BT878 from fetching RISC instructions from memory. While the + * CPLD is connected to most of the GPIOs of PCI device 0xD, only + * five appear to play a role in unlocking the REQ signal. The following + * sequence has been determined by trial and error without access to the + * original driver. + * + * Eight GPIOs of device 0xC are provided on connector CN4 (4 in, 4 out). + * Devices 0xE and 0xF do not appear to have anything connected to their + * GPIOs. + * + * The correct GPIO_OUT_EN value might have some more bits set. It should + * be possible to derive it from a boundary scan of the CPLD. Its JTAG + * pins are routed to test points. + * + */ +/* ----------------------------------------------------------------------- */ +static void +init_PCI8604PW(struct bttv *btv) +{ + int state; + + if ((PCI_SLOT(btv->c.pci->devfn) & ~3) != 0xC) { + pr_warn("This is not a PCI-8604PW\n"); + return; + } + + if (PCI_SLOT(btv->c.pci->devfn) != 0xD) + return; + + btwrite(0x080002, BT848_GPIO_OUT_EN); + + state = (btread(BT848_GPIO_DATA) >> 21) & 7; + + for (;;) { + switch (state) { + case 1: + case 5: + case 6: + case 4: + pr_debug("PCI-8604PW in state %i, toggling pin\n", + state); + btwrite(0x080000, BT848_GPIO_DATA); + msleep(1); + btwrite(0x000000, BT848_GPIO_DATA); + msleep(1); + break; + case 7: + pr_info("PCI-8604PW unlocked\n"); + return; + case 0: + /* FIXME: If we are in state 7 and toggle GPIO[19] one + more time, the CPLD goes into state 0, where PCI bus + mastering is inhibited again. We have not managed to + get out of that state. */ + + pr_err("PCI-8604PW locked until reset\n"); + return; + default: + pr_err("PCI-8604PW in unknown state %i\n", state); + return; + } + + state = (state << 4) | ((btread(BT848_GPIO_DATA) >> 21) & 7); + + switch (state) { + case 0x15: + case 0x56: + case 0x64: + case 0x47: + /* The transition from state 7 to state 0 is, as explained + above, valid but undesired and with this code impossible + as we exit as soon as we are in state 7. + case 0x70: */ + break; + default: + pr_err("PCI-8604PW invalid transition %i -> %i\n", + state >> 4, state & 7); + return; + } + state &= 7; + } +} + + + +/* ----------------------------------------------------------------------- */ /* Miro Pro radio stuff -- the tea5757 is connected to some GPIO ports */ /* * Copyright (c) 1999 Csaba Halasz <qgehali@uni-miskolc.hu> diff --git a/drivers/media/pci/bt8xx/bttv-driver.c b/drivers/media/pci/bt8xx/bttv-driver.c index afcd53bfcf8e..da780f42b121 100644 --- a/drivers/media/pci/bt8xx/bttv-driver.c +++ b/drivers/media/pci/bt8xx/bttv-driver.c @@ -1182,7 +1182,7 @@ set_tvnorm(struct bttv *btv, unsigned int norm) break; } id = tvnorm->v4l2_id; - bttv_call_all(btv, core, s_std, id); + bttv_call_all(btv, video, s_std, id); return 0; } diff --git a/drivers/media/pci/bt8xx/bttv.h b/drivers/media/pci/bt8xx/bttv.h index bb5da349a46e..f08126244662 100644 --- a/drivers/media/pci/bt8xx/bttv.h +++ b/drivers/media/pci/bt8xx/bttv.h @@ -189,6 +189,7 @@ #define BTTV_BOARD_BT848_CAP_14 0xa3 #define BTTV_BOARD_CYBERVISION_CV06 0xa4 #define BTTV_BOARD_KWORLD_VSTREAM_XPERT 0xa5 +#define BTTV_BOARD_PCI_8604PW 0xa6 /* more card-specific defines */ #define PT2254_L_CHANNEL 0x10 diff --git a/drivers/media/pci/bt8xx/dst.c b/drivers/media/pci/bt8xx/dst.c index 430b3eb11815..f2261dfe5d1a 100644 --- a/drivers/media/pci/bt8xx/dst.c +++ b/drivers/media/pci/bt8xx/dst.c @@ -1544,7 +1544,7 @@ static int dst_send_burst(struct dvb_frontend *fe, fe_sec_mini_cmd_t minicmd) } -static int dst_init(struct dvb_frontend *fe) +static int bt8xx_dst_init(struct dvb_frontend *fe) { struct dst_state *state = fe->demodulator_priv; @@ -1707,7 +1707,7 @@ static int dst_get_frontend(struct dvb_frontend *fe) return 0; } -static void dst_release(struct dvb_frontend *fe) +static void bt8xx_dst_release(struct dvb_frontend *fe) { struct dst_state *state = fe->demodulator_priv; if (state->dst_ca) { @@ -1776,8 +1776,8 @@ static struct dvb_frontend_ops dst_dvbt_ops = { FE_CAN_GUARD_INTERVAL_AUTO }, - .release = dst_release, - .init = dst_init, + .release = bt8xx_dst_release, + .init = bt8xx_dst_init, .tune = dst_tune_frontend, .set_frontend = dst_set_frontend, .get_frontend = dst_get_frontend, @@ -1801,8 +1801,8 @@ static struct dvb_frontend_ops dst_dvbs_ops = { .caps = FE_CAN_FEC_AUTO | FE_CAN_QPSK }, - .release = dst_release, - .init = dst_init, + .release = bt8xx_dst_release, + .init = bt8xx_dst_init, .tune = dst_tune_frontend, .set_frontend = dst_set_frontend, .get_frontend = dst_get_frontend, @@ -1834,8 +1834,8 @@ static struct dvb_frontend_ops dst_dvbc_ops = { FE_CAN_QAM_256 }, - .release = dst_release, - .init = dst_init, + .release = bt8xx_dst_release, + .init = bt8xx_dst_init, .tune = dst_tune_frontend, .set_frontend = dst_set_frontend, .get_frontend = dst_get_frontend, @@ -1857,8 +1857,8 @@ static struct dvb_frontend_ops dst_atsc_ops = { .caps = FE_CAN_FEC_AUTO | FE_CAN_QAM_AUTO | FE_CAN_QAM_64 | FE_CAN_QAM_256 | FE_CAN_8VSB }, - .release = dst_release, - .init = dst_init, + .release = bt8xx_dst_release, + .init = bt8xx_dst_init, .tune = dst_tune_frontend, .set_frontend = dst_set_frontend, .get_frontend = dst_get_frontend, diff --git a/drivers/media/pci/cx18/cx18-av-core.c b/drivers/media/pci/cx18/cx18-av-core.c index c4890a430dc6..2d3afe0431a9 100644 --- a/drivers/media/pci/cx18/cx18-av-core.c +++ b/drivers/media/pci/cx18/cx18-av-core.c @@ -1263,7 +1263,6 @@ static const struct v4l2_subdev_core_ops cx18_av_general_ops = { .log_status = cx18_av_log_status, .load_fw = cx18_av_load_fw, .reset = cx18_av_reset, - .s_std = cx18_av_s_std, #ifdef CONFIG_VIDEO_ADV_DEBUG .g_register = cx18_av_g_register, .s_register = cx18_av_s_register, @@ -1283,6 +1282,7 @@ static const struct v4l2_subdev_audio_ops cx18_av_audio_ops = { }; static const struct v4l2_subdev_video_ops cx18_av_video_ops = { + .s_std = cx18_av_s_std, .s_routing = cx18_av_s_video_routing, .s_stream = cx18_av_s_stream, .s_mbus_fmt = cx18_av_s_mbus_fmt, diff --git a/drivers/media/pci/cx18/cx18-fileops.c b/drivers/media/pci/cx18/cx18-fileops.c index 4bfd865a4106..76a3b4ac541e 100644 --- a/drivers/media/pci/cx18/cx18-fileops.c +++ b/drivers/media/pci/cx18/cx18-fileops.c @@ -760,7 +760,7 @@ int cx18_v4l2_close(struct file *filp) /* Mark that the radio is no longer in use */ clear_bit(CX18_F_I_RADIO_USER, &cx->i_flags); /* Switch tuner to TV */ - cx18_call_all(cx, core, s_std, cx->std); + cx18_call_all(cx, video, s_std, cx->std); /* Select correct audio input (i.e. TV tuner or Line in) */ cx18_audio_set_io(cx); if (atomic_read(&cx->ana_capturing) > 0) { diff --git a/drivers/media/pci/cx18/cx18-gpio.c b/drivers/media/pci/cx18/cx18-gpio.c index 5374aeb0cd22..38dc6b8f8254 100644 --- a/drivers/media/pci/cx18/cx18-gpio.c +++ b/drivers/media/pci/cx18/cx18-gpio.c @@ -180,7 +180,6 @@ static int gpiomux_s_audio_routing(struct v4l2_subdev *sd, static const struct v4l2_subdev_core_ops gpiomux_core_ops = { .log_status = gpiomux_log_status, - .s_std = gpiomux_s_std, }; static const struct v4l2_subdev_tuner_ops gpiomux_tuner_ops = { @@ -191,10 +190,15 @@ static const struct v4l2_subdev_audio_ops gpiomux_audio_ops = { .s_routing = gpiomux_s_audio_routing, }; +static const struct v4l2_subdev_video_ops gpiomux_video_ops = { + .s_std = gpiomux_s_std, +}; + static const struct v4l2_subdev_ops gpiomux_ops = { .core = &gpiomux_core_ops, .tuner = &gpiomux_tuner_ops, .audio = &gpiomux_audio_ops, + .video = &gpiomux_video_ops, }; /* diff --git a/drivers/media/pci/cx18/cx18-ioctl.c b/drivers/media/pci/cx18/cx18-ioctl.c index 1110bcb14e2f..fefb2cd35838 100644 --- a/drivers/media/pci/cx18/cx18-ioctl.c +++ b/drivers/media/pci/cx18/cx18-ioctl.c @@ -602,7 +602,7 @@ int cx18_s_std(struct file *file, void *fh, v4l2_std_id std) (unsigned long long) cx->std); /* Tuner */ - cx18_call_all(cx, core, s_std, cx->std); + cx18_call_all(cx, video, s_std, cx->std); return 0; } diff --git a/drivers/media/pci/cx23885/cx23885-video.c b/drivers/media/pci/cx23885/cx23885-video.c index 7891f34157d1..e0a59523cf3c 100644 --- a/drivers/media/pci/cx23885/cx23885-video.c +++ b/drivers/media/pci/cx23885/cx23885-video.c @@ -326,7 +326,7 @@ int cx23885_set_tvnorm(struct cx23885_dev *dev, v4l2_std_id norm) dev->tvnorm = norm; - call_all(dev, core, s_std, norm); + call_all(dev, video, s_std, norm); return 0; } @@ -1589,7 +1589,7 @@ static int cx23885_set_freq_via_ops(struct cx23885_dev *dev, fe = &dev->ts1.analog_fe; if (fe && fe->ops.tuner_ops.set_analog_params) { - call_all(dev, core, s_std, dev->tvnorm); + call_all(dev, video, s_std, dev->tvnorm); fe->ops.tuner_ops.set_analog_params(fe, ¶ms); } else diff --git a/drivers/media/pci/cx88/cx88-core.c b/drivers/media/pci/cx88/cx88-core.c index ad59dc9235ae..e061c88b697e 100644 --- a/drivers/media/pci/cx88/cx88-core.c +++ b/drivers/media/pci/cx88/cx88-core.c @@ -1012,7 +1012,7 @@ int cx88_set_tvnorm(struct cx88_core *core, v4l2_std_id norm) set_tvaudio(core); // tell i2c chips - call_all(core, core, s_std, norm); + call_all(core, video, s_std, norm); /* The chroma_agc control should be inaccessible if the video format is SECAM */ v4l2_ctrl_grab(core->chroma_agc, cxiformat == VideoFormatSECAM); diff --git a/drivers/media/pci/ivtv/ivtv-alsa-pcm.c b/drivers/media/pci/ivtv/ivtv-alsa-pcm.c index e1863dbf4edc..7a9b98bc208b 100644 --- a/drivers/media/pci/ivtv/ivtv-alsa-pcm.c +++ b/drivers/media/pci/ivtv/ivtv-alsa-pcm.c @@ -159,6 +159,12 @@ static int snd_ivtv_pcm_capture_open(struct snd_pcm_substream *substream) /* Instruct the CX2341[56] to start sending packets */ snd_ivtv_lock(itvsc); + + if (ivtv_init_on_first_open(itv)) { + snd_ivtv_unlock(itvsc); + return -ENXIO; + } + s = &itv->streams[IVTV_ENC_STREAM_TYPE_PCM]; v4l2_fh_init(&item.fh, s->vdev); diff --git a/drivers/media/pci/ivtv/ivtv-fileops.c b/drivers/media/pci/ivtv/ivtv-fileops.c index 9caffd8aa995..e5ff6277ca85 100644 --- a/drivers/media/pci/ivtv/ivtv-fileops.c +++ b/drivers/media/pci/ivtv/ivtv-fileops.c @@ -894,7 +894,7 @@ int ivtv_v4l2_close(struct file *filp) /* Mark that the radio is no longer in use */ clear_bit(IVTV_F_I_RADIO_USER, &itv->i_flags); /* Switch tuner to TV */ - ivtv_call_all(itv, core, s_std, itv->std); + ivtv_call_all(itv, video, s_std, itv->std); /* Select correct audio input (i.e. TV tuner or Line in) */ ivtv_audio_set_io(itv); if (itv->hw_flags & IVTV_HW_SAA711X) { diff --git a/drivers/media/pci/ivtv/ivtv-ioctl.c b/drivers/media/pci/ivtv/ivtv-ioctl.c index 807b275a847e..b3667a00db3a 100644 --- a/drivers/media/pci/ivtv/ivtv-ioctl.c +++ b/drivers/media/pci/ivtv/ivtv-ioctl.c @@ -1090,7 +1090,7 @@ void ivtv_s_std_enc(struct ivtv *itv, v4l2_std_id std) itv->vbi.sliced_decoder_line_size = itv->is_60hz ? 272 : 284; /* Tuner */ - ivtv_call_all(itv, core, s_std, itv->std); + ivtv_call_all(itv, video, s_std, itv->std); } void ivtv_s_std_dec(struct ivtv *itv, v4l2_std_id std) diff --git a/drivers/media/pci/saa7134/Kconfig b/drivers/media/pci/saa7134/Kconfig index 7883393571e5..18ae75546302 100644 --- a/drivers/media/pci/saa7134/Kconfig +++ b/drivers/media/pci/saa7134/Kconfig @@ -1,7 +1,7 @@ config VIDEO_SAA7134 tristate "Philips SAA7134 support" depends on VIDEO_DEV && PCI && I2C - select VIDEOBUF_DMA_SG + select VIDEOBUF2_DMA_SG select VIDEO_TUNER select VIDEO_TVEEPROM select CRC32 @@ -37,7 +37,7 @@ config VIDEO_SAA7134_RC config VIDEO_SAA7134_DVB tristate "DVB/ATSC Support for saa7134 based TV cards" depends on VIDEO_SAA7134 && DVB_CORE - select VIDEOBUF_DVB + select VIDEOBUF2_DVB select DVB_PLL if MEDIA_SUBDRV_AUTOSELECT select DVB_MT352 if MEDIA_SUBDRV_AUTOSELECT select DVB_TDA1004X if MEDIA_SUBDRV_AUTOSELECT diff --git a/drivers/media/pci/saa7134/saa7134-alsa.c b/drivers/media/pci/saa7134/saa7134-alsa.c index e04a4d5d6672..40569894c1c9 100644 --- a/drivers/media/pci/saa7134/saa7134-alsa.c +++ b/drivers/media/pci/saa7134/saa7134-alsa.c @@ -27,6 +27,7 @@ #include <sound/pcm_params.h> #include <sound/initval.h> #include <linux/interrupt.h> +#include <linux/vmalloc.h> #include "saa7134.h" #include "saa7134-reg.h" @@ -274,6 +275,82 @@ static int snd_card_saa7134_capture_trigger(struct snd_pcm_substream * substream return err; } +static int saa7134_alsa_dma_init(struct saa7134_dev *dev, int nr_pages) +{ + struct saa7134_dmasound *dma = &dev->dmasound; + struct page *pg; + int i; + + dma->vaddr = vmalloc_32(nr_pages << PAGE_SHIFT); + if (NULL == dma->vaddr) { + dprintk("vmalloc_32(%d pages) failed\n", nr_pages); + return -ENOMEM; + } + + dprintk("vmalloc is at addr 0x%08lx, size=%d\n", + (unsigned long)dma->vaddr, + nr_pages << PAGE_SHIFT); + + memset(dma->vaddr, 0, nr_pages << PAGE_SHIFT); + dma->nr_pages = nr_pages; + + dma->sglist = vzalloc(dma->nr_pages * sizeof(*dma->sglist)); + if (NULL == dma->sglist) + goto vzalloc_err; + + sg_init_table(dma->sglist, dma->nr_pages); + for (i = 0; i < dma->nr_pages; i++) { + pg = vmalloc_to_page(dma->vaddr + i * PAGE_SIZE); + if (NULL == pg) + goto vmalloc_to_page_err; + sg_set_page(&dma->sglist[i], pg, PAGE_SIZE, 0); + } + return 0; + +vmalloc_to_page_err: + vfree(dma->sglist); + dma->sglist = NULL; +vzalloc_err: + vfree(dma->vaddr); + dma->vaddr = NULL; + return -ENOMEM; +} + +static int saa7134_alsa_dma_map(struct saa7134_dev *dev) +{ + struct saa7134_dmasound *dma = &dev->dmasound; + + dma->sglen = dma_map_sg(&dev->pci->dev, dma->sglist, + dma->nr_pages, PCI_DMA_FROMDEVICE); + + if (0 == dma->sglen) { + pr_warn("%s: saa7134_alsa_map_sg failed\n", __func__); + return -ENOMEM; + } + return 0; +} + +static int saa7134_alsa_dma_unmap(struct saa7134_dev *dev) +{ + struct saa7134_dmasound *dma = &dev->dmasound; + + if (!dma->sglen) + return 0; + + dma_unmap_sg(&dev->pci->dev, dma->sglist, dma->sglen, PCI_DMA_FROMDEVICE); + dma->sglen = 0; + return 0; +} + +static int saa7134_alsa_dma_free(struct saa7134_dmasound *dma) +{ + vfree(dma->sglist); + dma->sglist = NULL; + vfree(dma->vaddr); + dma->vaddr = NULL; + return 0; +} + /* * DMA buffer initialization * @@ -291,9 +368,8 @@ static int dsp_buffer_init(struct saa7134_dev *dev) BUG_ON(!dev->dmasound.bufsize); - videobuf_dma_init(&dev->dmasound.dma); - err = videobuf_dma_init_kernel(&dev->dmasound.dma, PCI_DMA_FROMDEVICE, - (dev->dmasound.bufsize + PAGE_SIZE) >> PAGE_SHIFT); + err = saa7134_alsa_dma_init(dev, + (dev->dmasound.bufsize + PAGE_SIZE) >> PAGE_SHIFT); if (0 != err) return err; return 0; @@ -310,7 +386,7 @@ static int dsp_buffer_free(struct saa7134_dev *dev) { BUG_ON(!dev->dmasound.blksize); - videobuf_dma_free(&dev->dmasound.dma); + saa7134_alsa_dma_free(&dev->dmasound); dev->dmasound.blocks = 0; dev->dmasound.blksize = 0; @@ -632,7 +708,7 @@ static int snd_card_saa7134_hw_params(struct snd_pcm_substream * substream, /* release the old buffer */ if (substream->runtime->dma_area) { saa7134_pgtable_free(dev->pci, &dev->dmasound.pt); - videobuf_dma_unmap(&dev->pci->dev, &dev->dmasound.dma); + saa7134_alsa_dma_unmap(dev); dsp_buffer_free(dev); substream->runtime->dma_area = NULL; } @@ -648,21 +724,22 @@ static int snd_card_saa7134_hw_params(struct snd_pcm_substream * substream, return err; } - if (0 != (err = videobuf_dma_map(&dev->pci->dev, &dev->dmasound.dma))) { + err = saa7134_alsa_dma_map(dev); + if (err) { dsp_buffer_free(dev); return err; } - if (0 != (err = saa7134_pgtable_alloc(dev->pci,&dev->dmasound.pt))) { - videobuf_dma_unmap(&dev->pci->dev, &dev->dmasound.dma); + err = saa7134_pgtable_alloc(dev->pci, &dev->dmasound.pt); + if (err) { + saa7134_alsa_dma_unmap(dev); dsp_buffer_free(dev); return err; } - if (0 != (err = saa7134_pgtable_build(dev->pci,&dev->dmasound.pt, - dev->dmasound.dma.sglist, - dev->dmasound.dma.sglen, - 0))) { + err = saa7134_pgtable_build(dev->pci, &dev->dmasound.pt, + dev->dmasound.sglist, dev->dmasound.sglen, 0); + if (err) { saa7134_pgtable_free(dev->pci, &dev->dmasound.pt); - videobuf_dma_unmap(&dev->pci->dev, &dev->dmasound.dma); + saa7134_alsa_dma_unmap(dev); dsp_buffer_free(dev); return err; } @@ -671,7 +748,7 @@ static int snd_card_saa7134_hw_params(struct snd_pcm_substream * substream, byte, but it doesn't work. So I allocate the DMA using the V4L functions, and force ALSA to use that as the DMA area */ - substream->runtime->dma_area = dev->dmasound.dma.vaddr; + substream->runtime->dma_area = dev->dmasound.vaddr; substream->runtime->dma_bytes = dev->dmasound.bufsize; substream->runtime->dma_addr = 0; @@ -698,7 +775,7 @@ static int snd_card_saa7134_hw_free(struct snd_pcm_substream * substream) if (substream->runtime->dma_area) { saa7134_pgtable_free(dev->pci, &dev->dmasound.pt); - videobuf_dma_unmap(&dev->pci->dev, &dev->dmasound.dma); + saa7134_alsa_dma_unmap(dev); dsp_buffer_free(dev); substream->runtime->dma_area = NULL; } diff --git a/drivers/media/pci/saa7134/saa7134-core.c b/drivers/media/pci/saa7134/saa7134-core.c index 1362b4aab473..be19a051a492 100644 --- a/drivers/media/pci/saa7134/saa7134-core.c +++ b/drivers/media/pci/saa7134/saa7134-core.c @@ -69,6 +69,10 @@ module_param_named(no_overlay, saa7134_no_overlay, int, 0444); MODULE_PARM_DESC(no_overlay,"allow override overlay default (0 disables, 1 enables)" " [some VIA/SIS chipsets are known to have problem with overlay]"); +bool saa7134_userptr; +module_param(saa7134_userptr, bool, 0644); +MODULE_PARM_DESC(saa7134_userptr, "enable page-aligned userptr support"); + static unsigned int video_nr[] = {[0 ... (SAA7134_MAXBOARDS - 1)] = UNSET }; static unsigned int vbi_nr[] = {[0 ... (SAA7134_MAXBOARDS - 1)] = UNSET }; static unsigned int radio_nr[] = {[0 ... (SAA7134_MAXBOARDS - 1)] = UNSET }; @@ -203,16 +207,16 @@ int saa7134_buffer_count(unsigned int size, unsigned int count) int saa7134_buffer_startpage(struct saa7134_buf *buf) { - return saa7134_buffer_pages(buf->vb.bsize) * buf->vb.i; + return saa7134_buffer_pages(vb2_plane_size(&buf->vb2, 0)) * buf->vb2.v4l2_buf.index; } unsigned long saa7134_buffer_base(struct saa7134_buf *buf) { unsigned long base; - struct videobuf_dmabuf *dma=videobuf_to_dma(&buf->vb); + struct sg_table *dma = vb2_dma_sg_plane_desc(&buf->vb2, 0); base = saa7134_buffer_startpage(buf) * 4096; - base += dma->sglist[0].offset; + base += dma->sgl[0].offset; return base; } @@ -237,14 +241,16 @@ int saa7134_pgtable_build(struct pci_dev *pci, struct saa7134_pgtable *pt, unsigned int startpage) { __le32 *ptr; - unsigned int i,p; + unsigned int i, p; BUG_ON(NULL == pt || NULL == pt->cpu); ptr = pt->cpu + startpage; - for (i = 0; i < length; i++, list++) + for (i = 0; i < length; i++, list = sg_next(list)) { for (p = 0; p * 4096 < list->length; p++, ptr++) - *ptr = cpu_to_le32(sg_dma_address(list) - list->offset); + *ptr = cpu_to_le32(sg_dma_address(list) + + list->offset + p * 4096); + } return 0; } @@ -258,44 +264,31 @@ void saa7134_pgtable_free(struct pci_dev *pci, struct saa7134_pgtable *pt) /* ------------------------------------------------------------------ */ -void saa7134_dma_free(struct videobuf_queue *q,struct saa7134_buf *buf) -{ - struct videobuf_dmabuf *dma=videobuf_to_dma(&buf->vb); - BUG_ON(in_interrupt()); - - videobuf_waiton(q, &buf->vb, 0, 0); - videobuf_dma_unmap(q->dev, dma); - videobuf_dma_free(dma); - buf->vb.state = VIDEOBUF_NEEDS_INIT; -} - -/* ------------------------------------------------------------------ */ - int saa7134_buffer_queue(struct saa7134_dev *dev, struct saa7134_dmaqueue *q, struct saa7134_buf *buf) { struct saa7134_buf *next = NULL; + unsigned long flags; - assert_spin_locked(&dev->slock); - dprintk("buffer_queue %p\n",buf); + spin_lock_irqsave(&dev->slock, flags); + dprintk("buffer_queue %p\n", buf); if (NULL == q->curr) { if (!q->need_two) { q->curr = buf; - buf->activate(dev,buf,NULL); + buf->activate(dev, buf, NULL); } else if (list_empty(&q->queue)) { - list_add_tail(&buf->vb.queue,&q->queue); - buf->vb.state = VIDEOBUF_QUEUED; + list_add_tail(&buf->entry, &q->queue); } else { - next = list_entry(q->queue.next,struct saa7134_buf, - vb.queue); + next = list_entry(q->queue.next, struct saa7134_buf, + entry); q->curr = buf; - buf->activate(dev,buf,next); + buf->activate(dev, buf, next); } } else { - list_add_tail(&buf->vb.queue,&q->queue); - buf->vb.state = VIDEOBUF_QUEUED; + list_add_tail(&buf->entry, &q->queue); } + spin_unlock_irqrestore(&dev->slock, flags); return 0; } @@ -303,13 +296,12 @@ void saa7134_buffer_finish(struct saa7134_dev *dev, struct saa7134_dmaqueue *q, unsigned int state) { - assert_spin_locked(&dev->slock); - dprintk("buffer_finish %p\n",q->curr); + dprintk("buffer_finish %p\n", q->curr); /* finish current buffer */ - q->curr->vb.state = state; - v4l2_get_timestamp(&q->curr->vb.ts); - wake_up(&q->curr->vb.done); + v4l2_get_timestamp(&q->curr->vb2.v4l2_buf.timestamp); + q->curr->vb2.v4l2_buf.sequence = q->seq_nr++; + vb2_buffer_done(&q->curr->vb2, state); q->curr = NULL; } @@ -323,36 +315,31 @@ void saa7134_buffer_next(struct saa7134_dev *dev, if (!list_empty(&q->queue)) { /* activate next one from queue */ - buf = list_entry(q->queue.next,struct saa7134_buf,vb.queue); + buf = list_entry(q->queue.next, struct saa7134_buf, entry); dprintk("buffer_next %p [prev=%p/next=%p]\n", - buf,q->queue.prev,q->queue.next); - list_del(&buf->vb.queue); + buf, q->queue.prev, q->queue.next); + list_del(&buf->entry); if (!list_empty(&q->queue)) - next = list_entry(q->queue.next,struct saa7134_buf, - vb.queue); + next = list_entry(q->queue.next, struct saa7134_buf, entry); q->curr = buf; - buf->activate(dev,buf,next); + buf->activate(dev, buf, next); dprintk("buffer_next #2 prev=%p/next=%p\n", - q->queue.prev,q->queue.next); + q->queue.prev, q->queue.next); } else { /* nothing to do -- just stop DMA */ - dprintk("buffer_next %p\n",NULL); + dprintk("buffer_next %p\n", NULL); saa7134_set_dmabits(dev); del_timer(&q->timeout); - - if (card_has_mpeg(dev)) - if (dev->ts_started) - saa7134_ts_stop(dev); } } void saa7134_buffer_timeout(unsigned long data) { - struct saa7134_dmaqueue *q = (struct saa7134_dmaqueue*)data; + struct saa7134_dmaqueue *q = (struct saa7134_dmaqueue *)data; struct saa7134_dev *dev = q->dev; unsigned long flags; - spin_lock_irqsave(&dev->slock,flags); + spin_lock_irqsave(&dev->slock, flags); /* try to reset the hardware (SWRST) */ saa_writeb(SAA7134_REGION_ENABLE, 0x00); @@ -362,13 +349,33 @@ void saa7134_buffer_timeout(unsigned long data) /* flag current buffer as failed, try to start over with the next one. */ if (q->curr) { - dprintk("timeout on %p\n",q->curr); - saa7134_buffer_finish(dev,q,VIDEOBUF_ERROR); + dprintk("timeout on %p\n", q->curr); + saa7134_buffer_finish(dev, q, VB2_BUF_STATE_ERROR); } - saa7134_buffer_next(dev,q); - spin_unlock_irqrestore(&dev->slock,flags); + saa7134_buffer_next(dev, q); + spin_unlock_irqrestore(&dev->slock, flags); } +void saa7134_stop_streaming(struct saa7134_dev *dev, struct saa7134_dmaqueue *q) +{ + unsigned long flags; + struct list_head *pos, *n; + struct saa7134_buf *tmp; + + spin_lock_irqsave(&dev->slock, flags); + if (!list_empty(&q->queue)) { + list_for_each_safe(pos, n, &q->queue) { + tmp = list_entry(pos, struct saa7134_buf, entry); + vb2_buffer_done(&tmp->vb2, VB2_BUF_STATE_ERROR); + list_del(pos); + tmp = NULL; + } + } + spin_unlock_irqrestore(&dev->slock, flags); + saa7134_buffer_timeout((unsigned long)q); /* also calls del_timer(&q->timeout) */ +} +EXPORT_SYMBOL_GPL(saa7134_stop_streaming); + /* ------------------------------------------------------------------ */ int saa7134_set_dmabits(struct saa7134_dev *dev) @@ -388,12 +395,11 @@ int saa7134_set_dmabits(struct saa7134_dev *dev) ctrl |= SAA7134_MAIN_CTRL_TE0; irq |= SAA7134_IRQ1_INTE_RA0_1 | SAA7134_IRQ1_INTE_RA0_0; - cap = dev->video_q.curr->vb.field; + cap = dev->field; } /* video capture -- dma 1+2 (planar modes) */ - if (dev->video_q.curr && - dev->video_q.curr->fmt->planar) { + if (dev->video_q.curr && dev->fmt->planar) { ctrl |= SAA7134_MAIN_CTRL_TE4 | SAA7134_MAIN_CTRL_TE5; } @@ -1047,6 +1053,8 @@ static int saa7134_initdev(struct pci_dev *pci_dev, dev->video_dev = vdev_init(dev,&saa7134_video_template,"video"); dev->video_dev->ctrl_handler = &dev->ctrl_handler; + dev->video_dev->lock = &dev->lock; + dev->video_dev->queue = &dev->video_vbq; err = video_register_device(dev->video_dev,VFL_TYPE_GRABBER, video_nr[dev->nr]); if (err < 0) { @@ -1059,6 +1067,8 @@ static int saa7134_initdev(struct pci_dev *pci_dev, dev->vbi_dev = vdev_init(dev, &saa7134_video_template, "vbi"); dev->vbi_dev->ctrl_handler = &dev->ctrl_handler; + dev->vbi_dev->lock = &dev->lock; + dev->vbi_dev->queue = &dev->vbi_vbq; err = video_register_device(dev->vbi_dev,VFL_TYPE_VBI, vbi_nr[dev->nr]); @@ -1070,6 +1080,7 @@ static int saa7134_initdev(struct pci_dev *pci_dev, if (card_has_radio(dev)) { dev->radio_dev = vdev_init(dev,&saa7134_radio_template,"radio"); dev->radio_dev->ctrl_handler = &dev->radio_ctrl_handler; + dev->radio_dev->lock = &dev->lock; err = video_register_device(dev->radio_dev,VFL_TYPE_RADIO, radio_nr[dev->nr]); if (err < 0) @@ -1189,7 +1200,7 @@ static int saa7134_buffer_requeue(struct saa7134_dev *dev, if (!list_empty(&q->queue)) next = list_entry(q->queue.next, struct saa7134_buf, - vb.queue); + entry); buf->activate(dev, buf, next); return 0; @@ -1360,10 +1371,3 @@ EXPORT_SYMBOL(saa7134_pgtable_free); EXPORT_SYMBOL(saa7134_pgtable_build); EXPORT_SYMBOL(saa7134_pgtable_alloc); EXPORT_SYMBOL(saa7134_set_dmabits); - -/* ----------------------------------------------------------- */ -/* - * Local variables: - * c-basic-offset: 8 - * End: - */ diff --git a/drivers/media/pci/saa7134/saa7134-dvb.c b/drivers/media/pci/saa7134/saa7134-dvb.c index 4a08ae31df2e..73ffbabf831c 100644 --- a/drivers/media/pci/saa7134/saa7134-dvb.c +++ b/drivers/media/pci/saa7134/saa7134-dvb.c @@ -602,10 +602,10 @@ static int configure_tda827x_fe(struct saa7134_dev *dev, struct tda1004x_config *cdec_conf, struct tda827x_config *tuner_conf) { - struct videobuf_dvb_frontend *fe0; + struct vb2_dvb_frontend *fe0; /* Get the first frontend */ - fe0 = videobuf_dvb_get_frontend(&dev->frontends, 1); + fe0 = vb2_dvb_get_frontend(&dev->frontends, 1); if (!fe0) return -EINVAL; @@ -1215,29 +1215,38 @@ static int dvb_init(struct saa7134_dev *dev) { int ret; int attach_xc3028 = 0; - struct videobuf_dvb_frontend *fe0; + struct vb2_dvb_frontend *fe0; + struct vb2_queue *q; /* FIXME: add support for multi-frontend */ mutex_init(&dev->frontends.lock); INIT_LIST_HEAD(&dev->frontends.felist); printk(KERN_INFO "%s() allocating 1 frontend\n", __func__); - fe0 = videobuf_dvb_alloc_frontend(&dev->frontends, 1); + fe0 = vb2_dvb_alloc_frontend(&dev->frontends, 1); if (!fe0) { printk(KERN_ERR "%s() failed to alloc\n", __func__); return -ENOMEM; } - /* init struct videobuf_dvb */ + /* init struct vb2_dvb */ dev->ts.nr_bufs = 32; dev->ts.nr_packets = 32*4; fe0->dvb.name = dev->name; - videobuf_queue_sg_init(&fe0->dvb.dvbq, &saa7134_ts_qops, - &dev->pci->dev, &dev->slock, - V4L2_BUF_TYPE_VIDEO_CAPTURE, - V4L2_FIELD_ALTERNATE, - sizeof(struct saa7134_buf), - dev, NULL); + q = &fe0->dvb.dvbq; + q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + q->io_modes = VB2_MMAP | VB2_READ; + q->drv_priv = &dev->ts_q; + q->ops = &saa7134_ts_qops; + q->mem_ops = &vb2_dma_sg_memops; + q->buf_struct_size = sizeof(struct saa7134_buf); + q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + q->lock = &dev->lock; + ret = vb2_queue_init(q); + if (ret) { + vb2_dvb_dealloc_frontends(&dev->frontends); + return ret; + } switch (dev->board) { case SAA7134_BOARD_PINNACLE_300I_DVBT_PAL: @@ -1876,7 +1885,7 @@ static int dvb_init(struct saa7134_dev *dev) fe0->dvb.frontend->callback = saa7134_tuner_callback; /* register everything else */ - ret = videobuf_dvb_register_bus(&dev->frontends, THIS_MODULE, dev, + ret = vb2_dvb_register_bus(&dev->frontends, THIS_MODULE, dev, &dev->pci->dev, adapter_nr, 0); /* this sequence is necessary to make the tda1004x load its firmware @@ -1893,16 +1902,17 @@ static int dvb_init(struct saa7134_dev *dev) return ret; detach_frontend: - videobuf_dvb_dealloc_frontends(&dev->frontends); + vb2_dvb_dealloc_frontends(&dev->frontends); + vb2_queue_release(&fe0->dvb.dvbq); return -EINVAL; } static int dvb_fini(struct saa7134_dev *dev) { - struct videobuf_dvb_frontend *fe0; + struct vb2_dvb_frontend *fe0; /* Get the first frontend */ - fe0 = videobuf_dvb_get_frontend(&dev->frontends, 1); + fe0 = vb2_dvb_get_frontend(&dev->frontends, 1); if (!fe0) return -EINVAL; @@ -1933,7 +1943,8 @@ static int dvb_fini(struct saa7134_dev *dev) } } } - videobuf_dvb_unregister_bus(&dev->frontends); + vb2_dvb_unregister_bus(&dev->frontends); + vb2_queue_release(&fe0->dvb.dvbq); return 0; } @@ -1955,10 +1966,3 @@ static void __exit dvb_unregister(void) module_init(dvb_register); module_exit(dvb_unregister); - -/* ------------------------------------------------------------------ */ -/* - * Local variables: - * c-basic-offset: 8 - * End: - */ diff --git a/drivers/media/pci/saa7134/saa7134-empress.c b/drivers/media/pci/saa7134/saa7134-empress.c index 0a9047e754b9..e65c760e4e8b 100644 --- a/drivers/media/pci/saa7134/saa7134-empress.c +++ b/drivers/media/pci/saa7134/saa7134-empress.c @@ -48,21 +48,16 @@ MODULE_PARM_DESC(debug,"enable debug messages"); /* ------------------------------------------------------------------ */ -static void ts_reset_encoder(struct saa7134_dev* dev) -{ - if (!dev->empress_started) - return; - - saa_writeb(SAA7134_SPECIAL_MODE, 0x00); - msleep(10); - saa_writeb(SAA7134_SPECIAL_MODE, 0x01); - msleep(100); - dev->empress_started = 0; -} - -static int ts_init_encoder(struct saa7134_dev* dev) +static int start_streaming(struct vb2_queue *vq, unsigned int count) { + struct saa7134_dmaqueue *dmaq = vq->drv_priv; + struct saa7134_dev *dev = dmaq->dev; u32 leading_null_bytes = 0; + int err; + + err = saa7134_ts_start_streaming(vq, count); + if (err) + return err; /* If more cards start to need this, then this should probably be added to the card definitions. */ @@ -73,97 +68,43 @@ static int ts_init_encoder(struct saa7134_dev* dev) leading_null_bytes = 1; break; } - ts_reset_encoder(dev); saa_call_all(dev, core, init, leading_null_bytes); - dev->empress_started = 1; - return 0; -} - -/* ------------------------------------------------------------------ */ - -static int ts_open(struct file *file) -{ - struct video_device *vdev = video_devdata(file); - struct saa7134_dev *dev = video_drvdata(file); - struct saa7134_fh *fh; - - /* allocate + initialize per filehandle data */ - fh = kzalloc(sizeof(*fh), GFP_KERNEL); - if (NULL == fh) - return -ENOMEM; - - v4l2_fh_init(&fh->fh, vdev); - file->private_data = fh; - fh->is_empress = true; - v4l2_fh_add(&fh->fh); - /* Unmute audio */ saa_writeb(SAA7134_AUDIO_MUTE_CTRL, - saa_readb(SAA7134_AUDIO_MUTE_CTRL) & ~(1 << 6)); - - return 0; -} - -static int ts_release(struct file *file) -{ - struct saa7134_dev *dev = video_drvdata(file); - struct saa7134_fh *fh = file->private_data; - - if (res_check(fh, RESOURCE_EMPRESS)) { - videobuf_stop(&dev->empress_tsq); - videobuf_mmap_free(&dev->empress_tsq); - - /* stop the encoder */ - ts_reset_encoder(dev); - - /* Mute audio */ - saa_writeb(SAA7134_AUDIO_MUTE_CTRL, - saa_readb(SAA7134_AUDIO_MUTE_CTRL) | (1 << 6)); - } - - v4l2_fh_del(&fh->fh); - v4l2_fh_exit(&fh->fh); + saa_readb(SAA7134_AUDIO_MUTE_CTRL) & ~(1 << 6)); + dev->empress_started = 1; return 0; } -static ssize_t -ts_read(struct file *file, char __user *data, size_t count, loff_t *ppos) +static void stop_streaming(struct vb2_queue *vq) { - struct saa7134_dev *dev = video_drvdata(file); + struct saa7134_dmaqueue *dmaq = vq->drv_priv; + struct saa7134_dev *dev = dmaq->dev; - if (res_locked(dev, RESOURCE_EMPRESS)) - return -EBUSY; - if (!dev->empress_started) - ts_init_encoder(dev); - - return videobuf_read_stream(&dev->empress_tsq, - data, count, ppos, 0, - file->f_flags & O_NONBLOCK); -} - -static unsigned int -ts_poll(struct file *file, struct poll_table_struct *wait) -{ - unsigned long req_events = poll_requested_events(wait); - struct saa7134_dev *dev = video_drvdata(file); - struct saa7134_fh *fh = file->private_data; - unsigned int rc = 0; - - if (v4l2_event_pending(&fh->fh)) - rc = POLLPRI; - else if (req_events & POLLPRI) - poll_wait(file, &fh->fh.wait, wait); - return rc | videobuf_poll_stream(file, &dev->empress_tsq, wait); + saa7134_ts_stop_streaming(vq); + saa_writeb(SAA7134_SPECIAL_MODE, 0x00); + msleep(20); + saa_writeb(SAA7134_SPECIAL_MODE, 0x01); + msleep(100); + /* Mute audio */ + saa_writeb(SAA7134_AUDIO_MUTE_CTRL, + saa_readb(SAA7134_AUDIO_MUTE_CTRL) | (1 << 6)); + dev->empress_started = 0; } +static struct vb2_ops saa7134_empress_qops = { + .queue_setup = saa7134_ts_queue_setup, + .buf_init = saa7134_ts_buffer_init, + .buf_prepare = saa7134_ts_buffer_prepare, + .buf_finish = saa7134_ts_buffer_finish, + .buf_queue = saa7134_vb2_buffer_queue, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, + .start_streaming = start_streaming, + .stop_streaming = stop_streaming, +}; -static int -ts_mmap(struct file *file, struct vm_area_struct * vma) -{ - struct saa7134_dev *dev = video_drvdata(file); - - return videobuf_mmap_mapper(&dev->empress_tsq, vma); -} +/* ------------------------------------------------------------------ */ static int empress_enum_fmt_vid_cap(struct file *file, void *priv, struct v4l2_fmtdesc *f) @@ -233,11 +174,11 @@ static int empress_try_fmt_vid_cap(struct file *file, void *priv, static const struct v4l2_file_operations ts_fops = { .owner = THIS_MODULE, - .open = ts_open, - .release = ts_release, - .read = ts_read, - .poll = ts_poll, - .mmap = ts_mmap, + .open = v4l2_fh_open, + .release = vb2_fop_release, + .read = vb2_fop_read, + .poll = vb2_fop_poll, + .mmap = vb2_fop_mmap, .ioctl = video_ioctl2, }; @@ -247,12 +188,12 @@ static const struct v4l2_ioctl_ops ts_ioctl_ops = { .vidioc_try_fmt_vid_cap = empress_try_fmt_vid_cap, .vidioc_s_fmt_vid_cap = empress_s_fmt_vid_cap, .vidioc_g_fmt_vid_cap = empress_g_fmt_vid_cap, - .vidioc_reqbufs = saa7134_reqbufs, - .vidioc_querybuf = saa7134_querybuf, - .vidioc_qbuf = saa7134_qbuf, - .vidioc_dqbuf = saa7134_dqbuf, - .vidioc_streamon = saa7134_streamon, - .vidioc_streamoff = saa7134_streamoff, + .vidioc_reqbufs = vb2_ioctl_reqbufs, + .vidioc_querybuf = vb2_ioctl_querybuf, + .vidioc_qbuf = vb2_ioctl_qbuf, + .vidioc_dqbuf = vb2_ioctl_dqbuf, + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, .vidioc_g_frequency = saa7134_g_frequency, .vidioc_s_frequency = saa7134_s_frequency, .vidioc_g_tuner = saa7134_g_tuner, @@ -262,6 +203,7 @@ static const struct v4l2_ioctl_ops ts_ioctl_ops = { .vidioc_s_input = saa7134_s_input, .vidioc_s_std = saa7134_s_std, .vidioc_g_std = saa7134_g_std, + .vidioc_querystd = saa7134_querystd, .vidioc_log_status = v4l2_ctrl_log_status, .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, .vidioc_unsubscribe_event = v4l2_event_unsubscribe, @@ -314,6 +256,7 @@ static bool empress_ctrl_filter(const struct v4l2_ctrl *ctrl) static int empress_init(struct saa7134_dev *dev) { struct v4l2_ctrl_handler *hdl = &dev->empress_ctrl_handler; + struct vb2_queue *q; int err; dprintk("%s: %s\n",dev->name,__func__); @@ -323,6 +266,7 @@ static int empress_init(struct saa7134_dev *dev) *(dev->empress_dev) = saa7134_empress_template; dev->empress_dev->v4l2_dev = &dev->v4l2_dev; dev->empress_dev->release = video_device_release; + dev->empress_dev->lock = &dev->lock; snprintf(dev->empress_dev->name, sizeof(dev->empress_dev->name), "%s empress (%s)", dev->name, saa7134_boards[dev->board].name); @@ -339,6 +283,26 @@ static int empress_init(struct saa7134_dev *dev) INIT_WORK(&dev->empress_workqueue, empress_signal_update); + q = &dev->empress_vbq; + q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + /* + * Do not add VB2_USERPTR: the saa7134 DMA engine cannot handle + * transfers that do not start at the beginning of a page. A USERPTR + * can start anywhere in a page, so USERPTR support is a no-go. + */ + q->io_modes = VB2_MMAP | VB2_READ; + q->drv_priv = &dev->ts_q; + q->ops = &saa7134_empress_qops; + q->gfp_flags = GFP_DMA32; + q->mem_ops = &vb2_dma_sg_memops; + q->buf_struct_size = sizeof(struct saa7134_buf); + q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + q->lock = &dev->lock; + err = vb2_queue_init(q); + if (err) + return err; + dev->empress_dev->queue = q; + video_set_drvdata(dev->empress_dev, dev); err = video_register_device(dev->empress_dev,VFL_TYPE_GRABBER, empress_nr[dev->nr]); @@ -352,13 +316,6 @@ static int empress_init(struct saa7134_dev *dev) printk(KERN_INFO "%s: registered device %s [mpeg]\n", dev->name, video_device_node_name(dev->empress_dev)); - videobuf_queue_sg_init(&dev->empress_tsq, &saa7134_ts_qops, - &dev->pci->dev, &dev->slock, - V4L2_BUF_TYPE_VIDEO_CAPTURE, - V4L2_FIELD_ALTERNATE, - sizeof(struct saa7134_buf), - dev, NULL); - empress_signal_update(&dev->empress_workqueue); return 0; } @@ -371,6 +328,7 @@ static int empress_fini(struct saa7134_dev *dev) return 0; flush_work(&dev->empress_workqueue); video_unregister_device(dev->empress_dev); + vb2_queue_release(&dev->empress_vbq); v4l2_ctrl_handler_free(&dev->empress_ctrl_handler); dev->empress_dev = NULL; return 0; @@ -395,10 +353,3 @@ static void __exit empress_unregister(void) module_init(empress_register); module_exit(empress_unregister); - -/* ----------------------------------------------------------- */ -/* - * Local variables: - * c-basic-offset: 8 - * End: - */ diff --git a/drivers/media/pci/saa7134/saa7134-i2c.c b/drivers/media/pci/saa7134/saa7134-i2c.c index c68169d75804..f4da674e7f26 100644 --- a/drivers/media/pci/saa7134/saa7134-i2c.c +++ b/drivers/media/pci/saa7134/saa7134-i2c.c @@ -427,10 +427,3 @@ int saa7134_i2c_unregister(struct saa7134_dev *dev) i2c_del_adapter(&dev->i2c_adap); return 0; } - -/* ----------------------------------------------------------- */ -/* - * Local variables: - * c-basic-offset: 8 - * End: - */ diff --git a/drivers/media/pci/saa7134/saa7134-reg.h b/drivers/media/pci/saa7134/saa7134-reg.h index e7e0af101fa7..b6ea6f4f9b6c 100644 --- a/drivers/media/pci/saa7134/saa7134-reg.h +++ b/drivers/media/pci/saa7134/saa7134-reg.h @@ -167,17 +167,22 @@ #define SAA7134_HSYNC_START 0x106 #define SAA7134_HSYNC_STOP 0x107 #define SAA7134_SYNC_CTRL 0x108 +#define SAA7134_SYNC_CTRL_AUFD (1 << 7) #define SAA7134_LUMA_CTRL 0x109 +#define SAA7134_LUMA_CTRL_LDEL (1 << 5) #define SAA7134_DEC_LUMA_BRIGHT 0x10a #define SAA7134_DEC_LUMA_CONTRAST 0x10b #define SAA7134_DEC_CHROMA_SATURATION 0x10c #define SAA7134_DEC_CHROMA_HUE 0x10d #define SAA7134_CHROMA_CTRL1 0x10e +#define SAA7134_CHROMA_CTRL1_AUTO0 (1 << 1) +#define SAA7134_CHROMA_CTRL1_FCTC (1 << 2) #define SAA7134_CHROMA_GAIN 0x10f #define SAA7134_CHROMA_CTRL2 0x110 #define SAA7134_MODE_DELAY_CTRL 0x111 #define SAA7134_ANALOG_ADC 0x114 +#define SAA7134_ANALOG_ADC_AUTO1 (1 << 2) #define SAA7134_VGATE_START 0x115 #define SAA7134_VGATE_STOP 0x116 #define SAA7134_MISC_VGATE_MSB 0x117 @@ -369,10 +374,3 @@ #define SAA7135_DSP_RWCLEAR_RERR 1 #define SAA7133_I2S_AUDIO_CONTROL 0x591 -/* ------------------------------------------------------------------ */ -/* - * Local variables: - * c-basic-offset: 8 - * End: - */ - diff --git a/drivers/media/pci/saa7134/saa7134-ts.c b/drivers/media/pci/saa7134/saa7134-ts.c index 2e3f4b412d8c..bd25323bd947 100644 --- a/drivers/media/pci/saa7134/saa7134-ts.c +++ b/drivers/media/pci/saa7134/saa7134-ts.c @@ -39,26 +39,29 @@ MODULE_PARM_DESC(ts_debug,"enable debug messages [ts]"); printk(KERN_DEBUG "%s/ts: " fmt, dev->name , ## arg) /* ------------------------------------------------------------------ */ - static int buffer_activate(struct saa7134_dev *dev, struct saa7134_buf *buf, struct saa7134_buf *next) { dprintk("buffer_activate [%p]",buf); - buf->vb.state = VIDEOBUF_ACTIVE; buf->top_seen = 0; + if (!dev->ts_started) + dev->ts_field = V4L2_FIELD_TOP; + if (NULL == next) next = buf; - if (V4L2_FIELD_TOP == buf->vb.field) { + if (V4L2_FIELD_TOP == dev->ts_field) { dprintk("- [top] buf=%p next=%p\n",buf,next); saa_writel(SAA7134_RS_BA1(5),saa7134_buffer_base(buf)); saa_writel(SAA7134_RS_BA2(5),saa7134_buffer_base(next)); + dev->ts_field = V4L2_FIELD_BOTTOM; } else { dprintk("- [bottom] buf=%p next=%p\n",buf,next); saa_writel(SAA7134_RS_BA1(5),saa7134_buffer_base(next)); saa_writel(SAA7134_RS_BA2(5),saa7134_buffer_base(buf)); + dev->ts_field = V4L2_FIELD_TOP; } /* start DMA */ @@ -72,96 +75,123 @@ static int buffer_activate(struct saa7134_dev *dev, return 0; } -static int buffer_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb, - enum v4l2_field field) +int saa7134_ts_buffer_init(struct vb2_buffer *vb2) { - struct saa7134_dev *dev = q->priv_data; - struct saa7134_buf *buf = container_of(vb,struct saa7134_buf,vb); + struct saa7134_dmaqueue *dmaq = vb2->vb2_queue->drv_priv; + struct saa7134_buf *buf = container_of(vb2, struct saa7134_buf, vb2); + + dmaq->curr = NULL; + buf->activate = buffer_activate; + + return 0; +} +EXPORT_SYMBOL_GPL(saa7134_ts_buffer_init); + +int saa7134_ts_buffer_prepare(struct vb2_buffer *vb2) +{ + struct saa7134_dmaqueue *dmaq = vb2->vb2_queue->drv_priv; + struct saa7134_dev *dev = dmaq->dev; + struct saa7134_buf *buf = container_of(vb2, struct saa7134_buf, vb2); + struct sg_table *dma = vb2_dma_sg_plane_desc(vb2, 0); unsigned int lines, llength, size; - int err; + int ret; - dprintk("buffer_prepare [%p,%s]\n",buf,v4l2_field_names[field]); + dprintk("buffer_prepare [%p]\n", buf); llength = TS_PACKET_SIZE; lines = dev->ts.nr_packets; size = lines * llength; - if (0 != buf->vb.baddr && buf->vb.bsize < size) + if (vb2_plane_size(vb2, 0) < size) return -EINVAL; - if (buf->vb.size != size) { - saa7134_dma_free(q,buf); - } - - if (VIDEOBUF_NEEDS_INIT == buf->vb.state) { - - struct videobuf_dmabuf *dma=videobuf_to_dma(&buf->vb); - - dprintk("buffer_prepare: needs_init\n"); - - buf->vb.width = llength; - buf->vb.height = lines; - buf->vb.size = size; - buf->pt = &dev->ts.pt_ts; - - err = videobuf_iolock(q,&buf->vb,NULL); - if (err) - goto oops; - err = saa7134_pgtable_build(dev->pci,buf->pt, - dma->sglist, - dma->sglen, - saa7134_buffer_startpage(buf)); - if (err) - goto oops; - } - - buf->vb.state = VIDEOBUF_PREPARED; - buf->activate = buffer_activate; - buf->vb.field = field; - return 0; + vb2_set_plane_payload(vb2, 0, size); + vb2->v4l2_buf.field = dev->field; - oops: - saa7134_dma_free(q,buf); - return err; + ret = dma_map_sg(&dev->pci->dev, dma->sgl, dma->nents, DMA_FROM_DEVICE); + if (!ret) + return -EIO; + return saa7134_pgtable_build(dev->pci, &dmaq->pt, dma->sgl, dma->nents, + saa7134_buffer_startpage(buf)); } +EXPORT_SYMBOL_GPL(saa7134_ts_buffer_prepare); -static int -buffer_setup(struct videobuf_queue *q, unsigned int *count, unsigned int *size) +void saa7134_ts_buffer_finish(struct vb2_buffer *vb2) { - struct saa7134_dev *dev = q->priv_data; + struct saa7134_dmaqueue *dmaq = vb2->vb2_queue->drv_priv; + struct saa7134_dev *dev = dmaq->dev; + struct saa7134_buf *buf = container_of(vb2, struct saa7134_buf, vb2); + struct sg_table *dma = vb2_dma_sg_plane_desc(&buf->vb2, 0); - *size = TS_PACKET_SIZE * dev->ts.nr_packets; - if (0 == *count) - *count = dev->ts.nr_bufs; - *count = saa7134_buffer_count(*size,*count); + dma_unmap_sg(&dev->pci->dev, dma->sgl, dma->nents, DMA_FROM_DEVICE); +} +EXPORT_SYMBOL_GPL(saa7134_ts_buffer_finish); +int saa7134_ts_queue_setup(struct vb2_queue *q, const struct v4l2_format *fmt, + unsigned int *nbuffers, unsigned int *nplanes, + unsigned int sizes[], void *alloc_ctxs[]) +{ + struct saa7134_dmaqueue *dmaq = q->drv_priv; + struct saa7134_dev *dev = dmaq->dev; + int size = TS_PACKET_SIZE * dev->ts.nr_packets; + + if (0 == *nbuffers) + *nbuffers = dev->ts.nr_bufs; + *nbuffers = saa7134_buffer_count(size, *nbuffers); + if (*nbuffers < 3) + *nbuffers = 3; + *nplanes = 1; + sizes[0] = size; return 0; } +EXPORT_SYMBOL_GPL(saa7134_ts_queue_setup); -static void buffer_queue(struct videobuf_queue *q, struct videobuf_buffer *vb) +int saa7134_ts_start_streaming(struct vb2_queue *vq, unsigned int count) { - struct saa7134_dev *dev = q->priv_data; - struct saa7134_buf *buf = container_of(vb,struct saa7134_buf,vb); - - saa7134_buffer_queue(dev,&dev->ts_q,buf); + struct saa7134_dmaqueue *dmaq = vq->drv_priv; + struct saa7134_dev *dev = dmaq->dev; + + /* + * Planar video capture and TS share the same DMA channel, + * so only one can be active at a time. + */ + if (vb2_is_busy(&dev->video_vbq) && dev->fmt->planar) { + struct saa7134_buf *buf, *tmp; + + list_for_each_entry_safe(buf, tmp, &dmaq->queue, entry) { + list_del(&buf->entry); + vb2_buffer_done(&buf->vb2, VB2_BUF_STATE_QUEUED); + } + if (dmaq->curr) { + vb2_buffer_done(&dmaq->curr->vb2, VB2_BUF_STATE_QUEUED); + dmaq->curr = NULL; + } + return -EBUSY; + } + dmaq->seq_nr = 0; + return 0; } +EXPORT_SYMBOL_GPL(saa7134_ts_start_streaming); -static void buffer_release(struct videobuf_queue *q, struct videobuf_buffer *vb) +void saa7134_ts_stop_streaming(struct vb2_queue *vq) { - struct saa7134_buf *buf = container_of(vb,struct saa7134_buf,vb); - struct saa7134_dev *dev = q->priv_data; - - if (dev->ts_started) - saa7134_ts_stop(dev); + struct saa7134_dmaqueue *dmaq = vq->drv_priv; + struct saa7134_dev *dev = dmaq->dev; - saa7134_dma_free(q,buf); + saa7134_ts_stop(dev); + saa7134_stop_streaming(dev, dmaq); } - -struct videobuf_queue_ops saa7134_ts_qops = { - .buf_setup = buffer_setup, - .buf_prepare = buffer_prepare, - .buf_queue = buffer_queue, - .buf_release = buffer_release, +EXPORT_SYMBOL_GPL(saa7134_ts_stop_streaming); + +struct vb2_ops saa7134_ts_qops = { + .queue_setup = saa7134_ts_queue_setup, + .buf_init = saa7134_ts_buffer_init, + .buf_prepare = saa7134_ts_buffer_prepare, + .buf_finish = saa7134_ts_buffer_finish, + .buf_queue = saa7134_vb2_buffer_queue, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, + .stop_streaming = saa7134_ts_stop_streaming, }; EXPORT_SYMBOL_GPL(saa7134_ts_qops); @@ -213,7 +243,7 @@ int saa7134_ts_init1(struct saa7134_dev *dev) dev->ts_q.dev = dev; dev->ts_q.need_two = 1; dev->ts_started = 0; - saa7134_pgtable_alloc(dev->pci,&dev->ts.pt_ts); + saa7134_pgtable_alloc(dev->pci, &dev->ts_q.pt); /* init TS hw */ saa7134_ts_init_hw(dev); @@ -226,7 +256,8 @@ int saa7134_ts_stop(struct saa7134_dev *dev) { dprintk("TS stop\n"); - BUG_ON(!dev->ts_started); + if (!dev->ts_started) + return 0; /* Stop TS stream */ switch (saa7134_boards[dev->board].ts_type) { @@ -247,7 +278,8 @@ int saa7134_ts_start(struct saa7134_dev *dev) { dprintk("TS start\n"); - BUG_ON(dev->ts_started); + if (WARN_ON(dev->ts_started)) + return 0; /* dma: setup channel 5 (= TS) */ saa_writeb(SAA7134_TS_DMA0, (dev->ts.nr_packets - 1) & 0xff); @@ -259,7 +291,7 @@ int saa7134_ts_start(struct saa7134_dev *dev) saa_writel(SAA7134_RS_PITCH(5), TS_PACKET_SIZE); saa_writel(SAA7134_RS_CONTROL(5), SAA7134_RS_CONTROL_BURST_16 | SAA7134_RS_CONTROL_ME | - (dev->ts.pt_ts.dma >> 12)); + (dev->ts_q.pt.dma >> 12)); /* reset hardware TS buffers */ saa_writeb(SAA7134_TS_SERIAL1, 0x00); @@ -293,7 +325,7 @@ int saa7134_ts_start(struct saa7134_dev *dev) int saa7134_ts_fini(struct saa7134_dev *dev) { - saa7134_pgtable_free(dev->pci,&dev->ts.pt_ts); + saa7134_pgtable_free(dev->pci, &dev->ts_q.pt); return 0; } @@ -303,25 +335,18 @@ void saa7134_irq_ts_done(struct saa7134_dev *dev, unsigned long status) spin_lock(&dev->slock); if (dev->ts_q.curr) { - field = dev->ts_q.curr->vb.field; - if (field == V4L2_FIELD_TOP) { + field = dev->ts_field; + if (field != V4L2_FIELD_TOP) { if ((status & 0x100000) != 0x000000) goto done; } else { if ((status & 0x100000) != 0x100000) goto done; } - saa7134_buffer_finish(dev,&dev->ts_q,VIDEOBUF_DONE); + saa7134_buffer_finish(dev, &dev->ts_q, VB2_BUF_STATE_DONE); } saa7134_buffer_next(dev,&dev->ts_q); done: spin_unlock(&dev->slock); } - -/* ----------------------------------------------------------- */ -/* - * Local variables: - * c-basic-offset: 8 - * End: - */ diff --git a/drivers/media/pci/saa7134/saa7134-tvaudio.c b/drivers/media/pci/saa7134/saa7134-tvaudio.c index 0f34e09d98dc..3afbcb70b518 100644 --- a/drivers/media/pci/saa7134/saa7134-tvaudio.c +++ b/drivers/media/pci/saa7134/saa7134-tvaudio.c @@ -1079,10 +1079,3 @@ int saa7134_tvaudio_do_scan(struct saa7134_dev *dev) EXPORT_SYMBOL(saa_dsp_writel); EXPORT_SYMBOL(saa7134_tvaudio_setmute); - -/* ----------------------------------------------------------- */ -/* - * Local variables: - * c-basic-offset: 8 - * End: - */ diff --git a/drivers/media/pci/saa7134/saa7134-vbi.c b/drivers/media/pci/saa7134/saa7134-vbi.c index d4da18d049f3..c06dbe17a87f 100644 --- a/drivers/media/pci/saa7134/saa7134-vbi.c +++ b/drivers/media/pci/saa7134/saa7134-vbi.c @@ -67,10 +67,10 @@ static void task_init(struct saa7134_dev *dev, struct saa7134_buf *buf, saa_writeb(SAA7134_VBI_PHASE_OFFSET_LUMA(task), 0x00); saa_writeb(SAA7134_VBI_PHASE_OFFSET_CHROMA(task), 0x00); - saa_writeb(SAA7134_VBI_H_LEN1(task), buf->vb.width & 0xff); - saa_writeb(SAA7134_VBI_H_LEN2(task), buf->vb.width >> 8); - saa_writeb(SAA7134_VBI_V_LEN1(task), buf->vb.height & 0xff); - saa_writeb(SAA7134_VBI_V_LEN2(task), buf->vb.height >> 8); + saa_writeb(SAA7134_VBI_H_LEN1(task), dev->vbi_hlen & 0xff); + saa_writeb(SAA7134_VBI_H_LEN2(task), dev->vbi_hlen >> 8); + saa_writeb(SAA7134_VBI_V_LEN1(task), dev->vbi_vlen & 0xff); + saa_writeb(SAA7134_VBI_V_LEN2(task), dev->vbi_vlen >> 8); saa_andorb(SAA7134_DATA_PATH(task), 0xc0, 0x00); } @@ -81,14 +81,14 @@ static int buffer_activate(struct saa7134_dev *dev, struct saa7134_buf *buf, struct saa7134_buf *next) { - unsigned long control,base; + struct saa7134_dmaqueue *dmaq = buf->vb2.vb2_queue->drv_priv; + unsigned long control, base; - dprintk("buffer_activate [%p]\n",buf); - buf->vb.state = VIDEOBUF_ACTIVE; + dprintk("buffer_activate [%p]\n", buf); buf->top_seen = 0; - task_init(dev,buf,TASK_A); - task_init(dev,buf,TASK_B); + task_init(dev, buf, TASK_A); + task_init(dev, buf, TASK_B); saa_writeb(SAA7134_OFMT_DATA_A, 0x06); saa_writeb(SAA7134_OFMT_DATA_B, 0x06); @@ -96,107 +96,99 @@ static int buffer_activate(struct saa7134_dev *dev, base = saa7134_buffer_base(buf); control = SAA7134_RS_CONTROL_BURST_16 | SAA7134_RS_CONTROL_ME | - (buf->pt->dma >> 12); - saa_writel(SAA7134_RS_BA1(2),base); - saa_writel(SAA7134_RS_BA2(2),base + buf->vb.size/2); - saa_writel(SAA7134_RS_PITCH(2),buf->vb.width); - saa_writel(SAA7134_RS_CONTROL(2),control); - saa_writel(SAA7134_RS_BA1(3),base); - saa_writel(SAA7134_RS_BA2(3),base + buf->vb.size/2); - saa_writel(SAA7134_RS_PITCH(3),buf->vb.width); - saa_writel(SAA7134_RS_CONTROL(3),control); + (dmaq->pt.dma >> 12); + saa_writel(SAA7134_RS_BA1(2), base); + saa_writel(SAA7134_RS_BA2(2), base + dev->vbi_hlen * dev->vbi_vlen); + saa_writel(SAA7134_RS_PITCH(2), dev->vbi_hlen); + saa_writel(SAA7134_RS_CONTROL(2), control); + saa_writel(SAA7134_RS_BA1(3), base); + saa_writel(SAA7134_RS_BA2(3), base + dev->vbi_hlen * dev->vbi_vlen); + saa_writel(SAA7134_RS_PITCH(3), dev->vbi_hlen); + saa_writel(SAA7134_RS_CONTROL(3), control); /* start DMA */ saa7134_set_dmabits(dev); - mod_timer(&dev->vbi_q.timeout, jiffies+BUFFER_TIMEOUT); + mod_timer(&dmaq->timeout, jiffies + BUFFER_TIMEOUT); return 0; } -static int buffer_prepare(struct videobuf_queue *q, - struct videobuf_buffer *vb, - enum v4l2_field field) +static int buffer_prepare(struct vb2_buffer *vb2) { - struct saa7134_dev *dev = q->priv_data; - struct saa7134_buf *buf = container_of(vb,struct saa7134_buf,vb); - struct saa7134_tvnorm *norm = dev->tvnorm; - unsigned int lines, llength, size; - int err; - - lines = norm->vbi_v_stop_0 - norm->vbi_v_start_0 +1; - if (lines > VBI_LINE_COUNT) - lines = VBI_LINE_COUNT; - llength = VBI_LINE_LENGTH; - size = lines * llength * 2; - if (0 != buf->vb.baddr && buf->vb.bsize < size) + struct saa7134_dmaqueue *dmaq = vb2->vb2_queue->drv_priv; + struct saa7134_dev *dev = dmaq->dev; + struct saa7134_buf *buf = container_of(vb2, struct saa7134_buf, vb2); + struct sg_table *dma = vb2_dma_sg_plane_desc(&buf->vb2, 0); + unsigned int size; + int ret; + + if (dma->sgl->offset) { + pr_err("The buffer is not page-aligned\n"); return -EINVAL; - - if (buf->vb.size != size) - saa7134_dma_free(q,buf); - - if (VIDEOBUF_NEEDS_INIT == buf->vb.state) { - struct videobuf_dmabuf *dma=videobuf_to_dma(&buf->vb); - - buf->vb.width = llength; - buf->vb.height = lines; - buf->vb.size = size; - buf->pt = &dev->pt_vbi; - - err = videobuf_iolock(q,&buf->vb,NULL); - if (err) - goto oops; - err = saa7134_pgtable_build(dev->pci,buf->pt, - dma->sglist, - dma->sglen, - saa7134_buffer_startpage(buf)); - if (err) - goto oops; } - buf->vb.state = VIDEOBUF_PREPARED; - buf->activate = buffer_activate; - buf->vb.field = field; - return 0; + size = dev->vbi_hlen * dev->vbi_vlen * 2; + if (vb2_plane_size(vb2, 0) < size) + return -EINVAL; + + vb2_set_plane_payload(vb2, 0, size); - oops: - saa7134_dma_free(q,buf); - return err; + ret = dma_map_sg(&dev->pci->dev, dma->sgl, dma->nents, DMA_FROM_DEVICE); + if (!ret) + return -EIO; + return saa7134_pgtable_build(dev->pci, &dmaq->pt, dma->sgl, dma->nents, + saa7134_buffer_startpage(buf)); } -static int -buffer_setup(struct videobuf_queue *q, unsigned int *count, unsigned int *size) +static int queue_setup(struct vb2_queue *q, const struct v4l2_format *fmt, + unsigned int *nbuffers, unsigned int *nplanes, + unsigned int sizes[], void *alloc_ctxs[]) { - struct saa7134_dev *dev = q->priv_data; - int llength,lines; - - lines = dev->tvnorm->vbi_v_stop_0 - dev->tvnorm->vbi_v_start_0 +1; - llength = VBI_LINE_LENGTH; - *size = lines * llength * 2; - if (0 == *count) - *count = vbibufs; - *count = saa7134_buffer_count(*size,*count); + struct saa7134_dmaqueue *dmaq = q->drv_priv; + struct saa7134_dev *dev = dmaq->dev; + unsigned int size; + + dev->vbi_vlen = dev->tvnorm->vbi_v_stop_0 - dev->tvnorm->vbi_v_start_0 + 1; + if (dev->vbi_vlen > VBI_LINE_COUNT) + dev->vbi_vlen = VBI_LINE_COUNT; + dev->vbi_hlen = VBI_LINE_LENGTH; + size = dev->vbi_hlen * dev->vbi_vlen * 2; + + *nbuffers = saa7134_buffer_count(size, *nbuffers); + *nplanes = 1; + sizes[0] = size; return 0; } -static void buffer_queue(struct videobuf_queue *q, struct videobuf_buffer *vb) +static int buffer_init(struct vb2_buffer *vb2) { - struct saa7134_dev *dev = q->priv_data; - struct saa7134_buf *buf = container_of(vb,struct saa7134_buf,vb); + struct saa7134_dmaqueue *dmaq = vb2->vb2_queue->drv_priv; + struct saa7134_buf *buf = container_of(vb2, struct saa7134_buf, vb2); - saa7134_buffer_queue(dev,&dev->vbi_q,buf); + dmaq->curr = NULL; + buf->activate = buffer_activate; + return 0; } -static void buffer_release(struct videobuf_queue *q, struct videobuf_buffer *vb) +static void buffer_finish(struct vb2_buffer *vb2) { - struct saa7134_buf *buf = container_of(vb,struct saa7134_buf,vb); + struct saa7134_dmaqueue *dmaq = vb2->vb2_queue->drv_priv; + struct saa7134_dev *dev = dmaq->dev; + struct saa7134_buf *buf = container_of(vb2, struct saa7134_buf, vb2); + struct sg_table *dma = vb2_dma_sg_plane_desc(&buf->vb2, 0); - saa7134_dma_free(q,buf); + dma_unmap_sg(&dev->pci->dev, dma->sgl, dma->nents, DMA_FROM_DEVICE); } -struct videobuf_queue_ops saa7134_vbi_qops = { - .buf_setup = buffer_setup, - .buf_prepare = buffer_prepare, - .buf_queue = buffer_queue, - .buf_release = buffer_release, +struct vb2_ops saa7134_vbi_qops = { + .queue_setup = queue_setup, + .buf_init = buffer_init, + .buf_prepare = buffer_prepare, + .buf_finish = buffer_finish, + .buf_queue = saa7134_vb2_buffer_queue, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, + .start_streaming = saa7134_vb2_start_streaming, + .stop_streaming = saa7134_vb2_stop_streaming, }; /* ------------------------------------------------------------------ */ @@ -226,7 +218,6 @@ void saa7134_irq_vbi_done(struct saa7134_dev *dev, unsigned long status) { spin_lock(&dev->slock); if (dev->vbi_q.curr) { - dev->vbi_fieldcount++; /* make sure we have seen both fields */ if ((status & 0x10) == 0x00) { dev->vbi_q.curr->top_seen = 1; @@ -235,18 +226,10 @@ void saa7134_irq_vbi_done(struct saa7134_dev *dev, unsigned long status) if (!dev->vbi_q.curr->top_seen) goto done; - dev->vbi_q.curr->vb.field_count = dev->vbi_fieldcount; - saa7134_buffer_finish(dev,&dev->vbi_q,VIDEOBUF_DONE); + saa7134_buffer_finish(dev, &dev->vbi_q, VB2_BUF_STATE_DONE); } - saa7134_buffer_next(dev,&dev->vbi_q); + saa7134_buffer_next(dev, &dev->vbi_q); done: spin_unlock(&dev->slock); } - -/* ----------------------------------------------------------- */ -/* - * Local variables: - * c-basic-offset: 8 - * End: - */ diff --git a/drivers/media/pci/saa7134/saa7134-video.c b/drivers/media/pci/saa7134/saa7134-video.c index eb472b5b26a0..d37599980768 100644 --- a/drivers/media/pci/saa7134/saa7134-video.c +++ b/drivers/media/pci/saa7134/saa7134-video.c @@ -381,42 +381,6 @@ static struct saa7134_format* format_by_fourcc(unsigned int fourcc) return NULL; } -/* ----------------------------------------------------------------------- */ -/* resource management */ - -static int res_get(struct saa7134_dev *dev, struct saa7134_fh *fh, unsigned int bit) -{ - if (fh->resources & bit) - /* have it already allocated */ - return 1; - - /* is it free? */ - mutex_lock(&dev->lock); - if (dev->resources & bit) { - /* no, someone else uses it */ - mutex_unlock(&dev->lock); - return 0; - } - /* it's free, grab it */ - fh->resources |= bit; - dev->resources |= bit; - dprintk("res: get %d\n",bit); - mutex_unlock(&dev->lock); - return 1; -} - -static -void res_free(struct saa7134_dev *dev, struct saa7134_fh *fh, unsigned int bits) -{ - BUG_ON((fh->resources & bits) != bits); - - mutex_lock(&dev->lock); - fh->resources &= ~bits; - dev->resources &= ~bits; - dprintk("res: put %d\n",bits); - mutex_unlock(&dev->lock); -} - /* ------------------------------------------------------------------ */ static void set_tvnorm(struct saa7134_dev *dev, struct saa7134_tvnorm *norm) @@ -452,19 +416,26 @@ static void video_mux(struct saa7134_dev *dev, int input) static void saa7134_set_decoder(struct saa7134_dev *dev) { - int luma_control, sync_control, mux; + int luma_control, sync_control, chroma_ctrl1, mux; struct saa7134_tvnorm *norm = dev->tvnorm; mux = card_in(dev, dev->ctl_input).vmux; luma_control = norm->luma_control; sync_control = norm->sync_control; + chroma_ctrl1 = norm->chroma_ctrl1; if (mux > 5) luma_control |= 0x80; /* svideo */ if (noninterlaced || dev->nosignal) sync_control |= 0x20; + /* switch on auto standard detection */ + sync_control |= SAA7134_SYNC_CTRL_AUFD; + chroma_ctrl1 |= SAA7134_CHROMA_CTRL1_AUTO0; + chroma_ctrl1 &= ~SAA7134_CHROMA_CTRL1_FCTC; + luma_control &= ~SAA7134_LUMA_CTRL_LDEL; + /* setup video decoder */ saa_writeb(SAA7134_INCR_DELAY, 0x08); saa_writeb(SAA7134_ANALOG_IN_CTRL1, 0xc0 | mux); @@ -487,7 +458,7 @@ static void saa7134_set_decoder(struct saa7134_dev *dev) dev->ctl_invert ? -dev->ctl_saturation : dev->ctl_saturation); saa_writeb(SAA7134_DEC_CHROMA_HUE, dev->ctl_hue); - saa_writeb(SAA7134_CHROMA_CTRL1, norm->chroma_ctrl1); + saa_writeb(SAA7134_CHROMA_CTRL1, chroma_ctrl1); saa_writeb(SAA7134_CHROMA_GAIN, norm->chroma_gain); saa_writeb(SAA7134_CHROMA_CTRL2, norm->chroma_ctrl2); @@ -506,10 +477,10 @@ void saa7134_set_tvnorm_hw(struct saa7134_dev *dev) saa7134_set_decoder(dev); if (card_in(dev, dev->ctl_input).tv) - saa_call_all(dev, core, s_std, dev->tvnorm->id); + saa_call_all(dev, video, s_std, dev->tvnorm->id); /* Set the correct norm for the saa6752hs. This function does nothing if there is no saa6752hs. */ - saa_call_empress(dev, core, s_std, dev->tvnorm->id); + saa_call_empress(dev, video, s_std, dev->tvnorm->id); } static void set_h_prescale(struct saa7134_dev *dev, int task, int prescale) @@ -817,35 +788,35 @@ static int buffer_activate(struct saa7134_dev *dev, struct saa7134_buf *buf, struct saa7134_buf *next) { + struct saa7134_dmaqueue *dmaq = buf->vb2.vb2_queue->drv_priv; unsigned long base,control,bpl; unsigned long bpl_uv,lines_uv,base2,base3,tmp; /* planar */ dprintk("buffer_activate buf=%p\n",buf); - buf->vb.state = VIDEOBUF_ACTIVE; buf->top_seen = 0; - set_size(dev,TASK_A,buf->vb.width,buf->vb.height, - V4L2_FIELD_HAS_BOTH(buf->vb.field)); - if (buf->fmt->yuv) + set_size(dev, TASK_A, dev->width, dev->height, + V4L2_FIELD_HAS_BOTH(dev->field)); + if (dev->fmt->yuv) saa_andorb(SAA7134_DATA_PATH(TASK_A), 0x3f, 0x03); else saa_andorb(SAA7134_DATA_PATH(TASK_A), 0x3f, 0x01); - saa_writeb(SAA7134_OFMT_VIDEO_A, buf->fmt->pm); + saa_writeb(SAA7134_OFMT_VIDEO_A, dev->fmt->pm); /* DMA: setup channel 0 (= Video Task A0) */ base = saa7134_buffer_base(buf); - if (buf->fmt->planar) - bpl = buf->vb.width; + if (dev->fmt->planar) + bpl = dev->width; else - bpl = (buf->vb.width * buf->fmt->depth) / 8; + bpl = (dev->width * dev->fmt->depth) / 8; control = SAA7134_RS_CONTROL_BURST_16 | SAA7134_RS_CONTROL_ME | - (buf->pt->dma >> 12); - if (buf->fmt->bswap) + (dmaq->pt.dma >> 12); + if (dev->fmt->bswap) control |= SAA7134_RS_CONTROL_BSWAP; - if (buf->fmt->wswap) + if (dev->fmt->wswap) control |= SAA7134_RS_CONTROL_WSWAP; - if (V4L2_FIELD_HAS_BOTH(buf->vb.field)) { + if (V4L2_FIELD_HAS_BOTH(dev->field)) { /* interlaced */ saa_writel(SAA7134_RS_BA1(0),base); saa_writel(SAA7134_RS_BA2(0),base+bpl); @@ -858,17 +829,17 @@ static int buffer_activate(struct saa7134_dev *dev, } saa_writel(SAA7134_RS_CONTROL(0),control); - if (buf->fmt->planar) { + if (dev->fmt->planar) { /* DMA: setup channel 4+5 (= planar task A) */ - bpl_uv = bpl >> buf->fmt->hshift; - lines_uv = buf->vb.height >> buf->fmt->vshift; - base2 = base + bpl * buf->vb.height; + bpl_uv = bpl >> dev->fmt->hshift; + lines_uv = dev->height >> dev->fmt->vshift; + base2 = base + bpl * dev->height; base3 = base2 + bpl_uv * lines_uv; - if (buf->fmt->uvswap) + if (dev->fmt->uvswap) tmp = base2, base2 = base3, base3 = tmp; dprintk("uv: bpl=%ld lines=%ld base2/3=%ld/%ld\n", bpl_uv,lines_uv,base2,base3); - if (V4L2_FIELD_HAS_BOTH(buf->vb.field)) { + if (V4L2_FIELD_HAS_BOTH(dev->field)) { /* interlaced */ saa_writel(SAA7134_RS_BA1(4),base2); saa_writel(SAA7134_RS_BA2(4),base2+bpl_uv); @@ -891,22 +862,65 @@ static int buffer_activate(struct saa7134_dev *dev, /* start DMA */ saa7134_set_dmabits(dev); - mod_timer(&dev->video_q.timeout, jiffies+BUFFER_TIMEOUT); + mod_timer(&dmaq->timeout, jiffies + BUFFER_TIMEOUT); + return 0; +} + +static int buffer_init(struct vb2_buffer *vb2) +{ + struct saa7134_dmaqueue *dmaq = vb2->vb2_queue->drv_priv; + struct saa7134_buf *buf = container_of(vb2, struct saa7134_buf, vb2); + + dmaq->curr = NULL; + buf->activate = buffer_activate; return 0; } -static int buffer_prepare(struct videobuf_queue *q, - struct videobuf_buffer *vb, - enum v4l2_field field) +static int buffer_prepare(struct vb2_buffer *vb2) { - struct saa7134_dev *dev = q->priv_data; - struct saa7134_buf *buf = container_of(vb,struct saa7134_buf,vb); + struct saa7134_dmaqueue *dmaq = vb2->vb2_queue->drv_priv; + struct saa7134_dev *dev = dmaq->dev; + struct saa7134_buf *buf = container_of(vb2, struct saa7134_buf, vb2); + struct sg_table *dma = vb2_dma_sg_plane_desc(&buf->vb2, 0); unsigned int size; - int err; + int ret; - /* sanity checks */ - if (NULL == dev->fmt) + if (dma->sgl->offset) { + pr_err("The buffer is not page-aligned\n"); return -EINVAL; + } + size = (dev->width * dev->height * dev->fmt->depth) >> 3; + if (vb2_plane_size(vb2, 0) < size) + return -EINVAL; + + vb2_set_plane_payload(vb2, 0, size); + vb2->v4l2_buf.field = dev->field; + + ret = dma_map_sg(&dev->pci->dev, dma->sgl, dma->nents, DMA_FROM_DEVICE); + if (!ret) + return -EIO; + return saa7134_pgtable_build(dev->pci, &dmaq->pt, dma->sgl, dma->nents, + saa7134_buffer_startpage(buf)); +} + +static void buffer_finish(struct vb2_buffer *vb2) +{ + struct saa7134_dmaqueue *dmaq = vb2->vb2_queue->drv_priv; + struct saa7134_dev *dev = dmaq->dev; + struct saa7134_buf *buf = container_of(vb2, struct saa7134_buf, vb2); + struct sg_table *dma = vb2_dma_sg_plane_desc(&buf->vb2, 0); + + dma_unmap_sg(&dev->pci->dev, dma->sgl, dma->nents, DMA_FROM_DEVICE); +} + +static int queue_setup(struct vb2_queue *q, const struct v4l2_format *fmt, + unsigned int *nbuffers, unsigned int *nplanes, + unsigned int sizes[], void *alloc_ctxs[]) +{ + struct saa7134_dmaqueue *dmaq = q->drv_priv; + struct saa7134_dev *dev = dmaq->dev; + int size = dev->fmt->depth * dev->width * dev->height >> 3; + if (dev->width < 48 || dev->height < 32 || dev->width/4 > dev->crop_current.width || @@ -914,83 +928,88 @@ static int buffer_prepare(struct videobuf_queue *q, dev->width > dev->crop_bounds.width || dev->height > dev->crop_bounds.height) return -EINVAL; - size = (dev->width * dev->height * dev->fmt->depth) >> 3; - if (0 != buf->vb.baddr && buf->vb.bsize < size) - return -EINVAL; - - dprintk("buffer_prepare [%d,size=%dx%d,bytes=%d,fields=%s,%s]\n", - vb->i, dev->width, dev->height, size, v4l2_field_names[field], - dev->fmt->name); - if (buf->vb.width != dev->width || - buf->vb.height != dev->height || - buf->vb.size != size || - buf->vb.field != field || - buf->fmt != dev->fmt) { - saa7134_dma_free(q,buf); - } - if (VIDEOBUF_NEEDS_INIT == buf->vb.state) { - struct videobuf_dmabuf *dma=videobuf_to_dma(&buf->vb); - - buf->vb.width = dev->width; - buf->vb.height = dev->height; - buf->vb.size = size; - buf->vb.field = field; - buf->fmt = dev->fmt; - buf->pt = &dev->pt_cap; - dev->video_q.curr = NULL; - - err = videobuf_iolock(q,&buf->vb,&dev->ovbuf); - if (err) - goto oops; - err = saa7134_pgtable_build(dev->pci,buf->pt, - dma->sglist, - dma->sglen, - saa7134_buffer_startpage(buf)); - if (err) - goto oops; - } - buf->vb.state = VIDEOBUF_PREPARED; - buf->activate = buffer_activate; + *nbuffers = saa7134_buffer_count(size, *nbuffers); + *nplanes = 1; + sizes[0] = size; return 0; - - oops: - saa7134_dma_free(q,buf); - return err; } -static int -buffer_setup(struct videobuf_queue *q, unsigned int *count, unsigned int *size) +/* + * move buffer to hardware queue + */ +void saa7134_vb2_buffer_queue(struct vb2_buffer *vb) { - struct saa7134_dev *dev = q->priv_data; + struct saa7134_dmaqueue *dmaq = vb->vb2_queue->drv_priv; + struct saa7134_dev *dev = dmaq->dev; + struct saa7134_buf *buf = container_of(vb, struct saa7134_buf, vb2); - *size = dev->fmt->depth * dev->width * dev->height >> 3; - if (0 == *count) - *count = gbuffers; - *count = saa7134_buffer_count(*size,*count); - return 0; + saa7134_buffer_queue(dev, dmaq, buf); } +EXPORT_SYMBOL_GPL(saa7134_vb2_buffer_queue); -static void buffer_queue(struct videobuf_queue *q, struct videobuf_buffer *vb) +int saa7134_vb2_start_streaming(struct vb2_queue *vq, unsigned int count) { - struct saa7134_dev *dev = q->priv_data; - struct saa7134_buf *buf = container_of(vb,struct saa7134_buf,vb); + struct saa7134_dmaqueue *dmaq = vq->drv_priv; + struct saa7134_dev *dev = dmaq->dev; + + /* + * Planar video capture and TS share the same DMA channel, + * so only one can be active at a time. + */ + if (card_is_empress(dev) && vb2_is_busy(&dev->empress_vbq) && + dmaq == &dev->video_q && dev->fmt->planar) { + struct saa7134_buf *buf, *tmp; - saa7134_buffer_queue(dev, &dev->video_q, buf); + list_for_each_entry_safe(buf, tmp, &dmaq->queue, entry) { + list_del(&buf->entry); + vb2_buffer_done(&buf->vb2, VB2_BUF_STATE_QUEUED); + } + if (dmaq->curr) { + vb2_buffer_done(&dmaq->curr->vb2, VB2_BUF_STATE_QUEUED); + dmaq->curr = NULL; + } + return -EBUSY; + } + + /* The SAA7134 has a 1K FIFO; the datasheet suggests that when + * configured conservatively, there's 22 usec of buffering for video. + * We therefore request a DMA latency of 20 usec, giving us 2 usec of + * margin in case the FIFO is configured differently to the datasheet. + * Unfortunately, I lack register-level documentation to check the + * Linux FIFO setup and confirm the perfect value. + */ + if ((dmaq == &dev->video_q && !vb2_is_streaming(&dev->vbi_vbq)) || + (dmaq == &dev->vbi_q && !vb2_is_streaming(&dev->video_vbq))) + pm_qos_add_request(&dev->qos_request, + PM_QOS_CPU_DMA_LATENCY, 20); + dmaq->seq_nr = 0; + + return 0; } -static void buffer_release(struct videobuf_queue *q, struct videobuf_buffer *vb) +void saa7134_vb2_stop_streaming(struct vb2_queue *vq) { - struct saa7134_buf *buf = container_of(vb,struct saa7134_buf,vb); + struct saa7134_dmaqueue *dmaq = vq->drv_priv; + struct saa7134_dev *dev = dmaq->dev; + + saa7134_stop_streaming(dev, dmaq); - saa7134_dma_free(q,buf); + if ((dmaq == &dev->video_q && !vb2_is_streaming(&dev->vbi_vbq)) || + (dmaq == &dev->vbi_q && !vb2_is_streaming(&dev->video_vbq))) + pm_qos_remove_request(&dev->qos_request); } -static struct videobuf_queue_ops video_qops = { - .buf_setup = buffer_setup, - .buf_prepare = buffer_prepare, - .buf_queue = buffer_queue, - .buf_release = buffer_release, +static struct vb2_ops vb2_qops = { + .queue_setup = queue_setup, + .buf_init = buffer_init, + .buf_prepare = buffer_prepare, + .buf_finish = buffer_finish, + .buf_queue = saa7134_vb2_buffer_queue, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, + .start_streaming = saa7134_vb2_start_streaming, + .stop_streaming = saa7134_vb2_stop_streaming, }; /* ------------------------------------------------------------------ */ @@ -1068,7 +1087,7 @@ static int saa7134_s_ctrl(struct v4l2_ctrl *ctrl) default: return -EINVAL; } - if (restart_overlay && res_locked(dev, RESOURCE_OVERLAY)) { + if (restart_overlay && dev->overlay_owner) { spin_lock_irqsave(&dev->slock, flags); stop_preview(dev); start_preview(dev); @@ -1079,182 +1098,57 @@ static int saa7134_s_ctrl(struct v4l2_ctrl *ctrl) /* ------------------------------------------------------------------ */ -static struct videobuf_queue *saa7134_queue(struct file *file) +static inline struct vb2_queue *saa7134_queue(struct file *file) { - struct video_device *vdev = video_devdata(file); - struct saa7134_dev *dev = video_drvdata(file); - struct saa7134_fh *fh = file->private_data; - struct videobuf_queue *q = NULL; - - switch (vdev->vfl_type) { - case VFL_TYPE_GRABBER: - q = fh->is_empress ? &dev->empress_tsq : &dev->cap; - break; - case VFL_TYPE_VBI: - q = &dev->vbi; - break; - default: - BUG(); - } - return q; -} - -static int saa7134_resource(struct file *file) -{ - struct video_device *vdev = video_devdata(file); - struct saa7134_fh *fh = file->private_data; - - if (vdev->vfl_type == VFL_TYPE_GRABBER) - return fh->is_empress ? RESOURCE_EMPRESS : RESOURCE_VIDEO; - - if (vdev->vfl_type == VFL_TYPE_VBI) - return RESOURCE_VBI; - - BUG(); - return 0; + return video_devdata(file)->queue; } static int video_open(struct file *file) { struct video_device *vdev = video_devdata(file); struct saa7134_dev *dev = video_drvdata(file); - struct saa7134_fh *fh; - - /* allocate + initialize per filehandle data */ - fh = kzalloc(sizeof(*fh),GFP_KERNEL); - if (NULL == fh) - return -ENOMEM; + int ret = v4l2_fh_open(file); - v4l2_fh_init(&fh->fh, vdev); - file->private_data = fh; + if (ret < 0) + return ret; + mutex_lock(&dev->lock); if (vdev->vfl_type == VFL_TYPE_RADIO) { /* switch to radio mode */ - saa7134_tvaudio_setinput(dev,&card(dev).radio); + saa7134_tvaudio_setinput(dev, &card(dev).radio); saa_call_all(dev, tuner, s_radio); } else { /* switch to video/vbi mode */ - video_mux(dev,dev->ctl_input); + video_mux(dev, dev->ctl_input); } - v4l2_fh_add(&fh->fh); + mutex_unlock(&dev->lock); return 0; } -static ssize_t -video_read(struct file *file, char __user *data, size_t count, loff_t *ppos) -{ - struct video_device *vdev = video_devdata(file); - struct saa7134_dev *dev = video_drvdata(file); - struct saa7134_fh *fh = file->private_data; - - switch (vdev->vfl_type) { - case VFL_TYPE_GRABBER: - if (res_locked(dev, RESOURCE_VIDEO)) - return -EBUSY; - return videobuf_read_one(saa7134_queue(file), - data, count, ppos, - file->f_flags & O_NONBLOCK); - case VFL_TYPE_VBI: - if (!res_get(dev, fh, RESOURCE_VBI)) - return -EBUSY; - return videobuf_read_stream(saa7134_queue(file), - data, count, ppos, 1, - file->f_flags & O_NONBLOCK); - break; - default: - BUG(); - return 0; - } -} - -static unsigned int -video_poll(struct file *file, struct poll_table_struct *wait) -{ - unsigned long req_events = poll_requested_events(wait); - struct video_device *vdev = video_devdata(file); - struct saa7134_dev *dev = video_drvdata(file); - struct saa7134_fh *fh = file->private_data; - struct videobuf_buffer *buf = NULL; - unsigned int rc = 0; - - if (v4l2_event_pending(&fh->fh)) - rc = POLLPRI; - else if (req_events & POLLPRI) - poll_wait(file, &fh->fh.wait, wait); - - if (vdev->vfl_type == VFL_TYPE_VBI) - return rc | videobuf_poll_stream(file, &dev->vbi, wait); - - if (res_check(fh, RESOURCE_VIDEO)) { - mutex_lock(&dev->cap.vb_lock); - if (!list_empty(&dev->cap.stream)) - buf = list_entry(dev->cap.stream.next, struct videobuf_buffer, stream); - } else { - mutex_lock(&dev->cap.vb_lock); - if (UNSET == dev->cap.read_off) { - /* need to capture a new frame */ - if (res_locked(dev, RESOURCE_VIDEO)) - goto err; - if (0 != dev->cap.ops->buf_prepare(&dev->cap, - dev->cap.read_buf, dev->cap.field)) - goto err; - dev->cap.ops->buf_queue(&dev->cap, dev->cap.read_buf); - dev->cap.read_off = 0; - } - buf = dev->cap.read_buf; - } - - if (!buf) - goto err; - - poll_wait(file, &buf->done, wait); - if (buf->state == VIDEOBUF_DONE || buf->state == VIDEOBUF_ERROR) - rc |= POLLIN | POLLRDNORM; - mutex_unlock(&dev->cap.vb_lock); - return rc; - -err: - mutex_unlock(&dev->cap.vb_lock); - return rc | POLLERR; -} - static int video_release(struct file *file) { struct video_device *vdev = video_devdata(file); struct saa7134_dev *dev = video_drvdata(file); - struct saa7134_fh *fh = file->private_data; + struct v4l2_fh *fh = file->private_data; struct saa6588_command cmd; unsigned long flags; + mutex_lock(&dev->lock); saa7134_tvaudio_close(dev); /* turn off overlay */ - if (res_check(fh, RESOURCE_OVERLAY)) { + if (fh == dev->overlay_owner) { spin_lock_irqsave(&dev->slock,flags); stop_preview(dev); spin_unlock_irqrestore(&dev->slock,flags); - res_free(dev, fh, RESOURCE_OVERLAY); - } - - /* stop video capture */ - if (res_check(fh, RESOURCE_VIDEO)) { - pm_qos_remove_request(&dev->qos_request); - videobuf_streamoff(&dev->cap); - res_free(dev, fh, RESOURCE_VIDEO); - videobuf_mmap_free(&dev->cap); - } - if (dev->cap.read_buf) { - buffer_release(&dev->cap, dev->cap.read_buf); - kfree(dev->cap.read_buf); + dev->overlay_owner = NULL; } - /* stop vbi capture */ - if (res_check(fh, RESOURCE_VBI)) { - videobuf_stop(&dev->vbi); - res_free(dev, fh, RESOURCE_VBI); - videobuf_mmap_free(&dev->vbi); - } + if (vdev->vfl_type == VFL_TYPE_RADIO) + v4l2_fh_release(file); + else + _vb2_fop_release(file, NULL); /* ts-capture will not work in planar mode, so turn it off Hac: 04.05*/ saa_andorb(SAA7134_OFMT_VIDEO_A, 0x1f, 0); @@ -1265,19 +1159,11 @@ static int video_release(struct file *file) saa_call_all(dev, core, s_power, 0); if (vdev->vfl_type == VFL_TYPE_RADIO) saa_call_all(dev, core, ioctl, SAA6588_CMD_CLOSE, &cmd); + mutex_unlock(&dev->lock); - v4l2_fh_del(&fh->fh); - v4l2_fh_exit(&fh->fh); - file->private_data = NULL; - kfree(fh); return 0; } -static int video_mmap(struct file *file, struct vm_area_struct * vma) -{ - return videobuf_mmap_mapper(saa7134_queue(file), vma); -} - static ssize_t radio_read(struct file *file, char __user *data, size_t count, loff_t *ppos) { @@ -1290,7 +1176,9 @@ static ssize_t radio_read(struct file *file, char __user *data, cmd.instance = file; cmd.result = -ENODEV; + mutex_lock(&dev->lock); saa_call_all(dev, core, ioctl, SAA6588_CMD_READ, &cmd); + mutex_unlock(&dev->lock); return cmd.result; } @@ -1304,7 +1192,9 @@ static unsigned int radio_poll(struct file *file, poll_table *wait) cmd.instance = file; cmd.event_list = wait; cmd.result = 0; + mutex_lock(&dev->lock); saa_call_all(dev, core, ioctl, SAA6588_CMD_POLL, &cmd); + mutex_unlock(&dev->lock); return rc | cmd.result; } @@ -1338,7 +1228,7 @@ static int saa7134_g_fmt_vid_cap(struct file *file, void *priv, f->fmt.pix.width = dev->width; f->fmt.pix.height = dev->height; - f->fmt.pix.field = dev->cap.field; + f->fmt.pix.field = dev->field; f->fmt.pix.pixelformat = dev->fmt->fourcc; f->fmt.pix.bytesperline = (f->fmt.pix.width * dev->fmt->depth) >> 3; @@ -1362,7 +1252,6 @@ static int saa7134_g_fmt_vid_overlay(struct file *file, void *priv, printk(KERN_ERR "V4L2_BUF_TYPE_VIDEO_OVERLAY: no_overlay\n"); return -EINVAL; } - mutex_lock(&dev->lock); f->fmt.win = dev->win; f->fmt.win.clips = clips; if (clips == NULL) @@ -1376,7 +1265,6 @@ static int saa7134_g_fmt_vid_overlay(struct file *file, void *priv, sizeof(struct v4l2_rect))) err = -EFAULT; } - mutex_unlock(&dev->lock); return err; } @@ -1457,10 +1345,10 @@ static int saa7134_s_fmt_vid_cap(struct file *file, void *priv, if (0 != err) return err; - dev->fmt = format_by_fourcc(f->fmt.pix.pixelformat); - dev->width = f->fmt.pix.width; - dev->height = f->fmt.pix.height; - dev->cap.field = f->fmt.pix.field; + dev->fmt = format_by_fourcc(f->fmt.pix.pixelformat); + dev->width = f->fmt.pix.width; + dev->height = f->fmt.pix.height; + dev->field = f->fmt.pix.field; return 0; } @@ -1481,25 +1369,20 @@ static int saa7134_s_fmt_vid_overlay(struct file *file, void *priv, if (0 != err) return err; - mutex_lock(&dev->lock); - dev->win = f->fmt.win; dev->nclips = f->fmt.win.clipcount; if (copy_from_user(dev->clips, f->fmt.win.clips, - sizeof(struct v4l2_clip) * dev->nclips)) { - mutex_unlock(&dev->lock); + sizeof(struct v4l2_clip) * dev->nclips)) return -EFAULT; - } - if (res_check(priv, RESOURCE_OVERLAY)) { + if (priv == dev->overlay_owner) { spin_lock_irqsave(&dev->slock, flags); stop_preview(dev); start_preview(dev); spin_unlock_irqrestore(&dev->slock, flags); } - mutex_unlock(&dev->lock); return 0; } @@ -1551,9 +1434,7 @@ int saa7134_s_input(struct file *file, void *priv, unsigned int i) return -EINVAL; if (NULL == card_in(dev, i).name) return -EINVAL; - mutex_lock(&dev->lock); video_mux(dev, i); - mutex_unlock(&dev->lock); return 0; } EXPORT_SYMBOL_GPL(saa7134_s_input); @@ -1563,7 +1444,6 @@ int saa7134_querycap(struct file *file, void *priv, { struct saa7134_dev *dev = video_drvdata(file); struct video_device *vdev = video_devdata(file); - struct saa7134_fh *fh = priv; u32 radio_caps, video_caps, vbi_caps; unsigned int tuner_type = dev->tuner_type; @@ -1582,7 +1462,7 @@ int saa7134_querycap(struct file *file, void *priv, radio_caps |= V4L2_CAP_RDS_CAPTURE; video_caps = V4L2_CAP_VIDEO_CAPTURE; - if (saa7134_no_overlay <= 0 && !fh->is_empress) + if (saa7134_no_overlay <= 0 && !is_empress(file)) video_caps |= V4L2_CAP_VIDEO_OVERLAY; vbi_caps = V4L2_CAP_VBI_CAPTURE; @@ -1613,12 +1493,12 @@ EXPORT_SYMBOL_GPL(saa7134_querycap); int saa7134_s_std(struct file *file, void *priv, v4l2_std_id id) { struct saa7134_dev *dev = video_drvdata(file); - struct saa7134_fh *fh = priv; + struct v4l2_fh *fh = priv; unsigned long flags; unsigned int i; v4l2_std_id fixup; - if (fh->is_empress && res_locked(dev, RESOURCE_OVERLAY)) { + if (is_empress(file) && dev->overlay_owner) { /* Don't change the std from the mpeg device if overlay is active. */ return -EBUSY; @@ -1657,8 +1537,7 @@ int saa7134_s_std(struct file *file, void *priv, v4l2_std_id id) id = tvnorms[i].id; - mutex_lock(&dev->lock); - if (!fh->is_empress && res_check(fh, RESOURCE_OVERLAY)) { + if (!is_empress(file) && fh == dev->overlay_owner) { spin_lock_irqsave(&dev->slock, flags); stop_preview(dev); spin_unlock_irqrestore(&dev->slock, flags); @@ -1672,7 +1551,6 @@ int saa7134_s_std(struct file *file, void *priv, v4l2_std_id id) set_tvnorm(dev, &tvnorms[i]); saa7134_tvaudio_do_scan(dev); - mutex_unlock(&dev->lock); return 0; } EXPORT_SYMBOL_GPL(saa7134_s_std); @@ -1686,6 +1564,35 @@ int saa7134_g_std(struct file *file, void *priv, v4l2_std_id *id) } EXPORT_SYMBOL_GPL(saa7134_g_std); +static v4l2_std_id saa7134_read_std(struct saa7134_dev *dev) +{ + static v4l2_std_id stds[] = { + V4L2_STD_UNKNOWN, + V4L2_STD_NTSC, + V4L2_STD_PAL, + V4L2_STD_SECAM }; + + v4l2_std_id result = 0; + + u8 st1 = saa_readb(SAA7134_STATUS_VIDEO1); + u8 st2 = saa_readb(SAA7134_STATUS_VIDEO2); + + if (!(st2 & 0x1)) /* RDCAP == 0 */ + result = V4L2_STD_UNKNOWN; + else + result = stds[st1 & 0x03]; + + return result; +} + +int saa7134_querystd(struct file *file, void *priv, v4l2_std_id *std) +{ + struct saa7134_dev *dev = video_drvdata(file); + *std &= saa7134_read_std(dev); + return 0; +} +EXPORT_SYMBOL_GPL(saa7134_querystd); + static int saa7134_cropcap(struct file *file, void *priv, struct v4l2_cropcap *cap) { @@ -1730,9 +1637,9 @@ static int saa7134_s_crop(struct file *file, void *f, const struct v4l2_crop *cr crop->type != V4L2_BUF_TYPE_VIDEO_OVERLAY) return -EINVAL; - if (res_locked(dev, RESOURCE_OVERLAY)) + if (dev->overlay_owner) return -EBUSY; - if (res_locked(dev, RESOURCE_VIDEO)) + if (vb2_is_streaming(&dev->video_vbq)) return -EBUSY; *c = crop->c; @@ -1826,12 +1733,10 @@ int saa7134_s_frequency(struct file *file, void *priv, if (0 != f->tuner) return -EINVAL; - mutex_lock(&dev->lock); saa_call_all(dev, tuner, s_frequency, f); saa7134_tvaudio_do_scan(dev); - mutex_unlock(&dev->lock); return 0; } EXPORT_SYMBOL_GPL(saa7134_s_frequency); @@ -1915,92 +1820,24 @@ static int saa7134_overlay(struct file *file, void *priv, unsigned int on) return -EINVAL; } - if (!res_get(dev, priv, RESOURCE_OVERLAY)) + if (dev->overlay_owner && priv != dev->overlay_owner) return -EBUSY; + dev->overlay_owner = priv; spin_lock_irqsave(&dev->slock, flags); start_preview(dev); spin_unlock_irqrestore(&dev->slock, flags); } if (!on) { - if (!res_check(priv, RESOURCE_OVERLAY)) + if (priv != dev->overlay_owner) return -EINVAL; spin_lock_irqsave(&dev->slock, flags); stop_preview(dev); spin_unlock_irqrestore(&dev->slock, flags); - res_free(dev, priv, RESOURCE_OVERLAY); + dev->overlay_owner = NULL; } return 0; } -int saa7134_reqbufs(struct file *file, void *priv, - struct v4l2_requestbuffers *p) -{ - return videobuf_reqbufs(saa7134_queue(file), p); -} -EXPORT_SYMBOL_GPL(saa7134_reqbufs); - -int saa7134_querybuf(struct file *file, void *priv, - struct v4l2_buffer *b) -{ - return videobuf_querybuf(saa7134_queue(file), b); -} -EXPORT_SYMBOL_GPL(saa7134_querybuf); - -int saa7134_qbuf(struct file *file, void *priv, struct v4l2_buffer *b) -{ - return videobuf_qbuf(saa7134_queue(file), b); -} -EXPORT_SYMBOL_GPL(saa7134_qbuf); - -int saa7134_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b) -{ - return videobuf_dqbuf(saa7134_queue(file), b, - file->f_flags & O_NONBLOCK); -} -EXPORT_SYMBOL_GPL(saa7134_dqbuf); - -int saa7134_streamon(struct file *file, void *priv, - enum v4l2_buf_type type) -{ - struct saa7134_dev *dev = video_drvdata(file); - int res = saa7134_resource(file); - - if (!res_get(dev, priv, res)) - return -EBUSY; - - /* The SAA7134 has a 1K FIFO; the datasheet suggests that when - * configured conservatively, there's 22 usec of buffering for video. - * We therefore request a DMA latency of 20 usec, giving us 2 usec of - * margin in case the FIFO is configured differently to the datasheet. - * Unfortunately, I lack register-level documentation to check the - * Linux FIFO setup and confirm the perfect value. - */ - if (res != RESOURCE_EMPRESS) - pm_qos_add_request(&dev->qos_request, - PM_QOS_CPU_DMA_LATENCY, 20); - - return videobuf_streamon(saa7134_queue(file)); -} -EXPORT_SYMBOL_GPL(saa7134_streamon); - -int saa7134_streamoff(struct file *file, void *priv, - enum v4l2_buf_type type) -{ - struct saa7134_dev *dev = video_drvdata(file); - int err; - int res = saa7134_resource(file); - - if (res != RESOURCE_EMPRESS) - pm_qos_remove_request(&dev->qos_request); - - err = videobuf_streamoff(saa7134_queue(file)); - if (err < 0) - return err; - res_free(dev, priv, res); - return 0; -} -EXPORT_SYMBOL_GPL(saa7134_streamoff); - #ifdef CONFIG_VIDEO_ADV_DEBUG static int vidioc_g_register (struct file *file, void *priv, struct v4l2_dbg_register *reg) @@ -2058,10 +1895,10 @@ static const struct v4l2_file_operations video_fops = .owner = THIS_MODULE, .open = video_open, .release = video_release, - .read = video_read, - .poll = video_poll, - .mmap = video_mmap, - .ioctl = video_ioctl2, + .read = vb2_fop_read, + .poll = vb2_fop_poll, + .mmap = vb2_fop_mmap, + .unlocked_ioctl = video_ioctl2, }; static const struct v4l2_ioctl_ops video_ioctl_ops = { @@ -2078,17 +1915,18 @@ static const struct v4l2_ioctl_ops video_ioctl_ops = { .vidioc_try_fmt_vbi_cap = saa7134_try_get_set_fmt_vbi_cap, .vidioc_s_fmt_vbi_cap = saa7134_try_get_set_fmt_vbi_cap, .vidioc_cropcap = saa7134_cropcap, - .vidioc_reqbufs = saa7134_reqbufs, - .vidioc_querybuf = saa7134_querybuf, - .vidioc_qbuf = saa7134_qbuf, - .vidioc_dqbuf = saa7134_dqbuf, + .vidioc_reqbufs = vb2_ioctl_reqbufs, + .vidioc_querybuf = vb2_ioctl_querybuf, + .vidioc_qbuf = vb2_ioctl_qbuf, + .vidioc_dqbuf = vb2_ioctl_dqbuf, .vidioc_s_std = saa7134_s_std, .vidioc_g_std = saa7134_g_std, + .vidioc_querystd = saa7134_querystd, .vidioc_enum_input = saa7134_enum_input, .vidioc_g_input = saa7134_g_input, .vidioc_s_input = saa7134_s_input, - .vidioc_streamon = saa7134_streamon, - .vidioc_streamoff = saa7134_streamoff, + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, .vidioc_g_tuner = saa7134_g_tuner, .vidioc_s_tuner = saa7134_s_tuner, .vidioc_g_crop = saa7134_g_crop, @@ -2112,7 +1950,7 @@ static const struct v4l2_file_operations radio_fops = { .open = video_open, .read = radio_read, .release = video_release, - .ioctl = video_ioctl2, + .unlocked_ioctl = video_ioctl2, .poll = radio_poll, }; @@ -2190,6 +2028,8 @@ static const struct v4l2_ctrl_config saa7134_ctrl_automute = { int saa7134_video_init1(struct saa7134_dev *dev) { struct v4l2_ctrl_handler *hdl = &dev->ctrl_handler; + struct vb2_queue *q; + int ret; /* sanitycheck insmod options */ if (gbuffers < 2 || gbuffers > VIDEO_MAX_FRAME) @@ -2241,6 +2081,7 @@ int saa7134_video_init1(struct saa7134_dev *dev) dev->fmt = format_by_fourcc(V4L2_PIX_FMT_BGR24); dev->width = 720; dev->height = 576; + dev->field = V4L2_FIELD_INTERLACED; dev->win.w.width = dev->width; dev->win.w.height = dev->height; dev->win.field = V4L2_FIELD_INTERLACED; @@ -2252,20 +2093,47 @@ int saa7134_video_init1(struct saa7134_dev *dev) if (saa7134_boards[dev->board].video_out) saa7134_videoport_init(dev); - videobuf_queue_sg_init(&dev->cap, &video_qops, - &dev->pci->dev, &dev->slock, - V4L2_BUF_TYPE_VIDEO_CAPTURE, - V4L2_FIELD_INTERLACED, - sizeof(struct saa7134_buf), - dev, NULL); - videobuf_queue_sg_init(&dev->vbi, &saa7134_vbi_qops, - &dev->pci->dev, &dev->slock, - V4L2_BUF_TYPE_VBI_CAPTURE, - V4L2_FIELD_SEQ_TB, - sizeof(struct saa7134_buf), - dev, NULL); - saa7134_pgtable_alloc(dev->pci, &dev->pt_cap); - saa7134_pgtable_alloc(dev->pci, &dev->pt_vbi); + q = &dev->video_vbq; + q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + /* + * Do not add VB2_USERPTR unless explicitly requested: the saa7134 DMA + * engine cannot handle transfers that do not start at the beginning + * of a page. A user-provided pointer can start anywhere in a page, so + * USERPTR support is a no-go unless the application knows about these + * limitations and has special support for this. + */ + q->io_modes = VB2_MMAP | VB2_READ; + if (saa7134_userptr) + q->io_modes |= VB2_USERPTR; + q->drv_priv = &dev->video_q; + q->ops = &vb2_qops; + q->gfp_flags = GFP_DMA32; + q->mem_ops = &vb2_dma_sg_memops; + q->buf_struct_size = sizeof(struct saa7134_buf); + q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + q->lock = &dev->lock; + ret = vb2_queue_init(q); + if (ret) + return ret; + saa7134_pgtable_alloc(dev->pci, &dev->video_q.pt); + + q = &dev->vbi_vbq; + q->type = V4L2_BUF_TYPE_VBI_CAPTURE; + /* Don't add VB2_USERPTR, see comment above */ + q->io_modes = VB2_MMAP | VB2_READ; + if (saa7134_userptr) + q->io_modes |= VB2_USERPTR; + q->drv_priv = &dev->vbi_q; + q->ops = &saa7134_vbi_qops; + q->gfp_flags = GFP_DMA32; + q->mem_ops = &vb2_dma_sg_memops; + q->buf_struct_size = sizeof(struct saa7134_buf); + q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + q->lock = &dev->lock; + ret = vb2_queue_init(q); + if (ret) + return ret; + saa7134_pgtable_alloc(dev->pci, &dev->vbi_q.pt); return 0; } @@ -2273,8 +2141,10 @@ int saa7134_video_init1(struct saa7134_dev *dev) void saa7134_video_fini(struct saa7134_dev *dev) { /* free stuff */ - saa7134_pgtable_free(dev->pci, &dev->pt_cap); - saa7134_pgtable_free(dev->pci, &dev->pt_vbi); + vb2_queue_release(&dev->video_vbq); + saa7134_pgtable_free(dev->pci, &dev->video_q.pt); + vb2_queue_release(&dev->vbi_vbq); + saa7134_pgtable_free(dev->pci, &dev->vbi_q.pt); v4l2_ctrl_handler_free(&dev->ctrl_handler); if (card_has_radio(dev)) v4l2_ctrl_handler_free(&dev->radio_ctrl_handler); @@ -2367,8 +2237,7 @@ void saa7134_irq_video_done(struct saa7134_dev *dev, unsigned long status) spin_lock(&dev->slock); if (dev->video_q.curr) { - dev->video_fieldcount++; - field = dev->video_q.curr->vb.field; + field = dev->field; if (V4L2_FIELD_HAS_BOTH(field)) { /* make sure we have seen both fields */ if ((status & 0x10) == 0x00) { @@ -2384,18 +2253,10 @@ void saa7134_irq_video_done(struct saa7134_dev *dev, unsigned long status) if ((status & 0x10) != 0x00) goto done; } - dev->video_q.curr->vb.field_count = dev->video_fieldcount; - saa7134_buffer_finish(dev,&dev->video_q,VIDEOBUF_DONE); + saa7134_buffer_finish(dev, &dev->video_q, VB2_BUF_STATE_DONE); } - saa7134_buffer_next(dev,&dev->video_q); + saa7134_buffer_next(dev, &dev->video_q); done: spin_unlock(&dev->slock); } - -/* ----------------------------------------------------------- */ -/* - * Local variables: - * c-basic-offset: 8 - * End: - */ diff --git a/drivers/media/pci/saa7134/saa7134.h b/drivers/media/pci/saa7134/saa7134.h index 2474e848f2c0..e47edd4b57ce 100644 --- a/drivers/media/pci/saa7134/saa7134.h +++ b/drivers/media/pci/saa7134/saa7134.h @@ -41,11 +41,11 @@ #include <media/tuner.h> #include <media/rc-core.h> #include <media/ir-kbd-i2c.h> -#include <media/videobuf-dma-sg.h> +#include <media/videobuf2-dma-sg.h> #include <sound/core.h> #include <sound/pcm.h> #if IS_ENABLED(CONFIG_VIDEO_SAA7134_DVB) -#include <media/videobuf-dvb.h> +#include <media/videobuf2-dvb.h> #endif #include "tda8290.h" @@ -453,17 +453,15 @@ struct saa7134_thread { /* buffer for one video/vbi/ts frame */ struct saa7134_buf { /* common v4l buffer stuff -- must be first */ - struct videobuf_buffer vb; + struct vb2_buffer vb2; /* saa7134 specific */ - struct saa7134_format *fmt; unsigned int top_seen; int (*activate)(struct saa7134_dev *dev, struct saa7134_buf *buf, struct saa7134_buf *next); - /* page tables */ - struct saa7134_pgtable *pt; + struct list_head entry; }; struct saa7134_dmaqueue { @@ -472,13 +470,8 @@ struct saa7134_dmaqueue { struct list_head queue; struct timer_list timeout; unsigned int need_two; -}; - -/* video filehandle status */ -struct saa7134_fh { - struct v4l2_fh fh; - bool is_empress; - unsigned int resources; + unsigned int seq_nr; + struct saa7134_pgtable pt; }; /* dmasound dsp status */ @@ -504,7 +497,10 @@ struct saa7134_dmasound { unsigned int blksize; unsigned int bufsize; struct saa7134_pgtable pt; - struct videobuf_dmabuf dma; + void *vaddr; + struct scatterlist *sglist; + int sglen; + int nr_pages; unsigned int dma_blk; unsigned int read_offset; unsigned int read_count; @@ -515,7 +511,6 @@ struct saa7134_dmasound { /* ts/mpeg status */ struct saa7134_ts { /* TS capture */ - struct saa7134_pgtable pt_ts; int nr_packets; int nr_bufs; }; @@ -584,21 +579,35 @@ struct saa7134_dev { struct v4l2_window win; struct v4l2_clip clips[8]; unsigned int nclips; + struct v4l2_fh *overlay_owner; /* video+ts+vbi capture */ struct saa7134_dmaqueue video_q; - struct videobuf_queue cap; - struct saa7134_pgtable pt_cap; + struct vb2_queue video_vbq; struct saa7134_dmaqueue vbi_q; - struct videobuf_queue vbi; - struct saa7134_pgtable pt_vbi; - unsigned int video_fieldcount; - unsigned int vbi_fieldcount; + struct vb2_queue vbi_vbq; + enum v4l2_field field; struct saa7134_format *fmt; unsigned int width, height; + unsigned int vbi_hlen, vbi_vlen; struct pm_qos_request qos_request; + /* SAA7134_MPEG_* */ + struct saa7134_ts ts; + struct saa7134_dmaqueue ts_q; + enum v4l2_field ts_field; + int ts_started; + struct saa7134_mpeg_ops *mops; + + /* SAA7134_MPEG_EMPRESS only */ + struct video_device *empress_dev; + struct v4l2_subdev *empress_sd; + struct vb2_queue empress_vbq; + struct work_struct empress_workqueue; + int empress_started; + struct v4l2_ctrl_handler empress_ctrl_handler; + /* various v4l controls */ struct saa7134_tvnorm *tvnorm; /* video */ struct saa7134_tvaudio *tvaudio; @@ -635,23 +644,9 @@ struct saa7134_dev { /* I2C keyboard data */ struct IR_i2c_init_data init_data; - /* SAA7134_MPEG_* */ - struct saa7134_ts ts; - struct saa7134_dmaqueue ts_q; - int ts_started; - struct saa7134_mpeg_ops *mops; - - /* SAA7134_MPEG_EMPRESS only */ - struct video_device *empress_dev; - struct v4l2_subdev *empress_sd; - struct videobuf_queue empress_tsq; - struct work_struct empress_workqueue; - int empress_started; - struct v4l2_ctrl_handler empress_ctrl_handler; - #if IS_ENABLED(CONFIG_VIDEO_SAA7134_DVB) /* SAA7134_MPEG_DVB only */ - struct videobuf_dvb_frontends frontends; + struct vb2_dvb_frontends frontends; int (*original_demod_sleep)(struct dvb_frontend *fe); int (*original_set_voltage)(struct dvb_frontend *fe, fe_sec_voltage_t voltage); int (*original_set_high_voltage)(struct dvb_frontend *fe, long arg); @@ -705,14 +700,12 @@ struct saa7134_dev { _rc; \ }) -static inline int res_check(struct saa7134_fh *fh, unsigned int bit) +static inline bool is_empress(struct file *file) { - return fh->resources & bit; -} + struct video_device *vdev = video_devdata(file); + struct saa7134_dev *dev = video_get_drvdata(vdev); -static inline int res_locked(struct saa7134_dev *dev, unsigned int bit) -{ - return dev->resources & bit; + return vdev->queue == &dev->empress_vbq; } /* ----------------------------------------------------------- */ @@ -721,6 +714,7 @@ static inline int res_locked(struct saa7134_dev *dev, unsigned int bit) extern struct list_head saa7134_devlist; extern struct mutex saa7134_devlist_lock; extern int saa7134_no_overlay; +extern bool saa7134_userptr; void saa7134_track_gpio(struct saa7134_dev *dev, char *msg); void saa7134_set_gpio(struct saa7134_dev *dev, int bit_no, int value); @@ -743,7 +737,7 @@ void saa7134_buffer_finish(struct saa7134_dev *dev, struct saa7134_dmaqueue *q, unsigned int state); void saa7134_buffer_next(struct saa7134_dev *dev, struct saa7134_dmaqueue *q); void saa7134_buffer_timeout(unsigned long data); -void saa7134_dma_free(struct videobuf_queue *q,struct saa7134_buf *buf); +void saa7134_stop_streaming(struct saa7134_dev *dev, struct saa7134_dmaqueue *q); int saa7134_set_dmabits(struct saa7134_dev *dev); @@ -777,8 +771,13 @@ extern unsigned int video_debug; extern struct video_device saa7134_video_template; extern struct video_device saa7134_radio_template; +void saa7134_vb2_buffer_queue(struct vb2_buffer *vb); +int saa7134_vb2_start_streaming(struct vb2_queue *vq, unsigned int count); +void saa7134_vb2_stop_streaming(struct vb2_queue *vq); + int saa7134_s_std(struct file *file, void *priv, v4l2_std_id id); int saa7134_g_std(struct file *file, void *priv, v4l2_std_id *id); +int saa7134_querystd(struct file *file, void *priv, v4l2_std_id *std); int saa7134_enum_input(struct file *file, void *priv, struct v4l2_input *i); int saa7134_g_input(struct file *file, void *priv, unsigned int *i); int saa7134_s_input(struct file *file, void *priv, unsigned int i); @@ -792,16 +791,6 @@ int saa7134_g_frequency(struct file *file, void *priv, struct v4l2_frequency *f); int saa7134_s_frequency(struct file *file, void *priv, const struct v4l2_frequency *f); -int saa7134_reqbufs(struct file *file, void *priv, - struct v4l2_requestbuffers *p); -int saa7134_querybuf(struct file *file, void *priv, - struct v4l2_buffer *b); -int saa7134_qbuf(struct file *file, void *priv, struct v4l2_buffer *b); -int saa7134_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b); -int saa7134_streamon(struct file *file, void *priv, - enum v4l2_buf_type type); -int saa7134_streamoff(struct file *file, void *priv, - enum v4l2_buf_type type); int saa7134_videoport_init(struct saa7134_dev *dev); void saa7134_set_tvnorm_hw(struct saa7134_dev *dev); @@ -818,7 +807,16 @@ void saa7134_video_fini(struct saa7134_dev *dev); #define TS_PACKET_SIZE 188 /* TS packets 188 bytes */ -extern struct videobuf_queue_ops saa7134_ts_qops; +int saa7134_ts_buffer_init(struct vb2_buffer *vb2); +int saa7134_ts_buffer_prepare(struct vb2_buffer *vb2); +void saa7134_ts_buffer_finish(struct vb2_buffer *vb2); +int saa7134_ts_queue_setup(struct vb2_queue *q, const struct v4l2_format *fmt, + unsigned int *nbuffers, unsigned int *nplanes, + unsigned int sizes[], void *alloc_ctxs[]); +int saa7134_ts_start_streaming(struct vb2_queue *vq, unsigned int count); +void saa7134_ts_stop_streaming(struct vb2_queue *vq); + +extern struct vb2_ops saa7134_ts_qops; int saa7134_ts_init1(struct saa7134_dev *dev); int saa7134_ts_fini(struct saa7134_dev *dev); @@ -835,7 +833,7 @@ int saa7134_ts_stop(struct saa7134_dev *dev); /* ----------------------------------------------------------- */ /* saa7134-vbi.c */ -extern struct videobuf_queue_ops saa7134_vbi_qops; +extern struct vb2_ops saa7134_vbi_qops; extern struct video_device saa7134_vbi_template; int saa7134_vbi_init1(struct saa7134_dev *dev); diff --git a/drivers/media/pci/saa7146/mxb.c b/drivers/media/pci/saa7146/mxb.c index 33abe332d175..c4c8fce8f2b4 100644 --- a/drivers/media/pci/saa7146/mxb.c +++ b/drivers/media/pci/saa7146/mxb.c @@ -357,7 +357,7 @@ static int mxb_init_done(struct saa7146_dev* dev) tea6420_route(mxb, 6); /* select video mode in saa7111a */ - saa7111a_call(mxb, core, s_std, std); + saa7111a_call(mxb, video, s_std, std); /* select tuner-output on saa7111a */ i = 0; @@ -379,8 +379,8 @@ static int mxb_init_done(struct saa7146_dev* dev) /* These two gpio calls set the GPIO pins that control the tda9820 */ saa7146_write(dev, GPIO_CTRL, 0x00404050); saa7111a_call(mxb, core, s_gpio, 1); - saa7111a_call(mxb, core, s_std, std); - tuner_call(mxb, core, s_std, std); + saa7111a_call(mxb, video, s_std, std); + tuner_call(mxb, video, s_std, std); /* switch to tuner-channel on tea6415c */ tea6415c_call(mxb, video, s_routing, 3, 17, 0); @@ -771,9 +771,9 @@ static int std_callback(struct saa7146_dev *dev, struct saa7146_standard *standa /* These two gpio calls set the GPIO pins that control the tda9820 */ saa7146_write(dev, GPIO_CTRL, 0x00404050); saa7111a_call(mxb, core, s_gpio, 0); - saa7111a_call(mxb, core, s_std, std); + saa7111a_call(mxb, video, s_std, std); if (mxb->cur_input == 0) - tuner_call(mxb, core, s_std, std); + tuner_call(mxb, video, s_std, std); } else { v4l2_std_id std = V4L2_STD_PAL_BG; @@ -783,9 +783,9 @@ static int std_callback(struct saa7146_dev *dev, struct saa7146_standard *standa /* These two gpio calls set the GPIO pins that control the tda9820 */ saa7146_write(dev, GPIO_CTRL, 0x00404050); saa7111a_call(mxb, core, s_gpio, 1); - saa7111a_call(mxb, core, s_std, std); + saa7111a_call(mxb, video, s_std, std); if (mxb->cur_input == 0) - tuner_call(mxb, core, s_std, std); + tuner_call(mxb, video, s_std, std); } return 0; } diff --git a/drivers/media/pci/sta2x11/sta2x11_vip.c b/drivers/media/pci/sta2x11/sta2x11_vip.c index bb11443ed63e..d2abd3b5c2bf 100644 --- a/drivers/media/pci/sta2x11/sta2x11_vip.c +++ b/drivers/media/pci/sta2x11/sta2x11_vip.c @@ -357,7 +357,7 @@ static int start_streaming(struct vb2_queue *vq, unsigned int count) } /* abort streaming and wait for last buffer */ -static int stop_streaming(struct vb2_queue *vq) +static void stop_streaming(struct vb2_queue *vq) { struct sta2x11_vip *vip = vb2_get_drv_priv(vq); struct vip_buffer *vip_buf, *node; @@ -374,7 +374,6 @@ static int stop_streaming(struct vb2_queue *vq) list_del(&vip_buf->list); } spin_unlock(&vip->lock); - return 0; } static struct vb2_ops vip_video_qops = { @@ -445,7 +444,7 @@ static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id std) int status; if (V4L2_STD_ALL == std) { - v4l2_subdev_call(vip->decoder, core, s_std, std); + v4l2_subdev_call(vip->decoder, video, s_std, std); ssleep(2); v4l2_subdev_call(vip->decoder, video, querystd, &newstd); v4l2_subdev_call(vip->decoder, video, g_input_status, &status); @@ -468,7 +467,7 @@ static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id std) vip->format = formats_50[0]; } - return v4l2_subdev_call(vip->decoder, core, s_std, std); + return v4l2_subdev_call(vip->decoder, video, s_std, std); } /** diff --git a/drivers/media/pci/ttpci/av7110_av.c b/drivers/media/pci/ttpci/av7110_av.c index 301029ca4535..9544cfc06601 100644 --- a/drivers/media/pci/ttpci/av7110_av.c +++ b/drivers/media/pci/ttpci/av7110_av.c @@ -958,8 +958,10 @@ static unsigned int dvb_video_poll(struct file *file, poll_table *wait) if (av7110->playing) { if (FREE_COND) mask |= (POLLOUT | POLLWRNORM); - } else /* if not playing: may play if asked for */ - mask |= (POLLOUT | POLLWRNORM); + } else { + /* if not playing: may play if asked for */ + mask |= (POLLOUT | POLLWRNORM); + } } return mask; diff --git a/drivers/media/pci/zoran/zoran_device.c b/drivers/media/pci/zoran/zoran_device.c index 519164c572c8..bf34b93f23ee 100644 --- a/drivers/media/pci/zoran/zoran_device.c +++ b/drivers/media/pci/zoran/zoran_device.c @@ -1572,7 +1572,7 @@ zoran_init_hardware (struct zoran *zr) } decoder_call(zr, core, init, 0); - decoder_call(zr, core, s_std, zr->norm); + decoder_call(zr, video, s_std, zr->norm); decoder_call(zr, video, s_routing, zr->card.input[zr->input].muxsel, 0, 0); diff --git a/drivers/media/pci/zoran/zoran_driver.c b/drivers/media/pci/zoran/zoran_driver.c index e7e9840c6c35..099d5fbebb7c 100644 --- a/drivers/media/pci/zoran/zoran_driver.c +++ b/drivers/media/pci/zoran/zoran_driver.c @@ -1469,7 +1469,7 @@ zoran_set_norm (struct zoran *zr, if (on) zr36057_overlay(zr, 0); - decoder_call(zr, core, s_std, norm); + decoder_call(zr, video, s_std, norm); encoder_call(zr, video, s_std_output, norm); if (on) diff --git a/drivers/media/platform/blackfin/bfin_capture.c b/drivers/media/platform/blackfin/bfin_capture.c index 200bec91182e..16e4b1c525c4 100644 --- a/drivers/media/platform/blackfin/bfin_capture.c +++ b/drivers/media/platform/blackfin/bfin_capture.c @@ -427,15 +427,12 @@ static int bcap_start_streaming(struct vb2_queue *vq, unsigned int count) return 0; } -static int bcap_stop_streaming(struct vb2_queue *vq) +static void bcap_stop_streaming(struct vb2_queue *vq) { struct bcap_device *bcap_dev = vb2_get_drv_priv(vq); struct ppi_if *ppi = bcap_dev->ppi; int ret; - if (!vb2_is_streaming(vq)) - return 0; - bcap_dev->stop = true; wait_for_completion(&bcap_dev->comp); ppi->ops->stop(ppi); @@ -452,7 +449,6 @@ static int bcap_stop_streaming(struct vb2_queue *vq) list_del(&bcap_dev->cur_frm->list); vb2_buffer_done(&bcap_dev->cur_frm->vb, VB2_BUF_STATE_ERROR); } - return 0; } static struct vb2_ops bcap_video_qops = { @@ -635,7 +631,7 @@ static int bcap_s_std(struct file *file, void *priv, v4l2_std_id std) if (vb2_is_busy(&bcap_dev->buffer_queue)) return -EBUSY; - ret = v4l2_subdev_call(bcap_dev->sd, core, s_std, std); + ret = v4l2_subdev_call(bcap_dev->sd, video, s_std, std); if (ret < 0) return ret; @@ -648,7 +644,9 @@ static int bcap_enum_dv_timings(struct file *file, void *priv, { struct bcap_device *bcap_dev = video_drvdata(file); - return v4l2_subdev_call(bcap_dev->sd, video, + timings->pad = 0; + + return v4l2_subdev_call(bcap_dev->sd, pad, enum_dv_timings, timings); } @@ -1069,7 +1067,7 @@ static int bcap_probe(struct platform_device *pdev) /* now we can probe the default state */ if (config->inputs[0].capabilities & V4L2_IN_CAP_STD) { v4l2_std_id std; - ret = v4l2_subdev_call(bcap_dev->sd, core, g_std, &std); + ret = v4l2_subdev_call(bcap_dev->sd, video, g_std, &std); if (ret) { v4l2_err(&bcap_dev->v4l2_dev, "Unable to get std\n"); diff --git a/drivers/media/platform/coda.c b/drivers/media/platform/coda.c index 3e5199ee5d25..b1783791d426 100644 --- a/drivers/media/platform/coda.c +++ b/drivers/media/platform/coda.c @@ -2269,7 +2269,7 @@ out: return ret; } -static int coda_stop_streaming(struct vb2_queue *q) +static void coda_stop_streaming(struct vb2_queue *q) { struct coda_ctx *ctx = vb2_get_drv_priv(q); struct coda_dev *dev = ctx->dev; @@ -2295,8 +2295,6 @@ static int coda_stop_streaming(struct vb2_queue *q) ctx->bitstream.vaddr, ctx->bitstream.size); ctx->runcounter = 0; } - - return 0; } static struct vb2_ops coda_qops = { @@ -3235,7 +3233,7 @@ static int coda_probe(struct platform_device *pdev) } if (devm_request_threaded_irq(&pdev->dev, irq, NULL, coda_irq_handler, - IRQF_ONESHOT, CODA_NAME, dev) < 0) { + IRQF_ONESHOT, dev_name(&pdev->dev), dev) < 0) { dev_err(&pdev->dev, "failed to request irq\n"); return -ENOENT; } diff --git a/drivers/media/platform/davinci/vpbe_display.c b/drivers/media/platform/davinci/vpbe_display.c index 656708252962..bf5eff99452b 100644 --- a/drivers/media/platform/davinci/vpbe_display.c +++ b/drivers/media/platform/davinci/vpbe_display.c @@ -355,8 +355,17 @@ static int vpbe_start_streaming(struct vb2_queue *vq, unsigned int count) /* Set parameters in OSD and VENC */ ret = vpbe_set_osd_display_params(fh->disp_dev, layer); - if (ret < 0) + if (ret < 0) { + struct vpbe_disp_buffer *buf, *tmp; + + vb2_buffer_done(&layer->cur_frm->vb, VB2_BUF_STATE_QUEUED); + list_for_each_entry_safe(buf, tmp, &layer->dma_queue, list) { + list_del(&buf->list); + vb2_buffer_done(&buf->vb, VB2_BUF_STATE_QUEUED); + } + return ret; + } /* * if request format is yuv420 semiplanar, need to @@ -368,7 +377,7 @@ static int vpbe_start_streaming(struct vb2_queue *vq, unsigned int count) return ret; } -static int vpbe_stop_streaming(struct vb2_queue *vq) +static void vpbe_stop_streaming(struct vb2_queue *vq) { struct vpbe_fh *fh = vb2_get_drv_priv(vq); struct vpbe_layer *layer = fh->layer; @@ -376,7 +385,7 @@ static int vpbe_stop_streaming(struct vb2_queue *vq) unsigned long flags; if (!vb2_is_streaming(vq)) - return 0; + return; /* release all active buffers */ spin_lock_irqsave(&disp->dma_queue_lock, flags); @@ -398,7 +407,6 @@ static int vpbe_stop_streaming(struct vb2_queue *vq) vb2_buffer_done(&layer->next_frm->vb, VB2_BUF_STATE_ERROR); } spin_unlock_irqrestore(&disp->dma_queue_lock, flags); - return 0; } static struct vb2_ops video_qops = { @@ -680,29 +688,6 @@ static int vpbe_try_format(struct vpbe_display *disp_dev, return 0; } -static int vpbe_display_g_priority(struct file *file, void *priv, - enum v4l2_priority *p) -{ - struct vpbe_fh *fh = file->private_data; - struct vpbe_layer *layer = fh->layer; - - *p = v4l2_prio_max(&layer->prio); - - return 0; -} - -static int vpbe_display_s_priority(struct file *file, void *priv, - enum v4l2_priority p) -{ - struct vpbe_fh *fh = file->private_data; - struct vpbe_layer *layer = fh->layer; - int ret; - - ret = v4l2_prio_change(&layer->prio, &fh->prio, p); - - return ret; -} - static int vpbe_display_querycap(struct file *file, void *priv, struct v4l2_capability *cap) { @@ -1492,6 +1477,7 @@ static int vpbe_display_open(struct file *file) { struct vpbe_fh *fh = NULL; struct vpbe_layer *layer = video_drvdata(file); + struct video_device *vdev = video_devdata(file); struct vpbe_display *disp_dev = layer->disp_dev; struct vpbe_device *vpbe_dev = disp_dev->vpbe_dev; struct osd_state *osd_device = disp_dev->osd_device; @@ -1504,6 +1490,7 @@ static int vpbe_display_open(struct file *file) "unable to allocate memory for file handle object\n"); return -ENOMEM; } + v4l2_fh_init(&fh->fh, vdev); v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "vpbe display open plane = %d\n", layer->device_id); @@ -1532,9 +1519,7 @@ static int vpbe_display_open(struct file *file) layer->usrs++; /* Set io_allowed member to false */ fh->io_allowed = 0; - /* Initialize priority of this instance to default priority */ - fh->prio = V4L2_PRIORITY_UNSET; - v4l2_prio_open(&layer->prio, &fh->prio); + v4l2_fh_add(&fh->fh); v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "vpbe display device opened successfully\n"); return 0; @@ -1589,8 +1574,9 @@ static int vpbe_display_release(struct file *file) osd_device->ops.release_layer(osd_device, layer->layer_info.id); } - /* Close the priority */ - v4l2_prio_close(&layer->prio, fh->prio); + + v4l2_fh_del(&fh->fh); + v4l2_fh_exit(&fh->fh); file->private_data = NULL; mutex_unlock(&layer->opslock); @@ -1618,8 +1604,6 @@ static const struct v4l2_ioctl_ops vpbe_ioctl_ops = { .vidioc_cropcap = vpbe_display_cropcap, .vidioc_g_crop = vpbe_display_g_crop, .vidioc_s_crop = vpbe_display_s_crop, - .vidioc_g_priority = vpbe_display_g_priority, - .vidioc_s_priority = vpbe_display_s_priority, .vidioc_s_std = vpbe_display_s_std, .vidioc_g_std = vpbe_display_g_std, .vidioc_enum_output = vpbe_display_enum_output, @@ -1699,8 +1683,6 @@ static int init_vpbe_layer(int i, struct vpbe_display *disp_dev, vpbe_display_layer->layer_info.id = ((i == VPBE_DISPLAY_DEVICE_0) ? WIN_VID0 : WIN_VID1); - /* Initialize prio member of layer object */ - v4l2_prio_init(&vpbe_display_layer->prio); return 0; } @@ -1727,6 +1709,7 @@ static int register_device(struct vpbe_layer *vpbe_display_layer, vpbe_display_layer->disp_dev = disp_dev; /* set the driver data in platform device */ platform_set_drvdata(pdev, disp_dev); + set_bit(V4L2_FL_USE_FH_PRIO, &vpbe_display_layer->video_dev.flags); video_set_drvdata(&vpbe_display_layer->video_dev, vpbe_display_layer); diff --git a/drivers/media/platform/davinci/vpfe_capture.c b/drivers/media/platform/davinci/vpfe_capture.c index 0379cb9f9a9c..a51bda2fb637 100644 --- a/drivers/media/platform/davinci/vpfe_capture.c +++ b/drivers/media/platform/davinci/vpfe_capture.c @@ -498,6 +498,7 @@ unlock: static int vpfe_open(struct file *file) { struct vpfe_device *vpfe_dev = video_drvdata(file); + struct video_device *vdev = video_devdata(file); struct vpfe_fh *fh; v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_open\n"); @@ -517,6 +518,7 @@ static int vpfe_open(struct file *file) /* store pointer to fh in private_data member of file */ file->private_data = fh; fh->vpfe_dev = vpfe_dev; + v4l2_fh_init(&fh->fh, vdev); mutex_lock(&vpfe_dev->lock); /* If decoder is not initialized. initialize it */ if (!vpfe_dev->initialized) { @@ -529,9 +531,7 @@ static int vpfe_open(struct file *file) vpfe_dev->usrs++; /* Set io_allowed member to false */ fh->io_allowed = 0; - /* Initialize priority of this instance to default priority */ - fh->prio = V4L2_PRIORITY_UNSET; - v4l2_prio_open(&vpfe_dev->prio, &fh->prio); + v4l2_fh_add(&fh->fh); mutex_unlock(&vpfe_dev->lock); return 0; } @@ -740,8 +740,8 @@ static int vpfe_release(struct file *file) /* Decrement device usrs counter */ vpfe_dev->usrs--; - /* Close the priority */ - v4l2_prio_close(&vpfe_dev->prio, fh->prio); + v4l2_fh_del(&fh->fh); + v4l2_fh_exit(&fh->fh); /* If this is the last file handle */ if (!vpfe_dev->usrs) { vpfe_dev->initialized = 0; @@ -1217,7 +1217,7 @@ static int vpfe_s_std(struct file *file, void *priv, v4l2_std_id std_id) } ret = v4l2_device_call_until_err(&vpfe_dev->v4l2_dev, sdinfo->grp_id, - core, s_std, std_id); + video, s_std, std_id); if (ret < 0) { v4l2_err(&vpfe_dev->v4l2_dev, "Failed to set standard\n"); goto unlock_out; @@ -1910,14 +1910,13 @@ static int vpfe_probe(struct platform_device *pdev) /* Initialize field of the device objects */ vpfe_dev->numbuffers = config_params.numbuffers; - /* Initialize prio member of device object */ - v4l2_prio_init(&vpfe_dev->prio); /* register video device */ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "trying to register vpfe device.\n"); v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "video_dev=%x\n", (int)&vpfe_dev->video_dev); vpfe_dev->fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + set_bit(V4L2_FL_USE_FH_PRIO, &vpfe_dev->video_dev->flags); ret = video_register_device(vpfe_dev->video_dev, VFL_TYPE_GRABBER, -1); diff --git a/drivers/media/platform/davinci/vpif_capture.c b/drivers/media/platform/davinci/vpif_capture.c index 8dea0b84a3ad..a7ed16497903 100644 --- a/drivers/media/platform/davinci/vpif_capture.c +++ b/drivers/media/platform/davinci/vpif_capture.c @@ -1,5 +1,6 @@ /* * Copyright (C) 2009 Texas Instruments Inc + * Copyright (C) 2014 Lad, Prabhakar <prabhakar.csengg@gmail.com> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -65,14 +66,26 @@ static struct vpif_config_params config_params = { .channel_bufsize[1] = 720 * 576 * 2, }; +#define VPIF_DRIVER_NAME "vpif_capture" + /* global variables */ static struct vpif_device vpif_obj = { {NULL} }; static struct device *vpif_dev; static void vpif_calculate_offsets(struct channel_obj *ch); static void vpif_config_addr(struct channel_obj *ch, int muxmode); +static u8 channel_first_int[VPIF_NUMBER_OF_OBJECTS][2] = { {1, 1} }; + +/* Is set to 1 in case of SDTV formats, 2 in case of HDTV formats. */ +static int ycmux_mode; + +static inline struct vpif_cap_buffer *to_vpif_buffer(struct vb2_buffer *vb) +{ + return container_of(vb, struct vpif_cap_buffer, vb); +} + /** - * buffer_prepare : callback function for buffer prepare + * vpif_buffer_prepare : callback function for buffer prepare * @vb: ptr to vb2_buffer * * This is the callback function for buffer prepare when vb2_qbuf() @@ -81,10 +94,8 @@ static void vpif_config_addr(struct channel_obj *ch, int muxmode); */ static int vpif_buffer_prepare(struct vb2_buffer *vb) { - /* Get the file handle object and channel object */ - struct vpif_fh *fh = vb2_get_drv_priv(vb->vb2_queue); struct vb2_queue *q = vb->vb2_queue; - struct channel_obj *ch = fh->channel; + struct channel_obj *ch = vb2_get_drv_priv(q); struct common_obj *common; unsigned long addr; @@ -92,26 +103,22 @@ static int vpif_buffer_prepare(struct vb2_buffer *vb) common = &ch->common[VPIF_VIDEO_INDEX]; - if (vb->state != VB2_BUF_STATE_ACTIVE && - vb->state != VB2_BUF_STATE_PREPARED) { - vb2_set_plane_payload(vb, 0, common->fmt.fmt.pix.sizeimage); - if (vb2_plane_vaddr(vb, 0) && - vb2_get_plane_payload(vb, 0) > vb2_plane_size(vb, 0)) - goto exit; - addr = vb2_dma_contig_plane_dma_addr(vb, 0); + vb2_set_plane_payload(vb, 0, common->fmt.fmt.pix.sizeimage); + if (vb2_get_plane_payload(vb, 0) > vb2_plane_size(vb, 0)) + return -EINVAL; - if (q->streaming) { - if (!IS_ALIGNED((addr + common->ytop_off), 8) || - !IS_ALIGNED((addr + common->ybtm_off), 8) || - !IS_ALIGNED((addr + common->ctop_off), 8) || - !IS_ALIGNED((addr + common->cbtm_off), 8)) - goto exit; - } + vb->v4l2_buf.field = common->fmt.fmt.pix.field; + + addr = vb2_dma_contig_plane_dma_addr(vb, 0); + if (!IS_ALIGNED((addr + common->ytop_off), 8) || + !IS_ALIGNED((addr + common->ybtm_off), 8) || + !IS_ALIGNED((addr + common->ctop_off), 8) || + !IS_ALIGNED((addr + common->cbtm_off), 8)) { + vpif_dbg(1, debug, "offset is not aligned\n"); + return -EINVAL; } + return 0; -exit: - vpif_dbg(1, debug, "buffer_prepare:offset is not aligned to 8 bytes\n"); - return -EINVAL; } /** @@ -131,49 +138,26 @@ static int vpif_buffer_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers, unsigned int *nplanes, unsigned int sizes[], void *alloc_ctxs[]) { - /* Get the file handle object and channel object */ - struct vpif_fh *fh = vb2_get_drv_priv(vq); - struct channel_obj *ch = fh->channel; + struct channel_obj *ch = vb2_get_drv_priv(vq); struct common_obj *common; - unsigned long size; common = &ch->common[VPIF_VIDEO_INDEX]; vpif_dbg(2, debug, "vpif_buffer_setup\n"); - /* If memory type is not mmap, return */ - if (V4L2_MEMORY_MMAP == common->memory) { - /* Calculate the size of the buffer */ - size = config_params.channel_bufsize[ch->channel_id]; - /* - * Checking if the buffer size exceeds the available buffer - * ycmux_mode = 0 means 1 channel mode HD and - * ycmux_mode = 1 means 2 channels mode SD - */ - if (ch->vpifparams.std_info.ycmux_mode == 0) { - if (config_params.video_limit[ch->channel_id]) - while (size * *nbuffers > - (config_params.video_limit[0] - + config_params.video_limit[1])) - (*nbuffers)--; - } else { - if (config_params.video_limit[ch->channel_id]) - while (size * *nbuffers > - config_params.video_limit[ch->channel_id]) - (*nbuffers)--; - } - - } else { - size = common->fmt.fmt.pix.sizeimage; - } + if (fmt && fmt->fmt.pix.sizeimage < common->fmt.fmt.pix.sizeimage) + return -EINVAL; - if (*nbuffers < config_params.min_numbuffers) - *nbuffers = config_params.min_numbuffers; + if (vq->num_buffers + *nbuffers < 3) + *nbuffers = 3 - vq->num_buffers; *nplanes = 1; - sizes[0] = size; + sizes[0] = fmt ? fmt->fmt.pix.sizeimage : common->fmt.fmt.pix.sizeimage; alloc_ctxs[0] = common->alloc_ctx; + /* Calculate the offset for Y and C data in the buffer */ + vpif_calculate_offsets(ch); + return 0; } @@ -183,11 +167,8 @@ static int vpif_buffer_queue_setup(struct vb2_queue *vq, */ static void vpif_buffer_queue(struct vb2_buffer *vb) { - /* Get the file handle object and channel object */ - struct vpif_fh *fh = vb2_get_drv_priv(vb->vb2_queue); - struct channel_obj *ch = fh->channel; - struct vpif_cap_buffer *buf = container_of(vb, - struct vpif_cap_buffer, vb); + struct channel_obj *ch = vb2_get_drv_priv(vb->vb2_queue); + struct vpif_cap_buffer *buf = to_vpif_buffer(vb); struct common_obj *common; unsigned long flags; @@ -202,102 +183,25 @@ static void vpif_buffer_queue(struct vb2_buffer *vb) } /** - * vpif_buf_cleanup : Callback function to free buffer + * vpif_start_streaming : Starts the DMA engine for streaming * @vb: ptr to vb2_buffer - * - * This function is called from the videobuf2 layer to free memory - * allocated to the buffers + * @count: number of buffers */ -static void vpif_buf_cleanup(struct vb2_buffer *vb) -{ - /* Get the file handle object and channel object */ - struct vpif_fh *fh = vb2_get_drv_priv(vb->vb2_queue); - struct vpif_cap_buffer *buf = container_of(vb, - struct vpif_cap_buffer, vb); - struct channel_obj *ch = fh->channel; - struct common_obj *common; - unsigned long flags; - - common = &ch->common[VPIF_VIDEO_INDEX]; - - spin_lock_irqsave(&common->irqlock, flags); - if (vb->state == VB2_BUF_STATE_ACTIVE) - list_del_init(&buf->list); - spin_unlock_irqrestore(&common->irqlock, flags); - -} - -static void vpif_wait_prepare(struct vb2_queue *vq) -{ - struct vpif_fh *fh = vb2_get_drv_priv(vq); - struct channel_obj *ch = fh->channel; - struct common_obj *common; - - common = &ch->common[VPIF_VIDEO_INDEX]; - mutex_unlock(&common->lock); -} - -static void vpif_wait_finish(struct vb2_queue *vq) -{ - struct vpif_fh *fh = vb2_get_drv_priv(vq); - struct channel_obj *ch = fh->channel; - struct common_obj *common; - - common = &ch->common[VPIF_VIDEO_INDEX]; - mutex_lock(&common->lock); -} - -static int vpif_buffer_init(struct vb2_buffer *vb) -{ - struct vpif_cap_buffer *buf = container_of(vb, - struct vpif_cap_buffer, vb); - - INIT_LIST_HEAD(&buf->list); - - return 0; -} - -static u8 channel_first_int[VPIF_NUMBER_OF_OBJECTS][2] = - { {1, 1} }; - static int vpif_start_streaming(struct vb2_queue *vq, unsigned int count) { struct vpif_capture_config *vpif_config_data = vpif_dev->platform_data; - struct vpif_fh *fh = vb2_get_drv_priv(vq); - struct channel_obj *ch = fh->channel; + struct channel_obj *ch = vb2_get_drv_priv(vq); struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; struct vpif_params *vpif = &ch->vpifparams; - unsigned long addr = 0; - unsigned long flags; + struct vpif_cap_buffer *buf, *tmp; + unsigned long addr, flags; int ret; spin_lock_irqsave(&common->irqlock, flags); - /* Get the next frame from the buffer queue */ - common->cur_frm = common->next_frm = list_entry(common->dma_queue.next, - struct vpif_cap_buffer, list); - /* Remove buffer from the buffer queue */ - list_del(&common->cur_frm->list); - spin_unlock_irqrestore(&common->irqlock, flags); - /* Mark state of the current frame to active */ - common->cur_frm->vb.state = VB2_BUF_STATE_ACTIVE; - /* Initialize field_id and started member */ + /* Initialize field_id */ ch->field_id = 0; - common->started = 1; - addr = vb2_dma_contig_plane_dma_addr(&common->cur_frm->vb, 0); - - /* Calculate the offset for Y and C data in the buffer */ - vpif_calculate_offsets(ch); - - if ((vpif->std_info.frm_fmt && - ((common->fmt.fmt.pix.field != V4L2_FIELD_NONE) && - (common->fmt.fmt.pix.field != V4L2_FIELD_ANY))) || - (!vpif->std_info.frm_fmt && - (common->fmt.fmt.pix.field == V4L2_FIELD_NONE))) { - vpif_dbg(1, debug, "conflict in field format and std format\n"); - return -EINVAL; - } /* configure 1 or 2 channel mode */ if (vpif_config_data->setup_input_channel_mode) { @@ -305,21 +209,37 @@ static int vpif_start_streaming(struct vb2_queue *vq, unsigned int count) setup_input_channel_mode(vpif->std_info.ycmux_mode); if (ret < 0) { vpif_dbg(1, debug, "can't set vpif channel mode\n"); - return ret; + goto err; } } + ret = v4l2_subdev_call(ch->sd, video, s_stream, 1); + if (ret && ret != -ENOIOCTLCMD && ret != -ENODEV) { + vpif_dbg(1, debug, "stream on failed in subdev\n"); + goto err; + } + /* Call vpif_set_params function to set the parameters and addresses */ ret = vpif_set_video_params(vpif, ch->channel_id); - if (ret < 0) { vpif_dbg(1, debug, "can't set video params\n"); - return ret; + goto err; } - common->started = ret; + ycmux_mode = ret; vpif_config_addr(ch, ret); + /* Get the next frame from the buffer queue */ + common->cur_frm = common->next_frm = list_entry(common->dma_queue.next, + struct vpif_cap_buffer, list); + /* Remove buffer from the buffer queue */ + list_del(&common->cur_frm->list); + spin_unlock_irqrestore(&common->irqlock, flags); + /* Mark state of the current frame to active */ + common->cur_frm->vb.state = VB2_BUF_STATE_ACTIVE; + + addr = vb2_dma_contig_plane_dma_addr(&common->cur_frm->vb, 0); + common->set_addr(addr + common->ytop_off, addr + common->ybtm_off, addr + common->ctop_off, @@ -330,31 +250,42 @@ static int vpif_start_streaming(struct vb2_queue *vq, unsigned int count) * VPIF register */ channel_first_int[VPIF_VIDEO_INDEX][ch->channel_id] = 1; - if ((VPIF_CHANNEL0_VIDEO == ch->channel_id)) { + if (VPIF_CHANNEL0_VIDEO == ch->channel_id) { channel0_intr_assert(); channel0_intr_enable(1); enable_channel0(1); } - if ((VPIF_CHANNEL1_VIDEO == ch->channel_id) || - (common->started == 2)) { + if (VPIF_CHANNEL1_VIDEO == ch->channel_id || + ycmux_mode == 2) { channel1_intr_assert(); channel1_intr_enable(1); enable_channel1(1); } return 0; + +err: + list_for_each_entry_safe(buf, tmp, &common->dma_queue, list) { + list_del(&buf->list); + vb2_buffer_done(&buf->vb, VB2_BUF_STATE_QUEUED); + } + + return ret; } -/* abort streaming and wait for last buffer */ -static int vpif_stop_streaming(struct vb2_queue *vq) +/** + * vpif_stop_streaming : Stop the DMA engine + * @vq: ptr to vb2_queue + * + * This callback stops the DMA engine and any remaining buffers + * in the DMA queue are released. + */ +static void vpif_stop_streaming(struct vb2_queue *vq) { - struct vpif_fh *fh = vb2_get_drv_priv(vq); - struct channel_obj *ch = fh->channel; + struct channel_obj *ch = vb2_get_drv_priv(vq); struct common_obj *common; unsigned long flags; - - if (!vb2_is_streaming(vq)) - return 0; + int ret; common = &ch->common[VPIF_VIDEO_INDEX]; @@ -363,12 +294,17 @@ static int vpif_stop_streaming(struct vb2_queue *vq) enable_channel0(0); channel0_intr_enable(0); } - if ((VPIF_CHANNEL1_VIDEO == ch->channel_id) || - (2 == common->started)) { + if (VPIF_CHANNEL1_VIDEO == ch->channel_id || + ycmux_mode == 2) { enable_channel1(0); channel1_intr_enable(0); } - common->started = 0; + + ycmux_mode = 0; + + ret = v4l2_subdev_call(ch->sd, video, s_stream, 0); + if (ret && ret != -ENOIOCTLCMD && ret != -ENODEV) + vpif_dbg(1, debug, "stream off failed in subdev\n"); /* release all active buffers */ spin_lock_irqsave(&common->irqlock, flags); @@ -390,19 +326,13 @@ static int vpif_stop_streaming(struct vb2_queue *vq) vb2_buffer_done(&common->next_frm->vb, VB2_BUF_STATE_ERROR); } spin_unlock_irqrestore(&common->irqlock, flags); - - return 0; } static struct vb2_ops video_qops = { .queue_setup = vpif_buffer_queue_setup, - .wait_prepare = vpif_wait_prepare, - .wait_finish = vpif_wait_finish, - .buf_init = vpif_buffer_init, .buf_prepare = vpif_buffer_prepare, .start_streaming = vpif_start_streaming, .stop_streaming = vpif_stop_streaming, - .buf_cleanup = vpif_buf_cleanup, .buf_queue = vpif_buffer_queue, }; @@ -479,9 +409,6 @@ static irqreturn_t vpif_channel_isr(int irq, void *dev_id) for (i = 0; i < VPIF_NUMBER_OF_OBJECTS; i++) { common = &ch->common[i]; /* skip If streaming is not started in this channel */ - if (0 == common->started) - continue; - /* Check the field format */ if (1 == ch->vpifparams.std_info.frm_fmt) { /* Progressive mode */ @@ -683,11 +610,6 @@ static void vpif_config_format(struct channel_obj *ch) vpif_dbg(2, debug, "vpif_config_format\n"); common->fmt.fmt.pix.field = V4L2_FIELD_ANY; - if (config_params.numbuffers[ch->channel_id] == 0) - common->memory = V4L2_MEMORY_USERPTR; - else - common->memory = V4L2_MEMORY_MMAP; - common->fmt.fmt.pix.sizeimage = config_params.channel_bufsize[ch->channel_id]; @@ -837,415 +759,6 @@ static void vpif_config_addr(struct channel_obj *ch, int muxmode) } /** - * vpif_mmap : It is used to map kernel space buffers into user spaces - * @filep: file pointer - * @vma: ptr to vm_area_struct - */ -static int vpif_mmap(struct file *filep, struct vm_area_struct *vma) -{ - /* Get the channel object and file handle object */ - struct vpif_fh *fh = filep->private_data; - struct channel_obj *ch = fh->channel; - struct common_obj *common = &(ch->common[VPIF_VIDEO_INDEX]); - int ret; - - vpif_dbg(2, debug, "vpif_mmap\n"); - - if (mutex_lock_interruptible(&common->lock)) - return -ERESTARTSYS; - ret = vb2_mmap(&common->buffer_queue, vma); - mutex_unlock(&common->lock); - return ret; -} - -/** - * vpif_poll: It is used for select/poll system call - * @filep: file pointer - * @wait: poll table to wait - */ -static unsigned int vpif_poll(struct file *filep, poll_table * wait) -{ - struct vpif_fh *fh = filep->private_data; - struct channel_obj *channel = fh->channel; - struct common_obj *common = &(channel->common[VPIF_VIDEO_INDEX]); - unsigned int res = 0; - - vpif_dbg(2, debug, "vpif_poll\n"); - - if (common->started) { - mutex_lock(&common->lock); - res = vb2_poll(&common->buffer_queue, filep, wait); - mutex_unlock(&common->lock); - } - return res; -} - -/** - * vpif_open : vpif open handler - * @filep: file ptr - * - * It creates object of file handle structure and stores it in private_data - * member of filepointer - */ -static int vpif_open(struct file *filep) -{ - struct video_device *vdev = video_devdata(filep); - struct common_obj *common; - struct video_obj *vid_ch; - struct channel_obj *ch; - struct vpif_fh *fh; - - vpif_dbg(2, debug, "vpif_open\n"); - - ch = video_get_drvdata(vdev); - - vid_ch = &ch->video; - common = &ch->common[VPIF_VIDEO_INDEX]; - - /* Allocate memory for the file handle object */ - fh = kzalloc(sizeof(struct vpif_fh), GFP_KERNEL); - if (NULL == fh) { - vpif_err("unable to allocate memory for file handle object\n"); - return -ENOMEM; - } - - if (mutex_lock_interruptible(&common->lock)) { - kfree(fh); - return -ERESTARTSYS; - } - /* store pointer to fh in private_data member of filep */ - filep->private_data = fh; - fh->channel = ch; - fh->initialized = 0; - /* If decoder is not initialized. initialize it */ - if (!ch->initialized) { - fh->initialized = 1; - ch->initialized = 1; - memset(&(ch->vpifparams), 0, sizeof(struct vpif_params)); - } - /* Increment channel usrs counter */ - ch->usrs++; - /* Set io_allowed member to false */ - fh->io_allowed[VPIF_VIDEO_INDEX] = 0; - /* Initialize priority of this instance to default priority */ - fh->prio = V4L2_PRIORITY_UNSET; - v4l2_prio_open(&ch->prio, &fh->prio); - mutex_unlock(&common->lock); - return 0; -} - -/** - * vpif_release : function to clean up file close - * @filep: file pointer - * - * This function deletes buffer queue, frees the buffers and the vpif file - * handle - */ -static int vpif_release(struct file *filep) -{ - struct vpif_fh *fh = filep->private_data; - struct channel_obj *ch = fh->channel; - struct common_obj *common; - - vpif_dbg(2, debug, "vpif_release\n"); - - common = &ch->common[VPIF_VIDEO_INDEX]; - - mutex_lock(&common->lock); - /* if this instance is doing IO */ - if (fh->io_allowed[VPIF_VIDEO_INDEX]) { - /* Reset io_usrs member of channel object */ - common->io_usrs = 0; - /* Free buffers allocated */ - vb2_queue_release(&common->buffer_queue); - vb2_dma_contig_cleanup_ctx(common->alloc_ctx); - } - - /* Decrement channel usrs counter */ - ch->usrs--; - - /* Close the priority */ - v4l2_prio_close(&ch->prio, fh->prio); - - if (fh->initialized) - ch->initialized = 0; - - mutex_unlock(&common->lock); - filep->private_data = NULL; - kfree(fh); - return 0; -} - -/** - * vpif_reqbufs() - request buffer handler - * @file: file ptr - * @priv: file handle - * @reqbuf: request buffer structure ptr - */ -static int vpif_reqbufs(struct file *file, void *priv, - struct v4l2_requestbuffers *reqbuf) -{ - struct vpif_fh *fh = priv; - struct channel_obj *ch = fh->channel; - struct common_obj *common; - u8 index = 0; - struct vb2_queue *q; - int ret; - - vpif_dbg(2, debug, "vpif_reqbufs\n"); - - /** - * This file handle has not initialized the channel, - * It is not allowed to do settings - */ - if ((VPIF_CHANNEL0_VIDEO == ch->channel_id) - || (VPIF_CHANNEL1_VIDEO == ch->channel_id)) { - if (!fh->initialized) { - vpif_dbg(1, debug, "Channel Busy\n"); - return -EBUSY; - } - } - - if (V4L2_BUF_TYPE_VIDEO_CAPTURE != reqbuf->type || !vpif_dev) - return -EINVAL; - - index = VPIF_VIDEO_INDEX; - - common = &ch->common[index]; - - if (0 != common->io_usrs) - return -EBUSY; - - /* Initialize videobuf2 queue as per the buffer type */ - common->alloc_ctx = vb2_dma_contig_init_ctx(vpif_dev); - if (IS_ERR(common->alloc_ctx)) { - vpif_err("Failed to get the context\n"); - return PTR_ERR(common->alloc_ctx); - } - q = &common->buffer_queue; - q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - q->io_modes = VB2_MMAP | VB2_USERPTR; - q->drv_priv = fh; - q->ops = &video_qops; - q->mem_ops = &vb2_dma_contig_memops; - q->buf_struct_size = sizeof(struct vpif_cap_buffer); - q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; - q->min_buffers_needed = 1; - - ret = vb2_queue_init(q); - if (ret) { - vpif_err("vpif_capture: vb2_queue_init() failed\n"); - vb2_dma_contig_cleanup_ctx(common->alloc_ctx); - return ret; - } - /* Set io allowed member of file handle to TRUE */ - fh->io_allowed[index] = 1; - /* Increment io usrs member of channel object to 1 */ - common->io_usrs = 1; - /* Store type of memory requested in channel object */ - common->memory = reqbuf->memory; - INIT_LIST_HEAD(&common->dma_queue); - - /* Allocate buffers */ - return vb2_reqbufs(&common->buffer_queue, reqbuf); -} - -/** - * vpif_querybuf() - query buffer handler - * @file: file ptr - * @priv: file handle - * @buf: v4l2 buffer structure ptr - */ -static int vpif_querybuf(struct file *file, void *priv, - struct v4l2_buffer *buf) -{ - struct vpif_fh *fh = priv; - struct channel_obj *ch = fh->channel; - struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; - - vpif_dbg(2, debug, "vpif_querybuf\n"); - - if (common->fmt.type != buf->type) - return -EINVAL; - - if (common->memory != V4L2_MEMORY_MMAP) { - vpif_dbg(1, debug, "Invalid memory\n"); - return -EINVAL; - } - - return vb2_querybuf(&common->buffer_queue, buf); -} - -/** - * vpif_qbuf() - query buffer handler - * @file: file ptr - * @priv: file handle - * @buf: v4l2 buffer structure ptr - */ -static int vpif_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf) -{ - - struct vpif_fh *fh = priv; - struct channel_obj *ch = fh->channel; - struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; - struct v4l2_buffer tbuf = *buf; - - vpif_dbg(2, debug, "vpif_qbuf\n"); - - if (common->fmt.type != tbuf.type) { - vpif_err("invalid buffer type\n"); - return -EINVAL; - } - - if (!fh->io_allowed[VPIF_VIDEO_INDEX]) { - vpif_err("fh io not allowed\n"); - return -EACCES; - } - - return vb2_qbuf(&common->buffer_queue, buf); -} - -/** - * vpif_dqbuf() - query buffer handler - * @file: file ptr - * @priv: file handle - * @buf: v4l2 buffer structure ptr - */ -static int vpif_dqbuf(struct file *file, void *priv, struct v4l2_buffer *buf) -{ - struct vpif_fh *fh = priv; - struct channel_obj *ch = fh->channel; - struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; - - vpif_dbg(2, debug, "vpif_dqbuf\n"); - - return vb2_dqbuf(&common->buffer_queue, buf, - (file->f_flags & O_NONBLOCK)); -} - -/** - * vpif_streamon() - streamon handler - * @file: file ptr - * @priv: file handle - * @buftype: v4l2 buffer type - */ -static int vpif_streamon(struct file *file, void *priv, - enum v4l2_buf_type buftype) -{ - - struct vpif_fh *fh = priv; - struct channel_obj *ch = fh->channel; - struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; - struct channel_obj *oth_ch = vpif_obj.dev[!ch->channel_id]; - struct vpif_params *vpif; - int ret = 0; - - vpif_dbg(2, debug, "vpif_streamon\n"); - - vpif = &ch->vpifparams; - - if (buftype != V4L2_BUF_TYPE_VIDEO_CAPTURE) { - vpif_dbg(1, debug, "buffer type not supported\n"); - return -EINVAL; - } - - /* If file handle is not allowed IO, return error */ - if (!fh->io_allowed[VPIF_VIDEO_INDEX]) { - vpif_dbg(1, debug, "io not allowed\n"); - return -EACCES; - } - - /* If Streaming is already started, return error */ - if (common->started) { - vpif_dbg(1, debug, "channel->started\n"); - return -EBUSY; - } - - if ((ch->channel_id == VPIF_CHANNEL0_VIDEO && - oth_ch->common[VPIF_VIDEO_INDEX].started && - vpif->std_info.ycmux_mode == 0) || - ((ch->channel_id == VPIF_CHANNEL1_VIDEO) && - (2 == oth_ch->common[VPIF_VIDEO_INDEX].started))) { - vpif_dbg(1, debug, "other channel is being used\n"); - return -EBUSY; - } - - ret = vpif_check_format(ch, &common->fmt.fmt.pix, 0); - if (ret) - return ret; - - /* Enable streamon on the sub device */ - ret = v4l2_subdev_call(ch->sd, video, s_stream, 1); - - if (ret && ret != -ENOIOCTLCMD && ret != -ENODEV) { - vpif_dbg(1, debug, "stream on failed in subdev\n"); - return ret; - } - - /* Call vb2_streamon to start streaming in videobuf2 */ - ret = vb2_streamon(&common->buffer_queue, buftype); - if (ret) { - vpif_dbg(1, debug, "vb2_streamon\n"); - return ret; - } - - return ret; -} - -/** - * vpif_streamoff() - streamoff handler - * @file: file ptr - * @priv: file handle - * @buftype: v4l2 buffer type - */ -static int vpif_streamoff(struct file *file, void *priv, - enum v4l2_buf_type buftype) -{ - - struct vpif_fh *fh = priv; - struct channel_obj *ch = fh->channel; - struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; - int ret; - - vpif_dbg(2, debug, "vpif_streamoff\n"); - - if (buftype != V4L2_BUF_TYPE_VIDEO_CAPTURE) { - vpif_dbg(1, debug, "buffer type not supported\n"); - return -EINVAL; - } - - /* If io is allowed for this file handle, return error */ - if (!fh->io_allowed[VPIF_VIDEO_INDEX]) { - vpif_dbg(1, debug, "io not allowed\n"); - return -EACCES; - } - - /* If streaming is not started, return error */ - if (!common->started) { - vpif_dbg(1, debug, "channel->started\n"); - return -EINVAL; - } - - /* disable channel */ - if (VPIF_CHANNEL0_VIDEO == ch->channel_id) { - enable_channel0(0); - channel0_intr_enable(0); - } else { - enable_channel1(0); - channel1_intr_enable(0); - } - - common->started = 0; - - ret = v4l2_subdev_call(ch->sd, video, s_stream, 0); - - if (ret && ret != -ENOIOCTLCMD && ret != -ENODEV) - vpif_dbg(1, debug, "stream off failed in subdev\n"); - - return vb2_streamoff(&common->buffer_queue, buftype); -} - -/** * vpif_input_to_subdev() - Maps input to sub device * @vpif_cfg - global config ptr * @chan_cfg - channel config ptr @@ -1348,8 +861,8 @@ static int vpif_set_input( */ static int vpif_querystd(struct file *file, void *priv, v4l2_std_id *std_id) { - struct vpif_fh *fh = priv; - struct channel_obj *ch = fh->channel; + struct video_device *vdev = video_devdata(file); + struct channel_obj *ch = video_get_drvdata(vdev); int ret = 0; vpif_dbg(2, debug, "vpif_querystd\n"); @@ -1375,11 +888,22 @@ static int vpif_querystd(struct file *file, void *priv, v4l2_std_id *std_id) */ static int vpif_g_std(struct file *file, void *priv, v4l2_std_id *std) { - struct vpif_fh *fh = priv; - struct channel_obj *ch = fh->channel; + struct vpif_capture_config *config = vpif_dev->platform_data; + struct video_device *vdev = video_devdata(file); + struct channel_obj *ch = video_get_drvdata(vdev); + struct vpif_capture_chan_config *chan_cfg; + struct v4l2_input input; vpif_dbg(2, debug, "vpif_g_std\n"); + if (config->chan_config[ch->channel_id].inputs == NULL) + return -ENODATA; + + chan_cfg = &config->chan_config[ch->channel_id]; + input = chan_cfg->inputs[ch->input_idx].input; + if (input.capabilities != V4L2_IN_CAP_STD) + return -ENODATA; + *std = ch->video.stdid; return 0; } @@ -1392,31 +916,26 @@ static int vpif_g_std(struct file *file, void *priv, v4l2_std_id *std) */ static int vpif_s_std(struct file *file, void *priv, v4l2_std_id std_id) { - struct vpif_fh *fh = priv; - struct channel_obj *ch = fh->channel; + struct vpif_capture_config *config = vpif_dev->platform_data; + struct video_device *vdev = video_devdata(file); + struct channel_obj *ch = video_get_drvdata(vdev); struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; - int ret = 0; + struct vpif_capture_chan_config *chan_cfg; + struct v4l2_input input; + int ret; vpif_dbg(2, debug, "vpif_s_std\n"); - if (common->started) { - vpif_err("streaming in progress\n"); - return -EBUSY; - } - - if ((VPIF_CHANNEL0_VIDEO == ch->channel_id) || - (VPIF_CHANNEL1_VIDEO == ch->channel_id)) { - if (!fh->initialized) { - vpif_dbg(1, debug, "Channel Busy\n"); - return -EBUSY; - } - } + if (config->chan_config[ch->channel_id].inputs == NULL) + return -ENODATA; - ret = v4l2_prio_check(&ch->prio, fh->prio); - if (0 != ret) - return ret; + chan_cfg = &config->chan_config[ch->channel_id]; + input = chan_cfg->inputs[ch->input_idx].input; + if (input.capabilities != V4L2_IN_CAP_STD) + return -ENODATA; - fh->initialized = 1; + if (vb2_is_busy(&common->buffer_queue)) + return -EBUSY; /* Call encoder subdevice function to set the standard */ ch->video.stdid = std_id; @@ -1432,7 +951,7 @@ static int vpif_s_std(struct file *file, void *priv, v4l2_std_id std_id) vpif_config_format(ch); /* set standard in the sub device */ - ret = v4l2_subdev_call(ch->sd, core, s_std, std_id); + ret = v4l2_subdev_call(ch->sd, video, s_std, std_id); if (ret && ret != -ENOIOCTLCMD && ret != -ENODEV) { vpif_dbg(1, debug, "Failed to set standard for sub devices\n"); return ret; @@ -1451,9 +970,9 @@ static int vpif_enum_input(struct file *file, void *priv, { struct vpif_capture_config *config = vpif_dev->platform_data; + struct video_device *vdev = video_devdata(file); + struct channel_obj *ch = video_get_drvdata(vdev); struct vpif_capture_chan_config *chan_cfg; - struct vpif_fh *fh = priv; - struct channel_obj *ch = fh->channel; chan_cfg = &config->chan_config[ch->channel_id]; @@ -1475,8 +994,8 @@ static int vpif_enum_input(struct file *file, void *priv, */ static int vpif_g_input(struct file *file, void *priv, unsigned int *index) { - struct vpif_fh *fh = priv; - struct channel_obj *ch = fh->channel; + struct video_device *vdev = video_devdata(file); + struct channel_obj *ch = video_get_drvdata(vdev); *index = ch->input_idx; return 0; @@ -1491,35 +1010,19 @@ static int vpif_g_input(struct file *file, void *priv, unsigned int *index) static int vpif_s_input(struct file *file, void *priv, unsigned int index) { struct vpif_capture_config *config = vpif_dev->platform_data; - struct vpif_capture_chan_config *chan_cfg; - struct vpif_fh *fh = priv; - struct channel_obj *ch = fh->channel; + struct video_device *vdev = video_devdata(file); + struct channel_obj *ch = video_get_drvdata(vdev); struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; - int ret; + struct vpif_capture_chan_config *chan_cfg; chan_cfg = &config->chan_config[ch->channel_id]; if (index >= chan_cfg->input_count) return -EINVAL; - if (common->started) { - vpif_err("Streaming in progress\n"); + if (vb2_is_busy(&common->buffer_queue)) return -EBUSY; - } - - if ((VPIF_CHANNEL0_VIDEO == ch->channel_id) || - (VPIF_CHANNEL1_VIDEO == ch->channel_id)) { - if (!fh->initialized) { - vpif_dbg(1, debug, "Channel Busy\n"); - return -EBUSY; - } - } - - ret = v4l2_prio_check(&ch->prio, fh->prio); - if (0 != ret) - return ret; - fh->initialized = 1; return vpif_set_input(config, ch, index); } @@ -1532,8 +1035,8 @@ static int vpif_s_input(struct file *file, void *priv, unsigned int index) static int vpif_enum_fmt_vid_cap(struct file *file, void *priv, struct v4l2_fmtdesc *fmt) { - struct vpif_fh *fh = priv; - struct channel_obj *ch = fh->channel; + struct video_device *vdev = video_devdata(file); + struct channel_obj *ch = video_get_drvdata(vdev); if (fmt->index != 0) { vpif_dbg(1, debug, "Invalid format index\n"); @@ -1562,8 +1065,8 @@ static int vpif_enum_fmt_vid_cap(struct file *file, void *priv, static int vpif_try_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *fmt) { - struct vpif_fh *fh = priv; - struct channel_obj *ch = fh->channel; + struct video_device *vdev = video_devdata(file); + struct channel_obj *ch = video_get_drvdata(vdev); struct v4l2_pix_format *pixfmt = &fmt->fmt.pix; return vpif_check_format(ch, pixfmt, 1); @@ -1579,8 +1082,8 @@ static int vpif_try_fmt_vid_cap(struct file *file, void *priv, static int vpif_g_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *fmt) { - struct vpif_fh *fh = priv; - struct channel_obj *ch = fh->channel; + struct video_device *vdev = video_devdata(file); + struct channel_obj *ch = video_get_drvdata(vdev); struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; /* Check the validity of the buffer type */ @@ -1601,33 +1104,16 @@ static int vpif_g_fmt_vid_cap(struct file *file, void *priv, static int vpif_s_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *fmt) { - struct vpif_fh *fh = priv; - struct channel_obj *ch = fh->channel; + struct video_device *vdev = video_devdata(file); + struct channel_obj *ch = video_get_drvdata(vdev); struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; struct v4l2_pix_format *pixfmt; int ret = 0; vpif_dbg(2, debug, "%s\n", __func__); - /* If streaming is started, return error */ - if (common->started) { - vpif_dbg(1, debug, "Streaming is started\n"); + if (vb2_is_busy(&common->buffer_queue)) return -EBUSY; - } - - if ((VPIF_CHANNEL0_VIDEO == ch->channel_id) || - (VPIF_CHANNEL1_VIDEO == ch->channel_id)) { - if (!fh->initialized) { - vpif_dbg(1, debug, "Channel Busy\n"); - return -EBUSY; - } - } - - ret = v4l2_prio_check(&ch->prio, fh->prio); - if (0 != ret) - return ret; - - fh->initialized = 1; pixfmt = &fmt->fmt.pix; /* Check for valid field format */ @@ -1653,7 +1139,7 @@ static int vpif_querycap(struct file *file, void *priv, cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; - snprintf(cap->driver, sizeof(cap->driver), "%s", dev_name(vpif_dev)); + strlcpy(cap->driver, VPIF_DRIVER_NAME, sizeof(cap->driver)); snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s", dev_name(vpif_dev)); strlcpy(cap->card, config->card_name, sizeof(cap->card)); @@ -1662,61 +1148,6 @@ static int vpif_querycap(struct file *file, void *priv, } /** - * vpif_g_priority() - get priority handler - * @file: file ptr - * @priv: file handle - * @prio: ptr to v4l2_priority structure - */ -static int vpif_g_priority(struct file *file, void *priv, - enum v4l2_priority *prio) -{ - struct vpif_fh *fh = priv; - struct channel_obj *ch = fh->channel; - - *prio = v4l2_prio_max(&ch->prio); - - return 0; -} - -/** - * vpif_s_priority() - set priority handler - * @file: file ptr - * @priv: file handle - * @prio: ptr to v4l2_priority structure - */ -static int vpif_s_priority(struct file *file, void *priv, enum v4l2_priority p) -{ - struct vpif_fh *fh = priv; - struct channel_obj *ch = fh->channel; - - return v4l2_prio_change(&ch->prio, &fh->prio, p); -} - -/** - * vpif_cropcap() - cropcap handler - * @file: file ptr - * @priv: file handle - * @crop: ptr to v4l2_cropcap structure - */ -static int vpif_cropcap(struct file *file, void *priv, - struct v4l2_cropcap *crop) -{ - struct vpif_fh *fh = priv; - struct channel_obj *ch = fh->channel; - struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; - - if (V4L2_BUF_TYPE_VIDEO_CAPTURE != crop->type) - return -EINVAL; - - crop->bounds.left = 0; - crop->bounds.top = 0; - crop->bounds.height = common->height; - crop->bounds.width = common->width; - crop->defrect = crop->bounds; - return 0; -} - -/** * vpif_enum_dv_timings() - ENUM_DV_TIMINGS handler * @file: file ptr * @priv: file handle @@ -1726,13 +1157,27 @@ static int vpif_enum_dv_timings(struct file *file, void *priv, struct v4l2_enum_dv_timings *timings) { - struct vpif_fh *fh = priv; - struct channel_obj *ch = fh->channel; + struct vpif_capture_config *config = vpif_dev->platform_data; + struct video_device *vdev = video_devdata(file); + struct channel_obj *ch = video_get_drvdata(vdev); + struct vpif_capture_chan_config *chan_cfg; + struct v4l2_input input; int ret; - ret = v4l2_subdev_call(ch->sd, video, enum_dv_timings, timings); + if (config->chan_config[ch->channel_id].inputs == NULL) + return -ENODATA; + + chan_cfg = &config->chan_config[ch->channel_id]; + input = chan_cfg->inputs[ch->input_idx].input; + if (input.capabilities != V4L2_IN_CAP_DV_TIMINGS) + return -ENODATA; + + timings->pad = 0; + + ret = v4l2_subdev_call(ch->sd, pad, enum_dv_timings, timings); if (ret == -ENOIOCTLCMD || ret == -ENODEV) return -EINVAL; + return ret; } @@ -1746,13 +1191,25 @@ static int vpif_query_dv_timings(struct file *file, void *priv, struct v4l2_dv_timings *timings) { - struct vpif_fh *fh = priv; - struct channel_obj *ch = fh->channel; + struct vpif_capture_config *config = vpif_dev->platform_data; + struct video_device *vdev = video_devdata(file); + struct channel_obj *ch = video_get_drvdata(vdev); + struct vpif_capture_chan_config *chan_cfg; + struct v4l2_input input; int ret; + if (config->chan_config[ch->channel_id].inputs == NULL) + return -ENODATA; + + chan_cfg = &config->chan_config[ch->channel_id]; + input = chan_cfg->inputs[ch->input_idx].input; + if (input.capabilities != V4L2_IN_CAP_DV_TIMINGS) + return -ENODATA; + ret = v4l2_subdev_call(ch->sd, video, query_dv_timings, timings); if (ret == -ENOIOCTLCMD || ret == -ENODEV) return -ENODATA; + return ret; } @@ -1765,19 +1222,34 @@ vpif_query_dv_timings(struct file *file, void *priv, static int vpif_s_dv_timings(struct file *file, void *priv, struct v4l2_dv_timings *timings) { - struct vpif_fh *fh = priv; - struct channel_obj *ch = fh->channel; + struct vpif_capture_config *config = vpif_dev->platform_data; + struct video_device *vdev = video_devdata(file); + struct channel_obj *ch = video_get_drvdata(vdev); struct vpif_params *vpifparams = &ch->vpifparams; struct vpif_channel_config_params *std_info = &vpifparams->std_info; + struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; struct video_obj *vid_ch = &ch->video; struct v4l2_bt_timings *bt = &vid_ch->dv_timings.bt; + struct vpif_capture_chan_config *chan_cfg; + struct v4l2_input input; int ret; + if (config->chan_config[ch->channel_id].inputs == NULL) + return -ENODATA; + + chan_cfg = &config->chan_config[ch->channel_id]; + input = chan_cfg->inputs[ch->input_idx].input; + if (input.capabilities != V4L2_IN_CAP_DV_TIMINGS) + return -ENODATA; + if (timings->type != V4L2_DV_BT_656_1120) { vpif_dbg(2, debug, "Timing type not defined\n"); return -EINVAL; } + if (vb2_is_busy(&common->buffer_queue)) + return -EBUSY; + /* Configure subdevice timings, if any */ ret = v4l2_subdev_call(ch->sd, video, s_dv_timings, timings); if (ret == -ENOIOCTLCMD || ret == -ENODEV) @@ -1853,9 +1325,20 @@ static int vpif_s_dv_timings(struct file *file, void *priv, static int vpif_g_dv_timings(struct file *file, void *priv, struct v4l2_dv_timings *timings) { - struct vpif_fh *fh = priv; - struct channel_obj *ch = fh->channel; + struct vpif_capture_config *config = vpif_dev->platform_data; + struct video_device *vdev = video_devdata(file); + struct channel_obj *ch = video_get_drvdata(vdev); struct video_obj *vid_ch = &ch->video; + struct vpif_capture_chan_config *chan_cfg; + struct v4l2_input input; + + if (config->chan_config[ch->channel_id].inputs == NULL) + return -ENODATA; + + chan_cfg = &config->chan_config[ch->channel_id]; + input = chan_cfg->inputs[ch->input_idx].input; + if (input.capabilities != V4L2_IN_CAP_DV_TIMINGS) + return -ENODATA; *timings = vid_ch->dv_timings; @@ -1879,49 +1362,45 @@ static int vpif_log_status(struct file *filep, void *priv) /* vpif capture ioctl operations */ static const struct v4l2_ioctl_ops vpif_ioctl_ops = { - .vidioc_querycap = vpif_querycap, - .vidioc_g_priority = vpif_g_priority, - .vidioc_s_priority = vpif_s_priority, + .vidioc_querycap = vpif_querycap, .vidioc_enum_fmt_vid_cap = vpif_enum_fmt_vid_cap, - .vidioc_g_fmt_vid_cap = vpif_g_fmt_vid_cap, + .vidioc_g_fmt_vid_cap = vpif_g_fmt_vid_cap, .vidioc_s_fmt_vid_cap = vpif_s_fmt_vid_cap, .vidioc_try_fmt_vid_cap = vpif_try_fmt_vid_cap, + .vidioc_enum_input = vpif_enum_input, .vidioc_s_input = vpif_s_input, .vidioc_g_input = vpif_g_input, - .vidioc_reqbufs = vpif_reqbufs, - .vidioc_querybuf = vpif_querybuf, + + .vidioc_reqbufs = vb2_ioctl_reqbufs, + .vidioc_create_bufs = vb2_ioctl_create_bufs, + .vidioc_querybuf = vb2_ioctl_querybuf, + .vidioc_qbuf = vb2_ioctl_qbuf, + .vidioc_dqbuf = vb2_ioctl_dqbuf, + .vidioc_expbuf = vb2_ioctl_expbuf, + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, + .vidioc_querystd = vpif_querystd, - .vidioc_s_std = vpif_s_std, + .vidioc_s_std = vpif_s_std, .vidioc_g_std = vpif_g_std, - .vidioc_qbuf = vpif_qbuf, - .vidioc_dqbuf = vpif_dqbuf, - .vidioc_streamon = vpif_streamon, - .vidioc_streamoff = vpif_streamoff, - .vidioc_cropcap = vpif_cropcap, - .vidioc_enum_dv_timings = vpif_enum_dv_timings, - .vidioc_query_dv_timings = vpif_query_dv_timings, - .vidioc_s_dv_timings = vpif_s_dv_timings, - .vidioc_g_dv_timings = vpif_g_dv_timings, + + .vidioc_enum_dv_timings = vpif_enum_dv_timings, + .vidioc_query_dv_timings = vpif_query_dv_timings, + .vidioc_s_dv_timings = vpif_s_dv_timings, + .vidioc_g_dv_timings = vpif_g_dv_timings, + .vidioc_log_status = vpif_log_status, }; /* vpif file operations */ static struct v4l2_file_operations vpif_fops = { .owner = THIS_MODULE, - .open = vpif_open, - .release = vpif_release, + .open = v4l2_fh_open, + .release = vb2_fop_release, .unlocked_ioctl = video_ioctl2, - .mmap = vpif_mmap, - .poll = vpif_poll -}; - -/* vpif video template */ -static struct video_device vpif_video_template = { - .name = "vpif", - .fops = &vpif_fops, - .minor = -1, - .ioctl_ops = &vpif_ioctl_ops, + .mmap = vb2_fop_mmap, + .poll = vb2_fop_poll }; /** @@ -1999,7 +1478,9 @@ static int vpif_async_bound(struct v4l2_async_notifier *notifier, static int vpif_probe_complete(void) { struct common_obj *common; + struct video_device *vdev; struct channel_obj *ch; + struct vb2_queue *q; int i, j, err, k; for (j = 0; j < VPIF_CAPTURE_MAX_DEVICES; j++) { @@ -2008,17 +1489,52 @@ static int vpif_probe_complete(void) common = &(ch->common[VPIF_VIDEO_INDEX]); spin_lock_init(&common->irqlock); mutex_init(&common->lock); - ch->video_dev->lock = &common->lock; - /* Initialize prio member of channel object */ - v4l2_prio_init(&ch->prio); - video_set_drvdata(ch->video_dev, ch); /* select input 0 */ err = vpif_set_input(vpif_obj.config, ch, 0); if (err) goto probe_out; - err = video_register_device(ch->video_dev, + /* Initialize vb2 queue */ + q = &common->buffer_queue; + q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF; + q->drv_priv = ch; + q->ops = &video_qops; + q->mem_ops = &vb2_dma_contig_memops; + q->buf_struct_size = sizeof(struct vpif_cap_buffer); + q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + q->min_buffers_needed = 1; + q->lock = &common->lock; + + err = vb2_queue_init(q); + if (err) { + vpif_err("vpif_capture: vb2_queue_init() failed\n"); + goto probe_out; + } + + common->alloc_ctx = vb2_dma_contig_init_ctx(vpif_dev); + if (IS_ERR(common->alloc_ctx)) { + vpif_err("Failed to get the context\n"); + err = PTR_ERR(common->alloc_ctx); + goto probe_out; + } + + INIT_LIST_HEAD(&common->dma_queue); + + /* Initialize the video_device structure */ + vdev = ch->video_dev; + strlcpy(vdev->name, VPIF_DRIVER_NAME, sizeof(vdev->name)); + vdev->release = video_device_release; + vdev->fops = &vpif_fops; + vdev->ioctl_ops = &vpif_ioctl_ops; + vdev->v4l2_dev = &vpif_obj.v4l2_dev; + vdev->vfl_dir = VFL_DIR_RX; + vdev->queue = q; + vdev->lock = &common->lock; + set_bit(V4L2_FL_USE_FH_PRIO, &vdev->flags); + video_set_drvdata(ch->video_dev, ch); + err = video_register_device(vdev, VFL_TYPE_GRABBER, (j ? 1 : 0)); if (err) goto probe_out; @@ -2031,6 +1547,8 @@ probe_out: for (k = 0; k < j; k++) { /* Get the pointer to the channel object */ ch = vpif_obj.dev[k]; + common = &ch->common[k]; + vb2_dma_contig_cleanup_ctx(common->alloc_ctx); /* Unregister video device */ video_unregister_device(ch->video_dev); } @@ -2067,7 +1585,6 @@ static __init int vpif_probe(struct platform_device *pdev) struct video_device *vfd; struct resource *res; int subdev_count; - size_t size; vpif_dev = &pdev->dev; @@ -2085,7 +1602,7 @@ static __init int vpif_probe(struct platform_device *pdev) while ((res = platform_get_resource(pdev, IORESOURCE_IRQ, res_idx))) { err = devm_request_irq(&pdev->dev, res->start, vpif_channel_isr, - IRQF_SHARED, "VPIF_Capture", + IRQF_SHARED, VPIF_DRIVER_NAME, (void *)(&vpif_obj.dev[res_idx]-> channel_id)); if (err) { @@ -2109,34 +1626,10 @@ static __init int vpif_probe(struct platform_device *pdev) goto vpif_unregister; } - /* Initialize field of video device */ - *vfd = vpif_video_template; - vfd->v4l2_dev = &vpif_obj.v4l2_dev; - vfd->release = video_device_release; - snprintf(vfd->name, sizeof(vfd->name), - "VPIF_Capture_DRIVER_V%s", - VPIF_CAPTURE_VERSION); /* Set video_dev to the video device */ ch->video_dev = vfd; } - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (res) { - size = resource_size(res); - /* The resources are divided into two equal memory and when we - * have HD output we can add them together - */ - for (j = 0; j < VPIF_CAPTURE_MAX_DEVICES; j++) { - ch = vpif_obj.dev[j]; - ch->channel_id = j; - /* only enabled if second resource exists */ - config_params.video_limit[ch->channel_id] = 0; - if (size) - config_params.video_limit[ch->channel_id] = - size/2; - } - } - vpif_obj.config = pdev->dev.platform_data; subdev_count = vpif_obj.config->subdev_count; @@ -2209,6 +1702,7 @@ vpif_unregister: */ static int vpif_remove(struct platform_device *device) { + struct common_obj *common; struct channel_obj *ch; int i; @@ -2219,6 +1713,8 @@ static int vpif_remove(struct platform_device *device) for (i = 0; i < VPIF_CAPTURE_MAX_DEVICES; i++) { /* Get the pointer to the channel object */ ch = vpif_obj.dev[i]; + common = &ch->common[i]; + vb2_dma_contig_cleanup_ctx(common->alloc_ctx); /* Unregister video device */ video_unregister_device(ch->video_dev); kfree(vpif_obj.dev[i]); @@ -2226,7 +1722,7 @@ static int vpif_remove(struct platform_device *device) return 0; } -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP /** * vpif_suspend: vpif device suspend */ @@ -2241,18 +1737,20 @@ static int vpif_suspend(struct device *dev) /* Get the pointer to the channel object */ ch = vpif_obj.dev[i]; common = &ch->common[VPIF_VIDEO_INDEX]; + + if (!vb2_is_streaming(&common->buffer_queue)) + continue; + mutex_lock(&common->lock); - if (ch->usrs && common->io_usrs) { - /* Disable channel */ - if (ch->channel_id == VPIF_CHANNEL0_VIDEO) { - enable_channel0(0); - channel0_intr_enable(0); - } - if (ch->channel_id == VPIF_CHANNEL1_VIDEO || - common->started == 2) { - enable_channel1(0); - channel1_intr_enable(0); - } + /* Disable channel */ + if (ch->channel_id == VPIF_CHANNEL0_VIDEO) { + enable_channel0(0); + channel0_intr_enable(0); + } + if (ch->channel_id == VPIF_CHANNEL1_VIDEO || + ycmux_mode == 2) { + enable_channel1(0); + channel1_intr_enable(0); } mutex_unlock(&common->lock); } @@ -2273,40 +1771,35 @@ static int vpif_resume(struct device *dev) /* Get the pointer to the channel object */ ch = vpif_obj.dev[i]; common = &ch->common[VPIF_VIDEO_INDEX]; + + if (!vb2_is_streaming(&common->buffer_queue)) + continue; + mutex_lock(&common->lock); - if (ch->usrs && common->io_usrs) { - /* Disable channel */ - if (ch->channel_id == VPIF_CHANNEL0_VIDEO) { - enable_channel0(1); - channel0_intr_enable(1); - } - if (ch->channel_id == VPIF_CHANNEL1_VIDEO || - common->started == 2) { - enable_channel1(1); - channel1_intr_enable(1); - } + /* Enable channel */ + if (ch->channel_id == VPIF_CHANNEL0_VIDEO) { + enable_channel0(1); + channel0_intr_enable(1); + } + if (ch->channel_id == VPIF_CHANNEL1_VIDEO || + ycmux_mode == 2) { + enable_channel1(1); + channel1_intr_enable(1); } mutex_unlock(&common->lock); } return 0; } - -static const struct dev_pm_ops vpif_dev_pm_ops = { - .suspend = vpif_suspend, - .resume = vpif_resume, -}; - -#define vpif_pm_ops (&vpif_dev_pm_ops) -#else -#define vpif_pm_ops NULL #endif +static SIMPLE_DEV_PM_OPS(vpif_pm_ops, vpif_suspend, vpif_resume); + static __refdata struct platform_driver vpif_driver = { .driver = { - .name = "vpif_capture", + .name = VPIF_DRIVER_NAME, .owner = THIS_MODULE, - .pm = vpif_pm_ops, + .pm = &vpif_pm_ops, }, .probe = vpif_probe, .remove = vpif_remove, diff --git a/drivers/media/platform/davinci/vpif_capture.h b/drivers/media/platform/davinci/vpif_capture.h index 5a29d9a0cae1..1ee17824f484 100644 --- a/drivers/media/platform/davinci/vpif_capture.h +++ b/drivers/media/platform/davinci/vpif_capture.h @@ -19,8 +19,6 @@ #ifndef VPIF_CAPTURE_H #define VPIF_CAPTURE_H -#ifdef __KERNEL__ - /* Header files */ #include <media/videobuf2-dma-contig.h> #include <media/v4l2-device.h> @@ -63,11 +61,6 @@ struct common_obj { struct vpif_cap_buffer *cur_frm; /* Pointer pointing to current v4l2_buffer */ struct vpif_cap_buffer *next_frm; - /* - * This field keeps track of type of buffer exchange mechanism - * user has selected - */ - enum v4l2_memory memory; /* Used to store pixel format */ struct v4l2_format fmt; /* Buffer queue used in video-buf */ @@ -80,10 +73,6 @@ struct common_obj { spinlock_t irqlock; /* lock used to access this structure */ struct mutex lock; - /* number of users performing IO */ - u32 io_usrs; - /* Indicates whether streaming started */ - u8 started; /* Function pointer to set the addresses */ void (*set_addr) (unsigned long, unsigned long, unsigned long, unsigned long); @@ -104,10 +93,6 @@ struct common_obj { struct channel_obj { /* Identifies video device for this channel */ struct video_device *video_dev; - /* Used to keep track of state of the priority */ - struct v4l2_prio_state prio; - /* number of open instances of the channel */ - int usrs; /* Indicates id of the field which is being displayed */ u32 field_id; /* flag to indicate whether decoder is initialized */ @@ -126,18 +111,6 @@ struct channel_obj { struct video_obj video; }; -/* File handle structure */ -struct vpif_fh { - /* pointer to channel object for opened device */ - struct channel_obj *channel; - /* Indicates whether this file handle is doing IO */ - u8 io_allowed[VPIF_NUMBER_OF_OBJECTS]; - /* Used to keep track priority of this instance */ - enum v4l2_priority prio; - /* Used to indicate channel is initialize or not */ - u8 initialized; -}; - struct vpif_device { struct v4l2_device v4l2_dev; struct channel_obj *dev[VPIF_CAPTURE_NUM_CHANNELS]; @@ -157,5 +130,4 @@ struct vpif_config_params { u8 max_device_type; }; -#endif /* End of __KERNEL__ */ #endif /* VPIF_CAPTURE_H */ diff --git a/drivers/media/platform/davinci/vpif_display.c b/drivers/media/platform/davinci/vpif_display.c index aed41edd0501..5bb085b19bcb 100644 --- a/drivers/media/platform/davinci/vpif_display.c +++ b/drivers/media/platform/davinci/vpif_display.c @@ -3,6 +3,7 @@ * Display driver for TI DaVinci VPIF * * Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.com/ + * Copyright (C) 2014 Lad, Prabhakar <prabhakar.csengg@gmail.com> * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -35,129 +36,110 @@ MODULE_VERSION(VPIF_DISPLAY_VERSION); v4l2_dbg(level, debug, &vpif_obj.v4l2_dev, fmt, ## arg) static int debug = 1; -static u32 ch2_numbuffers = 3; -static u32 ch3_numbuffers = 3; -static u32 ch2_bufsize = 1920 * 1080 * 2; -static u32 ch3_bufsize = 720 * 576 * 2; module_param(debug, int, 0644); -module_param(ch2_numbuffers, uint, S_IRUGO); -module_param(ch3_numbuffers, uint, S_IRUGO); -module_param(ch2_bufsize, uint, S_IRUGO); -module_param(ch3_bufsize, uint, S_IRUGO); MODULE_PARM_DESC(debug, "Debug level 0-1"); -MODULE_PARM_DESC(ch2_numbuffers, "Channel2 buffer count (default:3)"); -MODULE_PARM_DESC(ch3_numbuffers, "Channel3 buffer count (default:3)"); -MODULE_PARM_DESC(ch2_bufsize, "Channel2 buffer size (default:1920 x 1080 x 2)"); -MODULE_PARM_DESC(ch3_bufsize, "Channel3 buffer size (default:720 x 576 x 2)"); - -static struct vpif_config_params config_params = { - .min_numbuffers = 3, - .numbuffers[0] = 3, - .numbuffers[1] = 3, - .min_bufsize[0] = 720 * 480 * 2, - .min_bufsize[1] = 720 * 480 * 2, - .channel_bufsize[0] = 1920 * 1080 * 2, - .channel_bufsize[1] = 720 * 576 * 2, -}; + +#define VPIF_DRIVER_NAME "vpif_display" + +/* Is set to 1 in case of SDTV formats, 2 in case of HDTV formats. */ +static int ycmux_mode; + +static u8 channel_first_int[VPIF_NUMOBJECTS][2] = { {1, 1} }; static struct vpif_device vpif_obj = { {NULL} }; static struct device *vpif_dev; static void vpif_calculate_offsets(struct channel_obj *ch); static void vpif_config_addr(struct channel_obj *ch, int muxmode); -/* - * buffer_prepare: This is the callback function called from vb2_qbuf() - * function the buffer is prepared and user space virtual address is converted - * into physical address +static inline struct vpif_disp_buffer *to_vpif_buffer(struct vb2_buffer *vb) +{ + return container_of(vb, struct vpif_disp_buffer, vb); +} + +/** + * vpif_buffer_prepare : callback function for buffer prepare + * @vb: ptr to vb2_buffer + * + * This is the callback function for buffer prepare when vb2_qbuf() + * function is called. The buffer is prepared and user space virtual address + * or user address is converted into physical address */ static int vpif_buffer_prepare(struct vb2_buffer *vb) { - struct vpif_fh *fh = vb2_get_drv_priv(vb->vb2_queue); - struct vb2_queue *q = vb->vb2_queue; + struct channel_obj *ch = vb2_get_drv_priv(vb->vb2_queue); struct common_obj *common; - unsigned long addr; - - common = &fh->channel->common[VPIF_VIDEO_INDEX]; - if (vb->state != VB2_BUF_STATE_ACTIVE && - vb->state != VB2_BUF_STATE_PREPARED) { - vb2_set_plane_payload(vb, 0, common->fmt.fmt.pix.sizeimage); - if (vb2_plane_vaddr(vb, 0) && - vb2_get_plane_payload(vb, 0) > vb2_plane_size(vb, 0)) - goto buf_align_exit; - - addr = vb2_dma_contig_plane_dma_addr(vb, 0); - if (q->streaming && - (V4L2_BUF_TYPE_SLICED_VBI_OUTPUT != q->type)) { - if (!ISALIGNED(addr + common->ytop_off) || + + common = &ch->common[VPIF_VIDEO_INDEX]; + + vb2_set_plane_payload(vb, 0, common->fmt.fmt.pix.sizeimage); + if (vb2_get_plane_payload(vb, 0) > vb2_plane_size(vb, 0)) + return -EINVAL; + + vb->v4l2_buf.field = common->fmt.fmt.pix.field; + + if (vb->vb2_queue->type != V4L2_BUF_TYPE_SLICED_VBI_OUTPUT) { + unsigned long addr = vb2_dma_contig_plane_dma_addr(vb, 0); + + if (!ISALIGNED(addr + common->ytop_off) || !ISALIGNED(addr + common->ybtm_off) || !ISALIGNED(addr + common->ctop_off) || - !ISALIGNED(addr + common->cbtm_off)) - goto buf_align_exit; + !ISALIGNED(addr + common->cbtm_off)) { + vpif_err("buffer offset not aligned to 8 bytes\n"); + return -EINVAL; } } - return 0; -buf_align_exit: - vpif_err("buffer offset not aligned to 8 bytes\n"); - return -EINVAL; + return 0; } -/* - * vpif_buffer_queue_setup: This function allocates memory for the buffers +/** + * vpif_buffer_queue_setup : Callback function for buffer setup. + * @vq: vb2_queue ptr + * @fmt: v4l2 format + * @nbuffers: ptr to number of buffers requested by application + * @nplanes:: contains number of distinct video planes needed to hold a frame + * @sizes[]: contains the size (in bytes) of each plane. + * @alloc_ctxs: ptr to allocation context + * + * This callback function is called when reqbuf() is called to adjust + * the buffer count and buffer size */ static int vpif_buffer_queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt, unsigned int *nbuffers, unsigned int *nplanes, unsigned int sizes[], void *alloc_ctxs[]) { - struct vpif_fh *fh = vb2_get_drv_priv(vq); - struct channel_obj *ch = fh->channel; + struct channel_obj *ch = vb2_get_drv_priv(vq); struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; - unsigned long size; - - if (V4L2_MEMORY_MMAP == common->memory) { - size = config_params.channel_bufsize[ch->channel_id]; - /* - * Checking if the buffer size exceeds the available buffer - * ycmux_mode = 0 means 1 channel mode HD and - * ycmux_mode = 1 means 2 channels mode SD - */ - if (ch->vpifparams.std_info.ycmux_mode == 0) { - if (config_params.video_limit[ch->channel_id]) - while (size * *nbuffers > - (config_params.video_limit[0] - + config_params.video_limit[1])) - (*nbuffers)--; - } else { - if (config_params.video_limit[ch->channel_id]) - while (size * *nbuffers > - config_params.video_limit[ch->channel_id]) - (*nbuffers)--; - } - } else { - size = common->fmt.fmt.pix.sizeimage; - } - if (*nbuffers < config_params.min_numbuffers) - *nbuffers = config_params.min_numbuffers; + if (fmt && fmt->fmt.pix.sizeimage < common->fmt.fmt.pix.sizeimage) + return -EINVAL; + + if (vq->num_buffers + *nbuffers < 3) + *nbuffers = 3 - vq->num_buffers; *nplanes = 1; - sizes[0] = size; + sizes[0] = fmt ? fmt->fmt.pix.sizeimage : common->fmt.fmt.pix.sizeimage; alloc_ctxs[0] = common->alloc_ctx; + + /* Calculate the offset for Y and C data in the buffer */ + vpif_calculate_offsets(ch); + return 0; } -/* - * vpif_buffer_queue: This function adds the buffer to DMA queue +/** + * vpif_buffer_queue : Callback function to add buffer to DMA queue + * @vb: ptr to vb2_buffer + * + * This callback fucntion queues the buffer to DMA engine */ static void vpif_buffer_queue(struct vb2_buffer *vb) { - struct vpif_fh *fh = vb2_get_drv_priv(vb->vb2_queue); - struct vpif_disp_buffer *buf = container_of(vb, - struct vpif_disp_buffer, vb); - struct channel_obj *ch = fh->channel; + struct vpif_disp_buffer *buf = to_vpif_buffer(vb); + struct channel_obj *ch = vb2_get_drv_priv(vb->vb2_queue); struct common_obj *common; unsigned long flags; @@ -169,98 +151,26 @@ static void vpif_buffer_queue(struct vb2_buffer *vb) spin_unlock_irqrestore(&common->irqlock, flags); } -/* - * vpif_buf_cleanup: This function is called from the videobuf2 layer to - * free memory allocated to the buffers +/** + * vpif_start_streaming : Starts the DMA engine for streaming + * @vb: ptr to vb2_buffer + * @count: number of buffers */ -static void vpif_buf_cleanup(struct vb2_buffer *vb) -{ - struct vpif_fh *fh = vb2_get_drv_priv(vb->vb2_queue); - struct vpif_disp_buffer *buf = container_of(vb, - struct vpif_disp_buffer, vb); - struct channel_obj *ch = fh->channel; - struct common_obj *common; - unsigned long flags; - - common = &ch->common[VPIF_VIDEO_INDEX]; - - spin_lock_irqsave(&common->irqlock, flags); - if (vb->state == VB2_BUF_STATE_ACTIVE) - list_del_init(&buf->list); - spin_unlock_irqrestore(&common->irqlock, flags); -} - -static void vpif_wait_prepare(struct vb2_queue *vq) -{ - struct vpif_fh *fh = vb2_get_drv_priv(vq); - struct channel_obj *ch = fh->channel; - struct common_obj *common; - - common = &ch->common[VPIF_VIDEO_INDEX]; - mutex_unlock(&common->lock); -} - -static void vpif_wait_finish(struct vb2_queue *vq) -{ - struct vpif_fh *fh = vb2_get_drv_priv(vq); - struct channel_obj *ch = fh->channel; - struct common_obj *common; - - common = &ch->common[VPIF_VIDEO_INDEX]; - mutex_lock(&common->lock); -} - -static int vpif_buffer_init(struct vb2_buffer *vb) -{ - struct vpif_disp_buffer *buf = container_of(vb, - struct vpif_disp_buffer, vb); - - INIT_LIST_HEAD(&buf->list); - - return 0; -} - -static u8 channel_first_int[VPIF_NUMOBJECTS][2] = { {1, 1} }; - static int vpif_start_streaming(struct vb2_queue *vq, unsigned int count) { struct vpif_display_config *vpif_config_data = vpif_dev->platform_data; - struct vpif_fh *fh = vb2_get_drv_priv(vq); - struct channel_obj *ch = fh->channel; + struct channel_obj *ch = vb2_get_drv_priv(vq); struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; struct vpif_params *vpif = &ch->vpifparams; - unsigned long addr = 0; - unsigned long flags; + struct vpif_disp_buffer *buf, *tmp; + unsigned long addr, flags; int ret; spin_lock_irqsave(&common->irqlock, flags); - /* Get the next frame from the buffer queue */ - common->next_frm = common->cur_frm = - list_entry(common->dma_queue.next, - struct vpif_disp_buffer, list); - - list_del(&common->cur_frm->list); - spin_unlock_irqrestore(&common->irqlock, flags); - /* Mark state of the current frame to active */ - common->cur_frm->vb.state = VB2_BUF_STATE_ACTIVE; - - /* Initialize field_id and started member */ + /* Initialize field_id */ ch->field_id = 0; - common->started = 1; - addr = vb2_dma_contig_plane_dma_addr(&common->cur_frm->vb, 0); - /* Calculate the offset for Y and C data in the buffer */ - vpif_calculate_offsets(ch); - - if ((ch->vpifparams.std_info.frm_fmt && - ((common->fmt.fmt.pix.field != V4L2_FIELD_NONE) - && (common->fmt.fmt.pix.field != V4L2_FIELD_ANY))) - || (!ch->vpifparams.std_info.frm_fmt - && (common->fmt.fmt.pix.field == V4L2_FIELD_NONE))) { - vpif_err("conflict in field format and std format\n"); - return -EINVAL; - } /* clock settings */ if (vpif_config_data->set_clock) { @@ -268,24 +178,37 @@ static int vpif_start_streaming(struct vb2_queue *vq, unsigned int count) ycmux_mode, ch->vpifparams.std_info.hd_sd); if (ret < 0) { vpif_err("can't set clock\n"); - return ret; + goto err; } } /* set the parameters and addresses */ ret = vpif_set_video_params(vpif, ch->channel_id + 2); if (ret < 0) - return ret; + goto err; - common->started = ret; + ycmux_mode = ret; vpif_config_addr(ch, ret); + /* Get the next frame from the buffer queue */ + common->next_frm = common->cur_frm = + list_entry(common->dma_queue.next, + struct vpif_disp_buffer, list); + + list_del(&common->cur_frm->list); + spin_unlock_irqrestore(&common->irqlock, flags); + /* Mark state of the current frame to active */ + common->cur_frm->vb.state = VB2_BUF_STATE_ACTIVE; + + addr = vb2_dma_contig_plane_dma_addr(&common->cur_frm->vb, 0); common->set_addr((addr + common->ytop_off), (addr + common->ybtm_off), (addr + common->ctop_off), (addr + common->cbtm_off)); - /* Set interrupt for both the fields in VPIF - Register enable channel in VPIF register */ + /* + * Set interrupt for both the fields in VPIF + * Register enable channel in VPIF register + */ channel_first_int[VPIF_VIDEO_INDEX][ch->channel_id] = 1; if (VPIF_CHANNEL2_VIDEO == ch->channel_id) { channel2_intr_assert(); @@ -295,8 +218,7 @@ static int vpif_start_streaming(struct vb2_queue *vq, unsigned int count) channel2_clipping_enable(1); } - if ((VPIF_CHANNEL3_VIDEO == ch->channel_id) - || (common->started == 2)) { + if (VPIF_CHANNEL3_VIDEO == ch->channel_id || ycmux_mode == 2) { channel3_intr_assert(); channel3_intr_enable(1); enable_channel3(1); @@ -305,19 +227,29 @@ static int vpif_start_streaming(struct vb2_queue *vq, unsigned int count) } return 0; + +err: + list_for_each_entry_safe(buf, tmp, &common->dma_queue, list) { + list_del(&buf->list); + vb2_buffer_done(&buf->vb, VB2_BUF_STATE_QUEUED); + } + + return ret; } -/* abort streaming and wait for last buffer */ -static int vpif_stop_streaming(struct vb2_queue *vq) +/** + * vpif_stop_streaming : Stop the DMA engine + * @vq: ptr to vb2_queue + * + * This callback stops the DMA engine and any remaining buffers + * in the DMA queue are released. + */ +static void vpif_stop_streaming(struct vb2_queue *vq) { - struct vpif_fh *fh = vb2_get_drv_priv(vq); - struct channel_obj *ch = fh->channel; + struct channel_obj *ch = vb2_get_drv_priv(vq); struct common_obj *common; unsigned long flags; - if (!vb2_is_streaming(vq)) - return 0; - common = &ch->common[VPIF_VIDEO_INDEX]; /* Disable channel */ @@ -325,12 +257,10 @@ static int vpif_stop_streaming(struct vb2_queue *vq) enable_channel2(0); channel2_intr_enable(0); } - if ((VPIF_CHANNEL3_VIDEO == ch->channel_id) || - (2 == common->started)) { + if (VPIF_CHANNEL3_VIDEO == ch->channel_id || ycmux_mode == 2) { enable_channel3(0); channel3_intr_enable(0); } - common->started = 0; /* release all active buffers */ spin_lock_irqsave(&common->irqlock, flags); @@ -352,19 +282,15 @@ static int vpif_stop_streaming(struct vb2_queue *vq) vb2_buffer_done(&common->next_frm->vb, VB2_BUF_STATE_ERROR); } spin_unlock_irqrestore(&common->irqlock, flags); - - return 0; } static struct vb2_ops video_qops = { .queue_setup = vpif_buffer_queue_setup, - .wait_prepare = vpif_wait_prepare, - .wait_finish = vpif_wait_finish, - .buf_init = vpif_buffer_init, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, .buf_prepare = vpif_buffer_prepare, .start_streaming = vpif_start_streaming, .stop_streaming = vpif_stop_streaming, - .buf_cleanup = vpif_buf_cleanup, .buf_queue = vpif_buffer_queue, }; @@ -446,8 +372,6 @@ static irqreturn_t vpif_channel_isr(int irq, void *dev_id) for (i = 0; i < VPIF_NUMOBJECTS; i++) { common = &ch->common[i]; /* If streaming is started in this channel */ - if (0 == common->started) - continue; if (1 == ch->vpifparams.std_info.frm_fmt) { spin_lock(&common->irqlock); @@ -543,6 +467,7 @@ static int vpif_update_resolution(struct channel_obj *ch) return -EINVAL; } + common->fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUV422P; common->fmt.fmt.pix.width = std_info->width; common->fmt.fmt.pix.height = std_info->height; vpif_dbg(1, debug, "Pixel details: Width = %d,Height = %d\n", @@ -551,6 +476,17 @@ static int vpif_update_resolution(struct channel_obj *ch) /* Set height and width paramateres */ common->height = std_info->height; common->width = std_info->width; + common->fmt.fmt.pix.sizeimage = common->height * common->width * 2; + + if (vid_ch->stdid) + common->fmt.fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; + else + common->fmt.fmt.pix.colorspace = V4L2_COLORSPACE_REC709; + + if (ch->vpifparams.std_info.frm_fmt) + common->fmt.fmt.pix.field = V4L2_FIELD_NONE; + else + common->fmt.fmt.pix.field = V4L2_FIELD_INTERLACED; return 0; } @@ -621,70 +557,6 @@ static void vpif_calculate_offsets(struct channel_obj *ch) ch->vpifparams.video_params.stdid = ch->vpifparams.std_info.stdid; } -static void vpif_config_format(struct channel_obj *ch) -{ - struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; - - common->fmt.fmt.pix.field = V4L2_FIELD_ANY; - if (config_params.numbuffers[ch->channel_id] == 0) - common->memory = V4L2_MEMORY_USERPTR; - else - common->memory = V4L2_MEMORY_MMAP; - - common->fmt.fmt.pix.sizeimage = - config_params.channel_bufsize[ch->channel_id]; - common->fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUV422P; - common->fmt.type = V4L2_BUF_TYPE_VIDEO_OUTPUT; -} - -static int vpif_check_format(struct channel_obj *ch, - struct v4l2_pix_format *pixfmt) -{ - struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; - enum v4l2_field field = pixfmt->field; - u32 sizeimage, hpitch, vpitch; - - if (pixfmt->pixelformat != V4L2_PIX_FMT_YUV422P) - goto invalid_fmt_exit; - - if (!(VPIF_VALID_FIELD(field))) - goto invalid_fmt_exit; - - if (pixfmt->bytesperline <= 0) - goto invalid_pitch_exit; - - sizeimage = pixfmt->sizeimage; - - if (vpif_update_resolution(ch)) - return -EINVAL; - - hpitch = pixfmt->bytesperline; - vpitch = sizeimage / (hpitch * 2); - - /* Check for valid value of pitch */ - if ((hpitch < ch->vpifparams.std_info.width) || - (vpitch < ch->vpifparams.std_info.height)) - goto invalid_pitch_exit; - - /* Check for 8 byte alignment */ - if (!ISALIGNED(hpitch)) { - vpif_err("invalid pitch alignment\n"); - return -EINVAL; - } - pixfmt->width = common->fmt.fmt.pix.width; - pixfmt->height = common->fmt.fmt.pix.height; - - return 0; - -invalid_fmt_exit: - vpif_err("invalid field format\n"); - return -EINVAL; - -invalid_pitch_exit: - vpif_err("invalid pitch\n"); - return -EINVAL; -} - static void vpif_config_addr(struct channel_obj *ch, int muxmode) { struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; @@ -699,127 +571,6 @@ static void vpif_config_addr(struct channel_obj *ch, int muxmode) } } -/* - * vpif_mmap: It is used to map kernel space buffers into user spaces - */ -static int vpif_mmap(struct file *filep, struct vm_area_struct *vma) -{ - struct vpif_fh *fh = filep->private_data; - struct channel_obj *ch = fh->channel; - struct common_obj *common = &(ch->common[VPIF_VIDEO_INDEX]); - int ret; - - vpif_dbg(2, debug, "vpif_mmap\n"); - - if (mutex_lock_interruptible(&common->lock)) - return -ERESTARTSYS; - ret = vb2_mmap(&common->buffer_queue, vma); - mutex_unlock(&common->lock); - return ret; -} - -/* - * vpif_poll: It is used for select/poll system call - */ -static unsigned int vpif_poll(struct file *filep, poll_table *wait) -{ - struct vpif_fh *fh = filep->private_data; - struct channel_obj *ch = fh->channel; - struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; - unsigned int res = 0; - - if (common->started) { - mutex_lock(&common->lock); - res = vb2_poll(&common->buffer_queue, filep, wait); - mutex_unlock(&common->lock); - } - - return res; -} - -/* - * vpif_open: It creates object of file handle structure and stores it in - * private_data member of filepointer - */ -static int vpif_open(struct file *filep) -{ - struct video_device *vdev = video_devdata(filep); - struct channel_obj *ch = video_get_drvdata(vdev); - struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; - struct vpif_fh *fh; - - /* Allocate memory for the file handle object */ - fh = kzalloc(sizeof(struct vpif_fh), GFP_KERNEL); - if (fh == NULL) { - vpif_err("unable to allocate memory for file handle object\n"); - return -ENOMEM; - } - - if (mutex_lock_interruptible(&common->lock)) { - kfree(fh); - return -ERESTARTSYS; - } - /* store pointer to fh in private_data member of filep */ - filep->private_data = fh; - fh->channel = ch; - fh->initialized = 0; - if (!ch->initialized) { - fh->initialized = 1; - ch->initialized = 1; - memset(&ch->vpifparams, 0, sizeof(ch->vpifparams)); - } - - /* Increment channel usrs counter */ - atomic_inc(&ch->usrs); - /* Set io_allowed[VPIF_VIDEO_INDEX] member to false */ - fh->io_allowed[VPIF_VIDEO_INDEX] = 0; - /* Initialize priority of this instance to default priority */ - fh->prio = V4L2_PRIORITY_UNSET; - v4l2_prio_open(&ch->prio, &fh->prio); - mutex_unlock(&common->lock); - - return 0; -} - -/* - * vpif_release: This function deletes buffer queue, frees the buffers and - * the vpif file handle - */ -static int vpif_release(struct file *filep) -{ - struct vpif_fh *fh = filep->private_data; - struct channel_obj *ch = fh->channel; - struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; - - mutex_lock(&common->lock); - /* if this instance is doing IO */ - if (fh->io_allowed[VPIF_VIDEO_INDEX]) { - /* Reset io_usrs member of channel object */ - common->io_usrs = 0; - /* Free buffers allocated */ - vb2_queue_release(&common->buffer_queue); - vb2_dma_contig_cleanup_ctx(common->alloc_ctx); - - common->numbuffers = - config_params.numbuffers[ch->channel_id]; - } - - /* Decrement channel usrs counter */ - atomic_dec(&ch->usrs); - /* If this file handle has initialize encoder device, reset it */ - if (fh->initialized) - ch->initialized = 0; - - /* Close the priority */ - v4l2_prio_close(&ch->prio, fh->prio); - filep->private_data = NULL; - fh->initialized = 0; - mutex_unlock(&common->lock); - kfree(fh); - - return 0; -} - /* functions implementing ioctls */ /** * vpif_querycap() - QUERYCAP handler @@ -834,7 +585,7 @@ static int vpif_querycap(struct file *file, void *priv, cap->device_caps = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING; cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; - snprintf(cap->driver, sizeof(cap->driver), "%s", dev_name(vpif_dev)); + strlcpy(cap->driver, VPIF_DRIVER_NAME, sizeof(cap->driver)); snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s", dev_name(vpif_dev)); strlcpy(cap->card, config->card_name, sizeof(cap->card)); @@ -845,24 +596,22 @@ static int vpif_querycap(struct file *file, void *priv, static int vpif_enum_fmt_vid_out(struct file *file, void *priv, struct v4l2_fmtdesc *fmt) { - if (fmt->index != 0) { - vpif_err("Invalid format index\n"); + if (fmt->index != 0) return -EINVAL; - } /* Fill in the information about format */ fmt->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; strcpy(fmt->description, "YCbCr4:2:2 YC Planar"); fmt->pixelformat = V4L2_PIX_FMT_YUV422P; - + fmt->flags = 0; return 0; } static int vpif_g_fmt_vid_out(struct file *file, void *priv, struct v4l2_format *fmt) { - struct vpif_fh *fh = priv; - struct channel_obj *ch = fh->channel; + struct video_device *vdev = video_devdata(file); + struct channel_obj *ch = video_get_drvdata(vdev); struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; /* Check the validity of the buffer type */ @@ -875,193 +624,84 @@ static int vpif_g_fmt_vid_out(struct file *file, void *priv, return 0; } -static int vpif_s_fmt_vid_out(struct file *file, void *priv, +static int vpif_try_fmt_vid_out(struct file *file, void *priv, struct v4l2_format *fmt) { - struct vpif_fh *fh = priv; - struct v4l2_pix_format *pixfmt; - struct channel_obj *ch = fh->channel; + struct video_device *vdev = video_devdata(file); + struct channel_obj *ch = video_get_drvdata(vdev); struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; - int ret = 0; - - if ((VPIF_CHANNEL2_VIDEO == ch->channel_id) - || (VPIF_CHANNEL3_VIDEO == ch->channel_id)) { - if (!fh->initialized) { - vpif_dbg(1, debug, "Channel Busy\n"); - return -EBUSY; - } + struct v4l2_pix_format *pixfmt = &fmt->fmt.pix; - /* Check for the priority */ - ret = v4l2_prio_check(&ch->prio, fh->prio); - if (0 != ret) - return ret; - fh->initialized = 1; - } + /* + * to supress v4l-compliance warnings silently correct + * the pixelformat + */ + if (pixfmt->pixelformat != V4L2_PIX_FMT_YUV422P) + pixfmt->pixelformat = common->fmt.fmt.pix.pixelformat; - if (common->started) { - vpif_dbg(1, debug, "Streaming in progress\n"); - return -EBUSY; - } + if (vpif_update_resolution(ch)) + return -EINVAL; - pixfmt = &fmt->fmt.pix; - /* Check for valid field format */ - ret = vpif_check_format(ch, pixfmt); - if (ret) - return ret; + pixfmt->colorspace = common->fmt.fmt.pix.colorspace; + pixfmt->field = common->fmt.fmt.pix.field; + pixfmt->bytesperline = common->fmt.fmt.pix.width; + pixfmt->width = common->fmt.fmt.pix.width; + pixfmt->height = common->fmt.fmt.pix.height; + pixfmt->sizeimage = pixfmt->bytesperline * pixfmt->height * 2; + pixfmt->priv = 0; - /* store the pix format in the channel object */ - common->fmt.fmt.pix = *pixfmt; - /* store the format in the channel object */ - common->fmt = *fmt; return 0; } -static int vpif_try_fmt_vid_out(struct file *file, void *priv, +static int vpif_s_fmt_vid_out(struct file *file, void *priv, struct v4l2_format *fmt) { - struct vpif_fh *fh = priv; - struct channel_obj *ch = fh->channel; + struct video_device *vdev = video_devdata(file); + struct channel_obj *ch = video_get_drvdata(vdev); struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; struct v4l2_pix_format *pixfmt = &fmt->fmt.pix; - int ret = 0; - - ret = vpif_check_format(ch, pixfmt); - if (ret) { - *pixfmt = common->fmt.fmt.pix; - pixfmt->sizeimage = pixfmt->width * pixfmt->height * 2; - } - - return ret; -} - -static int vpif_reqbufs(struct file *file, void *priv, - struct v4l2_requestbuffers *reqbuf) -{ - struct vpif_fh *fh = priv; - struct channel_obj *ch = fh->channel; - struct common_obj *common; - enum v4l2_field field; - struct vb2_queue *q; - u8 index = 0; int ret; - /* This file handle has not initialized the channel, - It is not allowed to do settings */ - if ((VPIF_CHANNEL2_VIDEO == ch->channel_id) - || (VPIF_CHANNEL3_VIDEO == ch->channel_id)) { - if (!fh->initialized) { - vpif_err("Channel Busy\n"); - return -EBUSY; - } - } - - if (V4L2_BUF_TYPE_VIDEO_OUTPUT != reqbuf->type) - return -EINVAL; - - index = VPIF_VIDEO_INDEX; - - common = &ch->common[index]; - - if (common->fmt.type != reqbuf->type || !vpif_dev) - return -EINVAL; - if (0 != common->io_usrs) + if (vb2_is_busy(&common->buffer_queue)) return -EBUSY; - if (reqbuf->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) { - if (common->fmt.fmt.pix.field == V4L2_FIELD_ANY) - field = V4L2_FIELD_INTERLACED; - else - field = common->fmt.fmt.pix.field; - } else { - field = V4L2_VBI_INTERLACED; - } - /* Initialize videobuf2 queue as per the buffer type */ - common->alloc_ctx = vb2_dma_contig_init_ctx(vpif_dev); - if (IS_ERR(common->alloc_ctx)) { - vpif_err("Failed to get the context\n"); - return PTR_ERR(common->alloc_ctx); - } - q = &common->buffer_queue; - q->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; - q->io_modes = VB2_MMAP | VB2_USERPTR; - q->drv_priv = fh; - q->ops = &video_qops; - q->mem_ops = &vb2_dma_contig_memops; - q->buf_struct_size = sizeof(struct vpif_disp_buffer); - q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; - q->min_buffers_needed = 1; - - ret = vb2_queue_init(q); - if (ret) { - vpif_err("vpif_display: vb2_queue_init() failed\n"); - vb2_dma_contig_cleanup_ctx(common->alloc_ctx); + ret = vpif_try_fmt_vid_out(file, priv, fmt); + if (ret) return ret; - } - /* Set io allowed member of file handle to TRUE */ - fh->io_allowed[index] = 1; - /* Increment io usrs member of channel object to 1 */ - common->io_usrs = 1; - /* Store type of memory requested in channel object */ - common->memory = reqbuf->memory; - INIT_LIST_HEAD(&common->dma_queue); - /* Allocate buffers */ - return vb2_reqbufs(&common->buffer_queue, reqbuf); -} - -static int vpif_querybuf(struct file *file, void *priv, - struct v4l2_buffer *tbuf) -{ - struct vpif_fh *fh = priv; - struct channel_obj *ch = fh->channel; - struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; - if (common->fmt.type != tbuf->type) - return -EINVAL; + /* store the pix format in the channel object */ + common->fmt.fmt.pix = *pixfmt; - return vb2_querybuf(&common->buffer_queue, tbuf); + /* store the format in the channel object */ + common->fmt = *fmt; + return 0; } -static int vpif_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf) +static int vpif_s_std(struct file *file, void *priv, v4l2_std_id std_id) { - struct vpif_fh *fh = NULL; - struct channel_obj *ch = NULL; - struct common_obj *common = NULL; - - if (!buf || !priv) - return -EINVAL; - - fh = priv; - ch = fh->channel; - if (!ch) - return -EINVAL; + struct vpif_display_config *config = vpif_dev->platform_data; + struct video_device *vdev = video_devdata(file); + struct channel_obj *ch = video_get_drvdata(vdev); + struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; + struct vpif_display_chan_config *chan_cfg; + struct v4l2_output output; + int ret; - common = &(ch->common[VPIF_VIDEO_INDEX]); - if (common->fmt.type != buf->type) - return -EINVAL; + if (config->chan_config[ch->channel_id].outputs == NULL) + return -ENODATA; - if (!fh->io_allowed[VPIF_VIDEO_INDEX]) { - vpif_err("fh->io_allowed\n"); - return -EACCES; - } + chan_cfg = &config->chan_config[ch->channel_id]; + output = chan_cfg->outputs[ch->output_idx].output; + if (output.capabilities != V4L2_OUT_CAP_STD) + return -ENODATA; - return vb2_qbuf(&common->buffer_queue, buf); -} + if (vb2_is_busy(&common->buffer_queue)) + return -EBUSY; -static int vpif_s_std(struct file *file, void *priv, v4l2_std_id std_id) -{ - struct vpif_fh *fh = priv; - struct channel_obj *ch = fh->channel; - struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; - int ret = 0; if (!(std_id & VPIF_V4L2_STD)) return -EINVAL; - if (common->started) { - vpif_err("streaming in progress\n"); - return -EBUSY; - } - /* Call encoder subdevice function to set the standard */ ch->video.stdid = std_id; memset(&ch->video.dv_timings, 0, sizeof(ch->video.dv_timings)); @@ -1069,16 +709,7 @@ static int vpif_s_std(struct file *file, void *priv, v4l2_std_id std_id) if (vpif_update_resolution(ch)) return -EINVAL; - if ((ch->vpifparams.std_info.width * - ch->vpifparams.std_info.height * 2) > - config_params.channel_bufsize[ch->channel_id]) { - vpif_err("invalid std for this size\n"); - return -EINVAL; - } - common->fmt.fmt.pix.bytesperline = common->fmt.fmt.pix.width; - /* Configure the default format information */ - vpif_config_format(ch); ret = v4l2_device_call_until_err(&vpif_obj.v4l2_dev, 1, video, s_std_output, std_id); @@ -1087,7 +718,7 @@ static int vpif_s_std(struct file *file, void *priv, v4l2_std_id std_id) return ret; } - ret = v4l2_device_call_until_err(&vpif_obj.v4l2_dev, 1, core, + ret = v4l2_device_call_until_err(&vpif_obj.v4l2_dev, 1, video, s_std, std_id); if (ret < 0) vpif_err("Failed to set standard for sub devices\n"); @@ -1096,132 +727,21 @@ static int vpif_s_std(struct file *file, void *priv, v4l2_std_id std_id) static int vpif_g_std(struct file *file, void *priv, v4l2_std_id *std) { - struct vpif_fh *fh = priv; - struct channel_obj *ch = fh->channel; - - *std = ch->video.stdid; - return 0; -} - -static int vpif_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p) -{ - struct vpif_fh *fh = priv; - struct channel_obj *ch = fh->channel; - struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; - - return vb2_dqbuf(&common->buffer_queue, p, - (file->f_flags & O_NONBLOCK)); -} - -static int vpif_streamon(struct file *file, void *priv, - enum v4l2_buf_type buftype) -{ - struct vpif_fh *fh = priv; - struct channel_obj *ch = fh->channel; - struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; - struct channel_obj *oth_ch = vpif_obj.dev[!ch->channel_id]; - int ret = 0; - - if (buftype != V4L2_BUF_TYPE_VIDEO_OUTPUT) { - vpif_err("buffer type not supported\n"); - return -EINVAL; - } - - if (!fh->io_allowed[VPIF_VIDEO_INDEX]) { - vpif_err("fh->io_allowed\n"); - return -EACCES; - } - - /* If Streaming is already started, return error */ - if (common->started) { - vpif_err("channel->started\n"); - return -EBUSY; - } - - if ((ch->channel_id == VPIF_CHANNEL2_VIDEO - && oth_ch->common[VPIF_VIDEO_INDEX].started && - ch->vpifparams.std_info.ycmux_mode == 0) - || ((ch->channel_id == VPIF_CHANNEL3_VIDEO) - && (2 == oth_ch->common[VPIF_VIDEO_INDEX].started))) { - vpif_err("other channel is using\n"); - return -EBUSY; - } - - ret = vpif_check_format(ch, &common->fmt.fmt.pix); - if (ret < 0) - return ret; - - /* Call vb2_streamon to start streaming in videobuf2 */ - ret = vb2_streamon(&common->buffer_queue, buftype); - if (ret < 0) { - vpif_err("vb2_streamon\n"); - return ret; - } - - return ret; -} - -static int vpif_streamoff(struct file *file, void *priv, - enum v4l2_buf_type buftype) -{ - struct vpif_fh *fh = priv; - struct channel_obj *ch = fh->channel; - struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; - struct vpif_display_config *vpif_config_data = - vpif_dev->platform_data; - - if (buftype != V4L2_BUF_TYPE_VIDEO_OUTPUT) { - vpif_err("buffer type not supported\n"); - return -EINVAL; - } - - if (!fh->io_allowed[VPIF_VIDEO_INDEX]) { - vpif_err("fh->io_allowed\n"); - return -EACCES; - } - - if (!common->started) { - vpif_err("channel->started\n"); - return -EINVAL; - } - - if (buftype == V4L2_BUF_TYPE_VIDEO_OUTPUT) { - /* disable channel */ - if (VPIF_CHANNEL2_VIDEO == ch->channel_id) { - if (vpif_config_data-> - chan_config[VPIF_CHANNEL2_VIDEO].clip_en) - channel2_clipping_enable(0); - enable_channel2(0); - channel2_intr_enable(0); - } - if ((VPIF_CHANNEL3_VIDEO == ch->channel_id) || - (2 == common->started)) { - if (vpif_config_data-> - chan_config[VPIF_CHANNEL3_VIDEO].clip_en) - channel3_clipping_enable(0); - enable_channel3(0); - channel3_intr_enable(0); - } - } - - common->started = 0; - return vb2_streamoff(&common->buffer_queue, buftype); -} + struct vpif_display_config *config = vpif_dev->platform_data; + struct video_device *vdev = video_devdata(file); + struct channel_obj *ch = video_get_drvdata(vdev); + struct vpif_display_chan_config *chan_cfg; + struct v4l2_output output; -static int vpif_cropcap(struct file *file, void *priv, - struct v4l2_cropcap *crop) -{ - struct vpif_fh *fh = priv; - struct channel_obj *ch = fh->channel; - struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; - if (V4L2_BUF_TYPE_VIDEO_OUTPUT != crop->type) - return -EINVAL; + if (config->chan_config[ch->channel_id].outputs == NULL) + return -ENODATA; - crop->bounds.left = crop->bounds.top = 0; - crop->defrect.left = crop->defrect.top = 0; - crop->defrect.height = crop->bounds.height = common->height; - crop->defrect.width = crop->bounds.width = common->width; + chan_cfg = &config->chan_config[ch->channel_id]; + output = chan_cfg->outputs[ch->output_idx].output; + if (output.capabilities != V4L2_OUT_CAP_STD) + return -ENODATA; + *std = ch->video.stdid; return 0; } @@ -1230,9 +750,9 @@ static int vpif_enum_output(struct file *file, void *fh, { struct vpif_display_config *config = vpif_dev->platform_data; + struct video_device *vdev = video_devdata(file); + struct channel_obj *ch = video_get_drvdata(vdev); struct vpif_display_chan_config *chan_cfg; - struct vpif_fh *vpif_handler = fh; - struct channel_obj *ch = vpif_handler->channel; chan_cfg = &config->chan_config[ch->channel_id]; if (output->index >= chan_cfg->output_count) { @@ -1326,52 +846,32 @@ static int vpif_set_output(struct vpif_display_config *vpif_cfg, static int vpif_s_output(struct file *file, void *priv, unsigned int i) { struct vpif_display_config *config = vpif_dev->platform_data; + struct video_device *vdev = video_devdata(file); + struct channel_obj *ch = video_get_drvdata(vdev); struct vpif_display_chan_config *chan_cfg; - struct vpif_fh *fh = priv; - struct channel_obj *ch = fh->channel; struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; + if (vb2_is_busy(&common->buffer_queue)) + return -EBUSY; + chan_cfg = &config->chan_config[ch->channel_id]; if (i >= chan_cfg->output_count) return -EINVAL; - if (common->started) { - vpif_err("Streaming in progress\n"); - return -EBUSY; - } - return vpif_set_output(config, ch, i); } static int vpif_g_output(struct file *file, void *priv, unsigned int *i) { - struct vpif_fh *fh = priv; - struct channel_obj *ch = fh->channel; + struct video_device *vdev = video_devdata(file); + struct channel_obj *ch = video_get_drvdata(vdev); *i = ch->output_idx; return 0; } -static int vpif_g_priority(struct file *file, void *priv, enum v4l2_priority *p) -{ - struct vpif_fh *fh = priv; - struct channel_obj *ch = fh->channel; - - *p = v4l2_prio_max(&ch->prio); - - return 0; -} - -static int vpif_s_priority(struct file *file, void *priv, enum v4l2_priority p) -{ - struct vpif_fh *fh = priv; - struct channel_obj *ch = fh->channel; - - return v4l2_prio_change(&ch->prio, &fh->prio, p); -} - /** * vpif_enum_dv_timings() - ENUM_DV_TIMINGS handler * @file: file ptr @@ -1382,11 +882,24 @@ static int vpif_enum_dv_timings(struct file *file, void *priv, struct v4l2_enum_dv_timings *timings) { - struct vpif_fh *fh = priv; - struct channel_obj *ch = fh->channel; + struct vpif_display_config *config = vpif_dev->platform_data; + struct video_device *vdev = video_devdata(file); + struct channel_obj *ch = video_get_drvdata(vdev); + struct vpif_display_chan_config *chan_cfg; + struct v4l2_output output; int ret; - ret = v4l2_subdev_call(ch->sd, video, enum_dv_timings, timings); + if (config->chan_config[ch->channel_id].outputs == NULL) + return -ENODATA; + + chan_cfg = &config->chan_config[ch->channel_id]; + output = chan_cfg->outputs[ch->output_idx].output; + if (output.capabilities != V4L2_OUT_CAP_DV_TIMINGS) + return -ENODATA; + + timings->pad = 0; + + ret = v4l2_subdev_call(ch->sd, pad, enum_dv_timings, timings); if (ret == -ENOIOCTLCMD || ret == -ENODEV) return -EINVAL; return ret; @@ -1401,14 +914,29 @@ vpif_enum_dv_timings(struct file *file, void *priv, static int vpif_s_dv_timings(struct file *file, void *priv, struct v4l2_dv_timings *timings) { - struct vpif_fh *fh = priv; - struct channel_obj *ch = fh->channel; + struct vpif_display_config *config = vpif_dev->platform_data; + struct video_device *vdev = video_devdata(file); + struct channel_obj *ch = video_get_drvdata(vdev); struct vpif_params *vpifparams = &ch->vpifparams; + struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; struct vpif_channel_config_params *std_info = &vpifparams->std_info; struct video_obj *vid_ch = &ch->video; struct v4l2_bt_timings *bt = &vid_ch->dv_timings.bt; + struct vpif_display_chan_config *chan_cfg; + struct v4l2_output output; int ret; + if (config->chan_config[ch->channel_id].outputs == NULL) + return -ENODATA; + + chan_cfg = &config->chan_config[ch->channel_id]; + output = chan_cfg->outputs[ch->output_idx].output; + if (output.capabilities != V4L2_OUT_CAP_DV_TIMINGS) + return -ENODATA; + + if (vb2_is_busy(&common->buffer_queue)) + return -EBUSY; + if (timings->type != V4L2_DV_BT_656_1120) { vpif_dbg(2, debug, "Timing type not defined\n"); return -EINVAL; @@ -1490,13 +1018,27 @@ static int vpif_s_dv_timings(struct file *file, void *priv, static int vpif_g_dv_timings(struct file *file, void *priv, struct v4l2_dv_timings *timings) { - struct vpif_fh *fh = priv; - struct channel_obj *ch = fh->channel; + struct vpif_display_config *config = vpif_dev->platform_data; + struct video_device *vdev = video_devdata(file); + struct channel_obj *ch = video_get_drvdata(vdev); + struct vpif_display_chan_config *chan_cfg; struct video_obj *vid_ch = &ch->video; + struct v4l2_output output; + + if (config->chan_config[ch->channel_id].outputs == NULL) + goto error; + + chan_cfg = &config->chan_config[ch->channel_id]; + output = chan_cfg->outputs[ch->output_idx].output; + + if (output.capabilities != V4L2_OUT_CAP_DV_TIMINGS) + goto error; *timings = vid_ch->dv_timings; return 0; +error: + return -ENODATA; } /* @@ -1516,83 +1058,49 @@ static int vpif_log_status(struct file *filep, void *priv) /* vpif display ioctl operations */ static const struct v4l2_ioctl_ops vpif_ioctl_ops = { - .vidioc_querycap = vpif_querycap, - .vidioc_g_priority = vpif_g_priority, - .vidioc_s_priority = vpif_s_priority, + .vidioc_querycap = vpif_querycap, .vidioc_enum_fmt_vid_out = vpif_enum_fmt_vid_out, - .vidioc_g_fmt_vid_out = vpif_g_fmt_vid_out, - .vidioc_s_fmt_vid_out = vpif_s_fmt_vid_out, - .vidioc_try_fmt_vid_out = vpif_try_fmt_vid_out, - .vidioc_reqbufs = vpif_reqbufs, - .vidioc_querybuf = vpif_querybuf, - .vidioc_qbuf = vpif_qbuf, - .vidioc_dqbuf = vpif_dqbuf, - .vidioc_streamon = vpif_streamon, - .vidioc_streamoff = vpif_streamoff, - .vidioc_s_std = vpif_s_std, + .vidioc_g_fmt_vid_out = vpif_g_fmt_vid_out, + .vidioc_s_fmt_vid_out = vpif_s_fmt_vid_out, + .vidioc_try_fmt_vid_out = vpif_try_fmt_vid_out, + + .vidioc_reqbufs = vb2_ioctl_reqbufs, + .vidioc_create_bufs = vb2_ioctl_create_bufs, + .vidioc_querybuf = vb2_ioctl_querybuf, + .vidioc_qbuf = vb2_ioctl_qbuf, + .vidioc_dqbuf = vb2_ioctl_dqbuf, + .vidioc_expbuf = vb2_ioctl_expbuf, + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, + + .vidioc_s_std = vpif_s_std, .vidioc_g_std = vpif_g_std, + .vidioc_enum_output = vpif_enum_output, .vidioc_s_output = vpif_s_output, .vidioc_g_output = vpif_g_output, - .vidioc_cropcap = vpif_cropcap, - .vidioc_enum_dv_timings = vpif_enum_dv_timings, - .vidioc_s_dv_timings = vpif_s_dv_timings, - .vidioc_g_dv_timings = vpif_g_dv_timings, + + .vidioc_enum_dv_timings = vpif_enum_dv_timings, + .vidioc_s_dv_timings = vpif_s_dv_timings, + .vidioc_g_dv_timings = vpif_g_dv_timings, + .vidioc_log_status = vpif_log_status, }; static const struct v4l2_file_operations vpif_fops = { .owner = THIS_MODULE, - .open = vpif_open, - .release = vpif_release, + .open = v4l2_fh_open, + .release = vb2_fop_release, .unlocked_ioctl = video_ioctl2, - .mmap = vpif_mmap, - .poll = vpif_poll -}; - -static struct video_device vpif_video_template = { - .name = "vpif", - .fops = &vpif_fops, - .ioctl_ops = &vpif_ioctl_ops, + .mmap = vb2_fop_mmap, + .poll = vb2_fop_poll }; /*Configure the channels, buffer sizei, request irq */ static int initialize_vpif(void) { int free_channel_objects_index; - int free_buffer_channel_index; - int free_buffer_index; - int err = 0, i, j; - - /* Default number of buffers should be 3 */ - if ((ch2_numbuffers > 0) && - (ch2_numbuffers < config_params.min_numbuffers)) - ch2_numbuffers = config_params.min_numbuffers; - if ((ch3_numbuffers > 0) && - (ch3_numbuffers < config_params.min_numbuffers)) - ch3_numbuffers = config_params.min_numbuffers; - - /* Set buffer size to min buffers size if invalid buffer size is - * given */ - if (ch2_bufsize < config_params.min_bufsize[VPIF_CHANNEL2_VIDEO]) - ch2_bufsize = - config_params.min_bufsize[VPIF_CHANNEL2_VIDEO]; - if (ch3_bufsize < config_params.min_bufsize[VPIF_CHANNEL3_VIDEO]) - ch3_bufsize = - config_params.min_bufsize[VPIF_CHANNEL3_VIDEO]; - - config_params.numbuffers[VPIF_CHANNEL2_VIDEO] = ch2_numbuffers; - - if (ch2_numbuffers) { - config_params.channel_bufsize[VPIF_CHANNEL2_VIDEO] = - ch2_bufsize; - } - config_params.numbuffers[VPIF_CHANNEL3_VIDEO] = ch3_numbuffers; - - if (ch3_numbuffers) { - config_params.channel_bufsize[VPIF_CHANNEL3_VIDEO] = - ch3_bufsize; - } + int err, i, j; /* Allocate memory for six channel objects */ for (i = 0; i < VPIF_DISPLAY_MAX_DEVICES; i++) { @@ -1606,10 +1114,6 @@ static int initialize_vpif(void) } } - free_channel_objects_index = VPIF_DISPLAY_MAX_DEVICES; - free_buffer_channel_index = VPIF_DISPLAY_NUM_CHANNELS; - free_buffer_index = config_params.numbuffers[i - 1]; - return 0; vpif_init_free_channel_objects: @@ -1638,21 +1142,18 @@ static int vpif_async_bound(struct v4l2_async_notifier *notifier, static int vpif_probe_complete(void) { struct common_obj *common; + struct video_device *vdev; struct channel_obj *ch; + struct vb2_queue *q; int j, err, k; for (j = 0; j < VPIF_DISPLAY_MAX_DEVICES; j++) { ch = vpif_obj.dev[j]; /* Initialize field of the channel objects */ - atomic_set(&ch->usrs, 0); for (k = 0; k < VPIF_NUMOBJECTS; k++) { - ch->common[k].numbuffers = 0; common = &ch->common[k]; - common->io_usrs = 0; - common->started = 0; spin_lock_init(&common->irqlock); mutex_init(&common->lock); - common->numbuffers = 0; common->set_addr = NULL; common->ytop_off = 0; common->ybtm_off = 0; @@ -1661,38 +1162,71 @@ static int vpif_probe_complete(void) common->cur_frm = NULL; common->next_frm = NULL; memset(&common->fmt, 0, sizeof(common->fmt)); - common->numbuffers = config_params.numbuffers[k]; } ch->initialized = 0; if (vpif_obj.config->subdev_count) ch->sd = vpif_obj.sd[0]; ch->channel_id = j; - if (j < 2) - ch->common[VPIF_VIDEO_INDEX].numbuffers = - config_params.numbuffers[ch->channel_id]; - else - ch->common[VPIF_VIDEO_INDEX].numbuffers = 0; memset(&ch->vpifparams, 0, sizeof(ch->vpifparams)); - /* Initialize prio member of channel object */ - v4l2_prio_init(&ch->prio); ch->common[VPIF_VIDEO_INDEX].fmt.type = V4L2_BUF_TYPE_VIDEO_OUTPUT; - ch->video_dev->lock = &common->lock; - video_set_drvdata(ch->video_dev, ch); /* select output 0 */ err = vpif_set_output(vpif_obj.config, ch, 0); if (err) goto probe_out; + /* set initial format */ + ch->video.stdid = V4L2_STD_525_60; + memset(&ch->video.dv_timings, 0, sizeof(ch->video.dv_timings)); + vpif_update_resolution(ch); + + /* Initialize vb2 queue */ + q = &common->buffer_queue; + q->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF; + q->drv_priv = ch; + q->ops = &video_qops; + q->mem_ops = &vb2_dma_contig_memops; + q->buf_struct_size = sizeof(struct vpif_disp_buffer); + q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + q->min_buffers_needed = 1; + q->lock = &common->lock; + err = vb2_queue_init(q); + if (err) { + vpif_err("vpif_display: vb2_queue_init() failed\n"); + goto probe_out; + } + + common->alloc_ctx = vb2_dma_contig_init_ctx(vpif_dev); + if (IS_ERR(common->alloc_ctx)) { + vpif_err("Failed to get the context\n"); + err = PTR_ERR(common->alloc_ctx); + goto probe_out; + } + + INIT_LIST_HEAD(&common->dma_queue); + /* register video device */ vpif_dbg(1, debug, "channel=%x,channel->video_dev=%x\n", (int)ch, (int)&ch->video_dev); - err = video_register_device(ch->video_dev, - VFL_TYPE_GRABBER, (j ? 3 : 2)); + /* Initialize the video_device structure */ + vdev = ch->video_dev; + strlcpy(vdev->name, VPIF_DRIVER_NAME, sizeof(vdev->name)); + vdev->release = video_device_release; + vdev->fops = &vpif_fops; + vdev->ioctl_ops = &vpif_ioctl_ops; + vdev->v4l2_dev = &vpif_obj.v4l2_dev; + vdev->vfl_dir = VFL_DIR_TX; + vdev->queue = q; + vdev->lock = &common->lock; + set_bit(V4L2_FL_USE_FH_PRIO, &vdev->flags); + video_set_drvdata(ch->video_dev, ch); + err = video_register_device(vdev, VFL_TYPE_GRABBER, + (j ? 3 : 2)); if (err < 0) goto probe_out; } @@ -1702,6 +1236,8 @@ static int vpif_probe_complete(void) probe_out: for (k = 0; k < j; k++) { ch = vpif_obj.dev[k]; + common = &ch->common[k]; + vb2_dma_contig_cleanup_ctx(common->alloc_ctx); video_unregister_device(ch->video_dev); video_device_release(ch->video_dev); ch->video_dev = NULL; @@ -1728,7 +1264,6 @@ static __init int vpif_probe(struct platform_device *pdev) struct video_device *vfd; struct resource *res; int subdev_count; - size_t size; vpif_dev = &pdev->dev; err = initialize_vpif(); @@ -1746,7 +1281,7 @@ static __init int vpif_probe(struct platform_device *pdev) while ((res = platform_get_resource(pdev, IORESOURCE_IRQ, res_idx))) { err = devm_request_irq(&pdev->dev, res->start, vpif_channel_isr, - IRQF_SHARED, "VPIF_Display", + IRQF_SHARED, VPIF_DRIVER_NAME, (void *)(&vpif_obj.dev[res_idx]-> channel_id)); if (err) { @@ -1772,36 +1307,10 @@ static __init int vpif_probe(struct platform_device *pdev) goto vpif_unregister; } - /* Initialize field of video device */ - *vfd = vpif_video_template; - vfd->v4l2_dev = &vpif_obj.v4l2_dev; - vfd->release = video_device_release; - vfd->vfl_dir = VFL_DIR_TX; - snprintf(vfd->name, sizeof(vfd->name), - "VPIF_Display_DRIVER_V%s", - VPIF_DISPLAY_VERSION); - /* Set video_dev to the video device */ ch->video_dev = vfd; } - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (res) { - size = resource_size(res); - /* The resources are divided into two equal memory and when - * we have HD output we can add them together - */ - for (j = 0; j < VPIF_DISPLAY_MAX_DEVICES; j++) { - ch = vpif_obj.dev[j]; - ch->channel_id = j; - - /* only enabled if second resource exists */ - config_params.video_limit[ch->channel_id] = 0; - if (size) - config_params.video_limit[ch->channel_id] = - size/2; - } - } vpif_obj.config = pdev->dev.platform_data; subdev_count = vpif_obj.config->subdev_count; subdevdata = vpif_obj.config->subdevinfo; @@ -1867,6 +1376,7 @@ vpif_unregister: */ static int vpif_remove(struct platform_device *device) { + struct common_obj *common; struct channel_obj *ch; int i; @@ -1877,6 +1387,8 @@ static int vpif_remove(struct platform_device *device) for (i = 0; i < VPIF_DISPLAY_MAX_DEVICES; i++) { /* Get the pointer to the channel object */ ch = vpif_obj.dev[i]; + common = &ch->common[i]; + vb2_dma_contig_cleanup_ctx(common->alloc_ctx); /* Unregister video device */ video_unregister_device(ch->video_dev); @@ -1887,7 +1399,7 @@ static int vpif_remove(struct platform_device *device) return 0; } -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP static int vpif_suspend(struct device *dev) { struct common_obj *common; @@ -1898,18 +1410,20 @@ static int vpif_suspend(struct device *dev) /* Get the pointer to the channel object */ ch = vpif_obj.dev[i]; common = &ch->common[VPIF_VIDEO_INDEX]; + + if (!vb2_is_streaming(&common->buffer_queue)) + continue; + mutex_lock(&common->lock); - if (atomic_read(&ch->usrs) && common->io_usrs) { - /* Disable channel */ - if (ch->channel_id == VPIF_CHANNEL2_VIDEO) { - enable_channel2(0); - channel2_intr_enable(0); - } - if (ch->channel_id == VPIF_CHANNEL3_VIDEO || - common->started == 2) { - enable_channel3(0); - channel3_intr_enable(0); - } + /* Disable channel */ + if (ch->channel_id == VPIF_CHANNEL2_VIDEO) { + enable_channel2(0); + channel2_intr_enable(0); + } + if (ch->channel_id == VPIF_CHANNEL3_VIDEO || + ycmux_mode == 2) { + enable_channel3(0); + channel3_intr_enable(0); } mutex_unlock(&common->lock); } @@ -1928,18 +1442,20 @@ static int vpif_resume(struct device *dev) /* Get the pointer to the channel object */ ch = vpif_obj.dev[i]; common = &ch->common[VPIF_VIDEO_INDEX]; + + if (!vb2_is_streaming(&common->buffer_queue)) + continue; + mutex_lock(&common->lock); - if (atomic_read(&ch->usrs) && common->io_usrs) { - /* Enable channel */ - if (ch->channel_id == VPIF_CHANNEL2_VIDEO) { - enable_channel2(1); - channel2_intr_enable(1); - } - if (ch->channel_id == VPIF_CHANNEL3_VIDEO || - common->started == 2) { - enable_channel3(1); - channel3_intr_enable(1); - } + /* Enable channel */ + if (ch->channel_id == VPIF_CHANNEL2_VIDEO) { + enable_channel2(1); + channel2_intr_enable(1); + } + if (ch->channel_id == VPIF_CHANNEL3_VIDEO || + ycmux_mode == 2) { + enable_channel3(1); + channel3_intr_enable(1); } mutex_unlock(&common->lock); } @@ -1947,21 +1463,15 @@ static int vpif_resume(struct device *dev) return 0; } -static const struct dev_pm_ops vpif_pm = { - .suspend = vpif_suspend, - .resume = vpif_resume, -}; - -#define vpif_pm_ops (&vpif_pm) -#else -#define vpif_pm_ops NULL #endif +static SIMPLE_DEV_PM_OPS(vpif_pm_ops, vpif_suspend, vpif_resume); + static __refdata struct platform_driver vpif_driver = { .driver = { - .name = "vpif_display", + .name = VPIF_DRIVER_NAME, .owner = THIS_MODULE, - .pm = vpif_pm_ops, + .pm = &vpif_pm_ops, }, .probe = vpif_probe, .remove = vpif_remove, diff --git a/drivers/media/platform/davinci/vpif_display.h b/drivers/media/platform/davinci/vpif_display.h index 4d0485b99a80..7b21a7607674 100644 --- a/drivers/media/platform/davinci/vpif_display.h +++ b/drivers/media/platform/davinci/vpif_display.h @@ -13,8 +13,8 @@ * GNU General Public License for more details. */ -#ifndef DAVINCIHD_DISPLAY_H -#define DAVINCIHD_DISPLAY_H +#ifndef VPIF_DISPLAY_H +#define VPIF_DISPLAY_H /* Header files */ #include <media/videobuf2-dma-contig.h> @@ -67,17 +67,10 @@ struct vpif_disp_buffer { }; struct common_obj { - /* Buffer specific parameters */ - u8 *fbuffers[VIDEO_MAX_FRAME]; /* List of buffer pointers for - * storing frames */ - u32 numbuffers; /* number of buffers */ struct vpif_disp_buffer *cur_frm; /* Pointer pointing to current * vb2_buffer */ struct vpif_disp_buffer *next_frm; /* Pointer pointing to next * vb2_buffer */ - enum v4l2_memory memory; /* This field keeps track of - * type of buffer exchange - * method user has selected */ struct v4l2_format fmt; /* Used to store the format */ struct vb2_queue buffer_queue; /* Buffer queue used in * video-buf */ @@ -90,10 +83,6 @@ struct common_obj { /* channel specific parameters */ struct mutex lock; /* lock used to access this * structure */ - u32 io_usrs; /* number of users performing - * IO */ - u8 started; /* Indicates whether streaming - * started */ u32 ytop_off; /* offset of Y top from the * starting of the buffer */ u32 ybtm_off; /* offset of Y bottom from the @@ -103,7 +92,7 @@ struct common_obj { u32 cbtm_off; /* offset of C bottom from the * starting of the buffer */ /* Function pointer to set the addresses */ - void (*set_addr) (unsigned long, unsigned long, + void (*set_addr)(unsigned long, unsigned long, unsigned long, unsigned long); u32 height; u32 width; @@ -113,10 +102,6 @@ struct channel_obj { /* V4l2 specific parameters */ struct video_device *video_dev; /* Identifies video device for * this channel */ - struct v4l2_prio_state prio; /* Used to keep track of state of - * the priority */ - atomic_t usrs; /* number of open instances of - * the channel */ u32 field_id; /* Indicates id of the field * which is being displayed */ u8 initialized; /* flag to indicate whether @@ -130,19 +115,6 @@ struct channel_obj { struct video_obj video; }; -/* File handle structure */ -struct vpif_fh { - struct channel_obj *channel; /* pointer to channel object for - * opened device */ - u8 io_allowed[VPIF_NUMOBJECTS]; /* Indicates whether this file handle - * is doing IO */ - enum v4l2_priority prio; /* Used to keep track priority of - * this instance */ - u8 initialized; /* Used to keep track of whether this - * file handle has initialized - * channel or not */ -}; - /* vpif device structure */ struct vpif_device { struct v4l2_device v4l2_dev; @@ -152,12 +124,4 @@ struct vpif_device { struct vpif_display_config *config; }; -struct vpif_config_params { - u32 min_bufsize[VPIF_DISPLAY_NUM_CHANNELS]; - u32 channel_bufsize[VPIF_DISPLAY_NUM_CHANNELS]; - u8 numbuffers[VPIF_DISPLAY_NUM_CHANNELS]; - u32 video_limit[VPIF_DISPLAY_NUM_CHANNELS]; - u8 min_numbuffers; -}; - -#endif /* DAVINCIHD_DISPLAY_H */ +#endif /* VPIF_DISPLAY_H */ diff --git a/drivers/media/platform/exynos-gsc/gsc-m2m.c b/drivers/media/platform/exynos-gsc/gsc-m2m.c index d0ea94f58d6f..e434f1f03d7b 100644 --- a/drivers/media/platform/exynos-gsc/gsc-m2m.c +++ b/drivers/media/platform/exynos-gsc/gsc-m2m.c @@ -66,15 +66,13 @@ static int gsc_m2m_start_streaming(struct vb2_queue *q, unsigned int count) return ret > 0 ? 0 : ret; } -static int gsc_m2m_stop_streaming(struct vb2_queue *q) +static void gsc_m2m_stop_streaming(struct vb2_queue *q) { struct gsc_ctx *ctx = q->drv_priv; __gsc_m2m_job_abort(ctx); pm_runtime_put(&ctx->gsc_dev->pdev->dev); - - return 0; } void gsc_m2m_job_finish(struct gsc_ctx *ctx, int vb_state) diff --git a/drivers/media/platform/exynos4-is/Kconfig b/drivers/media/platform/exynos4-is/Kconfig index e1b2ceba00c1..5dcaa0a80540 100644 --- a/drivers/media/platform/exynos4-is/Kconfig +++ b/drivers/media/platform/exynos4-is/Kconfig @@ -3,6 +3,7 @@ config VIDEO_SAMSUNG_EXYNOS4_IS bool "Samsung S5P/EXYNOS4 SoC series Camera Subsystem driver" depends on VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API depends on (PLAT_S5P || ARCH_EXYNOS) + depends on OF && COMMON_CLK help Say Y here to enable camera host interface devices for Samsung S5P and EXYNOS SoC series. @@ -17,7 +18,7 @@ config VIDEO_S5P_FIMC depends on I2C select VIDEOBUF2_DMA_CONTIG select V4L2_MEM2MEM_DEV - select MFD_SYSCON if OF + select MFD_SYSCON select VIDEO_EXYNOS4_IS_COMMON help This is a V4L2 driver for Samsung S5P and EXYNOS4 SoC camera host diff --git a/drivers/media/platform/exynos4-is/common.c b/drivers/media/platform/exynos4-is/common.c index 0ec210b4da1d..0eb34ecb8ee4 100644 --- a/drivers/media/platform/exynos4-is/common.c +++ b/drivers/media/platform/exynos4-is/common.c @@ -10,7 +10,7 @@ */ #include <linux/module.h> -#include <media/s5p_fimc.h> +#include <media/exynos-fimc.h> #include "common.h" /* Called with the media graph mutex held or entity->stream_count > 0. */ diff --git a/drivers/media/platform/exynos4-is/fimc-capture.c b/drivers/media/platform/exynos4-is/fimc-capture.c index 92ae812abce2..3d2babd5067a 100644 --- a/drivers/media/platform/exynos4-is/fimc-capture.c +++ b/drivers/media/platform/exynos4-is/fimc-capture.c @@ -294,15 +294,15 @@ static int start_streaming(struct vb2_queue *q, unsigned int count) return 0; } -static int stop_streaming(struct vb2_queue *q) +static void stop_streaming(struct vb2_queue *q) { struct fimc_ctx *ctx = q->drv_priv; struct fimc_dev *fimc = ctx->fimc_dev; if (!fimc_capture_active(fimc)) - return -EINVAL; + return; - return fimc_stop_capture(fimc, false); + fimc_stop_capture(fimc, false); } int fimc_capture_suspend(struct fimc_dev *fimc) diff --git a/drivers/media/platform/exynos4-is/fimc-core.c b/drivers/media/platform/exynos4-is/fimc-core.c index 25dbf5b05a96..b70fd996d794 100644 --- a/drivers/media/platform/exynos4-is/fimc-core.c +++ b/drivers/media/platform/exynos4-is/fimc-core.c @@ -56,8 +56,8 @@ static struct fimc_fmt fimc_formats[] = { .colplanes = 1, .flags = FMT_FLAGS_M2M, }, { - .name = "ARGB8888, 32 bpp", - .fourcc = V4L2_PIX_FMT_RGB32, + .name = "BGRA8888, 32 bpp", + .fourcc = V4L2_PIX_FMT_BGR32, .depth = { 32 }, .color = FIMC_FMT_RGB888, .memplanes = 1, @@ -450,7 +450,7 @@ void fimc_prepare_dma_offset(struct fimc_ctx *ctx, struct fimc_frame *f) bool pix_hoff = ctx->fimc_dev->drv_data->dma_pix_hoff; u32 i, depth = 0; - for (i = 0; i < f->fmt->colplanes; i++) + for (i = 0; i < f->fmt->memplanes; i++) depth += f->fmt->depth[i]; f->dma_offset.y_h = f->offs_h; diff --git a/drivers/media/platform/exynos4-is/fimc-core.h b/drivers/media/platform/exynos4-is/fimc-core.h index 1790fb4e32ea..6c75c6ced1f7 100644 --- a/drivers/media/platform/exynos4-is/fimc-core.h +++ b/drivers/media/platform/exynos4-is/fimc-core.h @@ -27,7 +27,7 @@ #include <media/v4l2-device.h> #include <media/v4l2-mem2mem.h> #include <media/v4l2-mediabus.h> -#include <media/s5p_fimc.h> +#include <media/exynos-fimc.h> #define dbg(fmt, args...) \ pr_debug("%s:%d: " fmt "\n", __func__, __LINE__, ##args) diff --git a/drivers/media/platform/exynos4-is/fimc-is.c b/drivers/media/platform/exynos4-is/fimc-is.c index 128b73b6cce2..5476dce3ad29 100644 --- a/drivers/media/platform/exynos4-is/fimc-is.c +++ b/drivers/media/platform/exynos4-is/fimc-is.c @@ -367,6 +367,9 @@ static void fimc_is_free_cpu_memory(struct fimc_is *is) { struct device *dev = &is->pdev->dev; + if (is->memory.vaddr == NULL) + return; + dma_free_coherent(dev, is->memory.size, is->memory.vaddr, is->memory.paddr); } diff --git a/drivers/media/platform/exynos4-is/fimc-isp-video.c b/drivers/media/platform/exynos4-is/fimc-isp-video.c index e92b4e115adb..93f9cf2ebcd6 100644 --- a/drivers/media/platform/exynos4-is/fimc-isp-video.c +++ b/drivers/media/platform/exynos4-is/fimc-isp-video.c @@ -30,7 +30,7 @@ #include <media/v4l2-ioctl.h> #include <media/videobuf2-core.h> #include <media/videobuf2-dma-contig.h> -#include <media/s5p_fimc.h> +#include <media/exynos-fimc.h> #include "common.h" #include "media-dev.h" @@ -125,7 +125,7 @@ static int isp_video_capture_start_streaming(struct vb2_queue *q, return ret; } -static int isp_video_capture_stop_streaming(struct vb2_queue *q) +static void isp_video_capture_stop_streaming(struct vb2_queue *q) { struct fimc_isp *isp = vb2_get_drv_priv(q); struct fimc_is *is = fimc_isp_to_is(isp); @@ -134,7 +134,7 @@ static int isp_video_capture_stop_streaming(struct vb2_queue *q) ret = fimc_pipeline_call(&isp->video_capture.ve, set_stream, 0); if (ret < 0) - return ret; + return; dma->cmd = DMA_OUTPUT_COMMAND_DISABLE; dma->notify_dma_done = DMA_OUTPUT_NOTIFY_DMA_DONE_DISABLE; @@ -155,7 +155,6 @@ static int isp_video_capture_stop_streaming(struct vb2_queue *q) clear_bit(ST_ISP_VID_CAP_STREAMING, &isp->state); isp->video_capture.buf_count = 0; - return 0; } static int isp_video_capture_buffer_prepare(struct vb2_buffer *vb) diff --git a/drivers/media/platform/exynos4-is/fimc-isp.h b/drivers/media/platform/exynos4-is/fimc-isp.h index 4dc55a18d978..b99be09b49fc 100644 --- a/drivers/media/platform/exynos4-is/fimc-isp.h +++ b/drivers/media/platform/exynos4-is/fimc-isp.h @@ -24,7 +24,7 @@ #include <media/videobuf2-core.h> #include <media/v4l2-device.h> #include <media/v4l2-mediabus.h> -#include <media/s5p_fimc.h> +#include <media/exynos-fimc.h> extern int fimc_isp_debug; diff --git a/drivers/media/platform/exynos4-is/fimc-lite-reg.c b/drivers/media/platform/exynos4-is/fimc-lite-reg.c index d0dc7ee04452..bc3ec7d25a32 100644 --- a/drivers/media/platform/exynos4-is/fimc-lite-reg.c +++ b/drivers/media/platform/exynos4-is/fimc-lite-reg.c @@ -12,7 +12,7 @@ #include <linux/bitops.h> #include <linux/delay.h> #include <linux/io.h> -#include <media/s5p_fimc.h> +#include <media/exynos-fimc.h> #include "fimc-lite-reg.h" #include "fimc-lite.h" diff --git a/drivers/media/platform/exynos4-is/fimc-lite.c b/drivers/media/platform/exynos4-is/fimc-lite.c index 3ad660b55b6b..a97d2352f1d7 100644 --- a/drivers/media/platform/exynos4-is/fimc-lite.c +++ b/drivers/media/platform/exynos4-is/fimc-lite.c @@ -30,7 +30,7 @@ #include <media/v4l2-mem2mem.h> #include <media/videobuf2-core.h> #include <media/videobuf2-dma-contig.h> -#include <media/s5p_fimc.h> +#include <media/exynos-fimc.h> #include "common.h" #include "fimc-core.h" @@ -350,14 +350,14 @@ static int start_streaming(struct vb2_queue *q, unsigned int count) return 0; } -static int stop_streaming(struct vb2_queue *q) +static void stop_streaming(struct vb2_queue *q) { struct fimc_lite *fimc = q->drv_priv; if (!fimc_lite_active(fimc)) - return -EINVAL; + return; - return fimc_lite_stop_capture(fimc, false); + fimc_lite_stop_capture(fimc, false); } static int queue_setup(struct vb2_queue *vq, const struct v4l2_format *pfmt, diff --git a/drivers/media/platform/exynos4-is/fimc-lite.h b/drivers/media/platform/exynos4-is/fimc-lite.h index 7428b2d22b52..ea19dc7be63e 100644 --- a/drivers/media/platform/exynos4-is/fimc-lite.h +++ b/drivers/media/platform/exynos4-is/fimc-lite.h @@ -23,7 +23,7 @@ #include <media/v4l2-ctrls.h> #include <media/v4l2-device.h> #include <media/v4l2-mediabus.h> -#include <media/s5p_fimc.h> +#include <media/exynos-fimc.h> #define FIMC_LITE_DRV_NAME "exynos-fimc-lite" #define FLITE_CLK_NAME "flite" diff --git a/drivers/media/platform/exynos4-is/fimc-m2m.c b/drivers/media/platform/exynos4-is/fimc-m2m.c index 36971d915b53..0ad1b6f84a27 100644 --- a/drivers/media/platform/exynos4-is/fimc-m2m.c +++ b/drivers/media/platform/exynos4-is/fimc-m2m.c @@ -85,7 +85,7 @@ static int start_streaming(struct vb2_queue *q, unsigned int count) return ret > 0 ? 0 : ret; } -static int stop_streaming(struct vb2_queue *q) +static void stop_streaming(struct vb2_queue *q) { struct fimc_ctx *ctx = q->drv_priv; int ret; @@ -95,7 +95,6 @@ static int stop_streaming(struct vb2_queue *q) fimc_m2m_job_finish(ctx, VB2_BUF_STATE_ERROR); pm_runtime_put(&ctx->fimc_dev->pdev->dev); - return 0; } static void fimc_device_run(void *priv) @@ -197,7 +196,7 @@ static int fimc_queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt, *num_planes = f->fmt->memplanes; for (i = 0; i < f->fmt->memplanes; i++) { - sizes[i] = (f->f_width * f->f_height * f->fmt->depth[i]) / 8; + sizes[i] = f->payload[i]; allocators[i] = ctx->fimc_dev->alloc_ctx; } return 0; @@ -342,7 +341,7 @@ static void __set_frame_format(struct fimc_frame *frame, struct fimc_fmt *fmt, { int i; - for (i = 0; i < fmt->colplanes; i++) { + for (i = 0; i < fmt->memplanes; i++) { frame->bytesperline[i] = pixm->plane_fmt[i].bytesperline; frame->payload[i] = pixm->plane_fmt[i].sizeimage; } @@ -461,7 +460,7 @@ static int fimc_m2m_try_crop(struct fimc_ctx *ctx, struct v4l2_crop *cr) else halign = ffs(fimc->variant->min_vsize_align) - 1; - for (i = 0; i < f->fmt->colplanes; i++) + for (i = 0; i < f->fmt->memplanes; i++) depth += f->fmt->depth[i]; v4l_bound_align_image(&cr->c.width, min_size, f->o_width, diff --git a/drivers/media/platform/exynos4-is/fimc-reg.c b/drivers/media/platform/exynos4-is/fimc-reg.c index 1db8cb4c46ef..2d77fd8f440a 100644 --- a/drivers/media/platform/exynos4-is/fimc-reg.c +++ b/drivers/media/platform/exynos4-is/fimc-reg.c @@ -13,7 +13,7 @@ #include <linux/io.h> #include <linux/regmap.h> -#include <media/s5p_fimc.h> +#include <media/exynos-fimc.h> #include "media-dev.h" #include "fimc-reg.h" diff --git a/drivers/media/platform/exynos4-is/media-dev.c b/drivers/media/platform/exynos4-is/media-dev.c index e62211a80f0e..344718df5c62 100644 --- a/drivers/media/platform/exynos4-is/media-dev.c +++ b/drivers/media/platform/exynos4-is/media-dev.c @@ -31,7 +31,7 @@ #include <media/v4l2-ctrls.h> #include <media/v4l2-of.h> #include <media/media-device.h> -#include <media/s5p_fimc.h> +#include <media/exynos-fimc.h> #include "media-dev.h" #include "fimc-core.h" @@ -39,10 +39,6 @@ #include "fimc-lite.h" #include "mipi-csis.h" -static int __fimc_md_set_camclk(struct fimc_md *fmd, - struct fimc_source_info *si, - bool on); - /* Set up image sensor subdev -> FIMC capture node notifications. */ static void __setup_sensor_notification(struct fimc_md *fmd, struct v4l2_subdev *sensor, @@ -223,17 +219,10 @@ static int __fimc_pipeline_open(struct exynos_media_pipeline *ep, return ret; } - ret = fimc_md_set_camclk(sd, true); - if (ret < 0) - goto err_wbclk; - ret = fimc_pipeline_s_power(p, 1); if (!ret) return 0; - fimc_md_set_camclk(sd, false); - -err_wbclk: if (!IS_ERR(fmd->wbclk[CLK_IDX_WB_B]) && p->subdevs[IDX_IS_ISP]) clk_disable_unprepare(fmd->wbclk[CLK_IDX_WB_B]); @@ -259,7 +248,6 @@ static int __fimc_pipeline_close(struct exynos_media_pipeline *ep) } ret = fimc_pipeline_s_power(p, 0); - fimc_md_set_camclk(sd, false); fmd = entity_to_fimc_mdev(&sd->entity); @@ -337,75 +325,14 @@ static void fimc_md_pipelines_free(struct fimc_md *fmd) } } -/* - * Sensor subdevice helper functions - */ -static struct v4l2_subdev *fimc_md_register_sensor(struct fimc_md *fmd, - struct fimc_source_info *si) -{ - struct i2c_adapter *adapter; - struct v4l2_subdev *sd = NULL; - - if (!si || !fmd) - return NULL; - /* - * If FIMC bus type is not Writeback FIFO assume it is same - * as sensor_bus_type. - */ - si->fimc_bus_type = si->sensor_bus_type; - - adapter = i2c_get_adapter(si->i2c_bus_num); - if (!adapter) { - v4l2_warn(&fmd->v4l2_dev, - "Failed to get I2C adapter %d, deferring probe\n", - si->i2c_bus_num); - return ERR_PTR(-EPROBE_DEFER); - } - sd = v4l2_i2c_new_subdev_board(&fmd->v4l2_dev, adapter, - si->board_info, NULL); - if (IS_ERR_OR_NULL(sd)) { - i2c_put_adapter(adapter); - v4l2_warn(&fmd->v4l2_dev, - "Failed to acquire subdev %s, deferring probe\n", - si->board_info->type); - return ERR_PTR(-EPROBE_DEFER); - } - v4l2_set_subdev_hostdata(sd, si); - sd->grp_id = GRP_ID_SENSOR; - - v4l2_info(&fmd->v4l2_dev, "Registered sensor subdevice %s\n", - sd->name); - return sd; -} - -static void fimc_md_unregister_sensor(struct v4l2_subdev *sd) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct i2c_adapter *adapter; - - if (!client || client->dev.of_node) - return; - - v4l2_device_unregister_subdev(sd); - - adapter = client->adapter; - i2c_unregister_device(client); - if (adapter) - i2c_put_adapter(adapter); -} - -#ifdef CONFIG_OF /* Parse port node and register as a sub-device any sensor specified there. */ static int fimc_md_parse_port_node(struct fimc_md *fmd, struct device_node *port, unsigned int index) { + struct fimc_source_info *pd = &fmd->sensor[index].pdata; struct device_node *rem, *ep, *np; - struct fimc_source_info *pd; struct v4l2_of_endpoint endpoint; - u32 val; - - pd = &fmd->sensor[index].pdata; /* Assume here a port node can have only one endpoint node. */ ep = of_get_next_child(port, NULL); @@ -425,20 +352,6 @@ static int fimc_md_parse_port_node(struct fimc_md *fmd, ep->full_name); return 0; } - if (!of_property_read_u32(rem, "samsung,camclk-out", &val)) - pd->clk_id = val; - - if (!of_property_read_u32(rem, "clock-frequency", &val)) - pd->clk_frequency = val; - else - pd->clk_frequency = DEFAULT_SENSOR_CLK_FREQ; - - if (pd->clk_frequency == 0) { - v4l2_err(&fmd->v4l2_dev, "Wrong clock frequency at node %s\n", - rem->full_name); - of_node_put(rem); - return -EINVAL; - } if (fimc_input_is_parallel(endpoint.base.port)) { if (endpoint.bus_type == V4L2_MBUS_PARALLEL) @@ -485,14 +398,26 @@ static int fimc_md_parse_port_node(struct fimc_md *fmd, } /* Register all SoC external sub-devices */ -static int fimc_md_of_sensors_register(struct fimc_md *fmd, - struct device_node *np) +static int fimc_md_register_sensor_entities(struct fimc_md *fmd) { struct device_node *parent = fmd->pdev->dev.of_node; struct device_node *node, *ports; int index = 0; int ret; + /* + * Runtime resume one of the FIMC entities to make sure + * the sclk_cam clocks are not globally disabled. + */ + if (!fmd->pmf) + return -ENXIO; + + ret = pm_runtime_get_sync(fmd->pmf); + if (ret < 0) + return ret; + + fmd->num_sensors = 0; + /* Attach sensors linked to MIPI CSI-2 receivers */ for_each_available_child_of_node(parent, node) { struct device_node *port; @@ -506,14 +431,14 @@ static int fimc_md_of_sensors_register(struct fimc_md *fmd, ret = fimc_md_parse_port_node(fmd, port, index); if (ret < 0) - return ret; + goto rpm_put; index++; } /* Attach sensors listed in the parallel-ports node */ ports = of_get_child_by_name(parent, "parallel-ports"); if (!ports) - return 0; + goto rpm_put; for_each_child_of_node(ports, node) { ret = fimc_md_parse_port_node(fmd, node, index); @@ -521,8 +446,9 @@ static int fimc_md_of_sensors_register(struct fimc_md *fmd, break; index++; } - - return 0; +rpm_put: + pm_runtime_put(fmd->pmf); + return ret; } static int __of_get_csis_id(struct device_node *np) @@ -535,68 +461,10 @@ static int __of_get_csis_id(struct device_node *np) of_property_read_u32(np, "reg", ®); return reg - FIMC_INPUT_MIPI_CSI2_0; } -#else -#define fimc_md_of_sensors_register(fmd, np) (-ENOSYS) -#define __of_get_csis_id(np) (-ENOSYS) -#endif - -static int fimc_md_register_sensor_entities(struct fimc_md *fmd) -{ - struct s5p_platform_fimc *pdata = fmd->pdev->dev.platform_data; - struct device_node *of_node = fmd->pdev->dev.of_node; - int num_clients = 0; - int ret, i; - - /* - * Runtime resume one of the FIMC entities to make sure - * the sclk_cam clocks are not globally disabled. - */ - if (!fmd->pmf) - return -ENXIO; - - ret = pm_runtime_get_sync(fmd->pmf); - if (ret < 0) - return ret; - - if (of_node) { - fmd->num_sensors = 0; - ret = fimc_md_of_sensors_register(fmd, of_node); - } else if (pdata) { - WARN_ON(pdata->num_clients > ARRAY_SIZE(fmd->sensor)); - num_clients = min_t(u32, pdata->num_clients, - ARRAY_SIZE(fmd->sensor)); - fmd->num_sensors = num_clients; - - for (i = 0; i < num_clients; i++) { - struct fimc_sensor_info *si = &fmd->sensor[i]; - struct v4l2_subdev *sd; - - si->pdata = pdata->source_info[i]; - ret = __fimc_md_set_camclk(fmd, &si->pdata, true); - if (ret) - break; - sd = fimc_md_register_sensor(fmd, &si->pdata); - ret = __fimc_md_set_camclk(fmd, &si->pdata, false); - - if (IS_ERR(sd)) { - si->subdev = NULL; - ret = PTR_ERR(sd); - break; - } - si->subdev = sd; - if (ret) - break; - } - } - - pm_runtime_put(fmd->pmf); - return ret; -} /* * MIPI-CSIS, FIMC and FIMC-LITE platform devices registration. */ - static int register_fimc_lite_entity(struct fimc_md *fmd, struct fimc_lite *fimc_lite) { @@ -753,35 +621,9 @@ dev_unlock: return ret; } -static int fimc_md_pdev_match(struct device *dev, void *data) -{ - struct platform_device *pdev = to_platform_device(dev); - int plat_entity = -1; - int ret; - char *p; - - if (!get_device(dev)) - return -ENODEV; - - if (!strcmp(pdev->name, CSIS_DRIVER_NAME)) { - plat_entity = IDX_CSIS; - } else { - p = strstr(pdev->name, "fimc"); - if (p && *(p + 4) == 0) - plat_entity = IDX_FIMC; - } - - if (plat_entity >= 0) - ret = fimc_md_register_platform_entity(data, pdev, - plat_entity); - put_device(dev); - return 0; -} - /* Register FIMC, FIMC-LITE and CSIS media entities */ -#ifdef CONFIG_OF -static int fimc_md_register_of_platform_entities(struct fimc_md *fmd, - struct device_node *parent) +static int fimc_md_register_platform_entities(struct fimc_md *fmd, + struct device_node *parent) { struct device_node *node; int ret = 0; @@ -815,9 +657,6 @@ static int fimc_md_register_of_platform_entities(struct fimc_md *fmd, return ret; } -#else -#define fimc_md_register_of_platform_entities(fmd, node) (-ENOSYS) -#endif static void fimc_md_unregister_entities(struct fimc_md *fmd) { @@ -845,14 +684,6 @@ static void fimc_md_unregister_entities(struct fimc_md *fmd) v4l2_device_unregister_subdev(fmd->csis[i].sd); fmd->csis[i].sd = NULL; } - if (fmd->pdev->dev.of_node == NULL) { - for (i = 0; i < fmd->num_sensors; i++) { - if (fmd->sensor[i].subdev == NULL) - continue; - fimc_md_unregister_sensor(fmd->sensor[i].subdev); - fmd->sensor[i].subdev = NULL; - } - } if (fmd->fimc_is) v4l2_device_unregister_subdev(&fmd->fimc_is->isp.subdev); @@ -1137,7 +968,7 @@ static void fimc_md_put_clocks(struct fimc_md *fmd) static int fimc_md_get_clocks(struct fimc_md *fmd) { - struct device *dev = NULL; + struct device *dev = &fmd->pdev->dev; char clk_name[32]; struct clk *clock; int i, ret = 0; @@ -1145,16 +976,12 @@ static int fimc_md_get_clocks(struct fimc_md *fmd) for (i = 0; i < FIMC_MAX_CAMCLKS; i++) fmd->camclk[i].clock = ERR_PTR(-EINVAL); - if (fmd->pdev->dev.of_node) - dev = &fmd->pdev->dev; - for (i = 0; i < FIMC_MAX_CAMCLKS; i++) { snprintf(clk_name, sizeof(clk_name), "sclk_cam%u", i); clock = clk_get(dev, clk_name); if (IS_ERR(clock)) { - dev_err(&fmd->pdev->dev, "Failed to get clock: %s\n", - clk_name); + dev_err(dev, "Failed to get clock: %s\n", clk_name); ret = PTR_ERR(clock); break; } @@ -1188,86 +1015,6 @@ static int fimc_md_get_clocks(struct fimc_md *fmd) return ret; } -static int __fimc_md_set_camclk(struct fimc_md *fmd, - struct fimc_source_info *si, - bool on) -{ - struct fimc_camclk_info *camclk; - int ret = 0; - - /* - * When device tree is used the sensor drivers are supposed to - * control the clock themselves. This whole function will be - * removed once S5PV210 platform is converted to the device tree. - */ - if (fmd->pdev->dev.of_node) - return 0; - - if (WARN_ON(si->clk_id >= FIMC_MAX_CAMCLKS) || !fmd || !fmd->pmf) - return -EINVAL; - - camclk = &fmd->camclk[si->clk_id]; - - dbg("camclk %d, f: %lu, use_count: %d, on: %d", - si->clk_id, si->clk_frequency, camclk->use_count, on); - - if (on) { - if (camclk->use_count > 0 && - camclk->frequency != si->clk_frequency) - return -EINVAL; - - if (camclk->use_count++ == 0) { - clk_set_rate(camclk->clock, si->clk_frequency); - camclk->frequency = si->clk_frequency; - ret = pm_runtime_get_sync(fmd->pmf); - if (ret < 0) - return ret; - ret = clk_prepare_enable(camclk->clock); - dbg("Enabled camclk %d: f: %lu", si->clk_id, - clk_get_rate(camclk->clock)); - } - return ret; - } - - if (WARN_ON(camclk->use_count == 0)) - return 0; - - if (--camclk->use_count == 0) { - clk_disable_unprepare(camclk->clock); - pm_runtime_put(fmd->pmf); - dbg("Disabled camclk %d", si->clk_id); - } - return ret; -} - -/** - * fimc_md_set_camclk - peripheral sensor clock setup - * @sd: sensor subdev to configure sclk_cam clock for - * @on: 1 to enable or 0 to disable the clock - * - * There are 2 separate clock outputs available in the SoC for external - * image processors. These clocks are shared between all registered FIMC - * devices to which sensors can be attached, either directly or through - * the MIPI CSI receiver. The clock is allowed here to be used by - * multiple sensors concurrently if they use same frequency. - * This function should only be called when the graph mutex is held. - */ -int fimc_md_set_camclk(struct v4l2_subdev *sd, bool on) -{ - struct fimc_source_info *si = v4l2_get_subdev_hostdata(sd); - struct fimc_md *fmd = entity_to_fimc_mdev(&sd->entity); - - /* - * If there is a clock provider registered the sensors will - * handle their clock themselves, no need to control it on - * the host interface side. - */ - if (fmd->clk_provider.num_clocks > 0) - return 0; - - return __fimc_md_set_camclk(fmd, si, on); -} - static int __fimc_md_modify_pipeline(struct media_entity *entity, bool enable) { struct exynos_video_entity *ve; @@ -1426,7 +1173,6 @@ static int fimc_md_get_pinctrl(struct fimc_md *fmd) return 0; } -#ifdef CONFIG_OF static int cam_clk_prepare(struct clk_hw *hw) { struct cam_clk *camclk = to_cam_clk(hw); @@ -1518,10 +1264,6 @@ err: fimc_md_unregister_clk_provider(fmd); return ret; } -#else -#define fimc_md_register_clk_provider(fmd) (0) -#define fimc_md_unregister_clk_provider(fmd) (0) -#endif static int subdev_notifier_bound(struct v4l2_async_notifier *notifier, struct v4l2_subdev *subdev, @@ -1585,8 +1327,8 @@ static int fimc_md_probe(struct platform_device *pdev) return -ENOMEM; spin_lock_init(&fmd->slock); - fmd->pdev = pdev; INIT_LIST_HEAD(&fmd->pipelines); + fmd->pdev = pdev; strlcpy(fmd->media_dev.model, "SAMSUNG S5P FIMC", sizeof(fmd->media_dev.model)); @@ -1599,6 +1341,7 @@ static int fimc_md_probe(struct platform_device *pdev) strlcpy(v4l2_dev->name, "s5p-fimc-md", sizeof(v4l2_dev->name)); fmd->use_isp = fimc_md_is_isp_available(dev->of_node); + fmd->user_subdev_api = true; ret = v4l2_device_register(dev, &fmd->v4l2_dev); if (ret < 0) { @@ -1616,8 +1359,6 @@ static int fimc_md_probe(struct platform_device *pdev) if (ret) goto err_md; - fmd->user_subdev_api = (dev->of_node != NULL); - ret = fimc_md_get_pinctrl(fmd); if (ret < 0) { if (ret != EPROBE_DEFER) @@ -1630,22 +1371,16 @@ static int fimc_md_probe(struct platform_device *pdev) /* Protect the media graph while we're registering entities */ mutex_lock(&fmd->media_dev.graph_mutex); - if (dev->of_node) - ret = fimc_md_register_of_platform_entities(fmd, dev->of_node); - else - ret = bus_for_each_dev(&platform_bus_type, NULL, fmd, - fimc_md_pdev_match); + ret = fimc_md_register_platform_entities(fmd, dev->of_node); if (ret) { mutex_unlock(&fmd->media_dev.graph_mutex); goto err_clk; } - if (dev->platform_data || dev->of_node) { - ret = fimc_md_register_sensor_entities(fmd); - if (ret) { - mutex_unlock(&fmd->media_dev.graph_mutex); - goto err_m_ent; - } + ret = fimc_md_register_sensor_entities(fmd); + if (ret) { + mutex_unlock(&fmd->media_dev.graph_mutex); + goto err_m_ent; } mutex_unlock(&fmd->media_dev.graph_mutex); diff --git a/drivers/media/platform/exynos4-is/media-dev.h b/drivers/media/platform/exynos4-is/media-dev.h index ee1e2519f728..03214541f149 100644 --- a/drivers/media/platform/exynos4-is/media-dev.h +++ b/drivers/media/platform/exynos4-is/media-dev.h @@ -19,7 +19,7 @@ #include <media/media-entity.h> #include <media/v4l2-device.h> #include <media/v4l2-subdev.h> -#include <media/s5p_fimc.h> +#include <media/exynos-fimc.h> #include "fimc-core.h" #include "fimc-lite.h" diff --git a/drivers/media/platform/exynos4-is/mipi-csis.c b/drivers/media/platform/exynos4-is/mipi-csis.c index 3678ba59725c..ae54ef5f535d 100644 --- a/drivers/media/platform/exynos4-is/mipi-csis.c +++ b/drivers/media/platform/exynos4-is/mipi-csis.c @@ -22,14 +22,13 @@ #include <linux/of.h> #include <linux/of_graph.h> #include <linux/phy/phy.h> -#include <linux/platform_data/mipi-csis.h> #include <linux/platform_device.h> #include <linux/pm_runtime.h> #include <linux/regulator/consumer.h> #include <linux/slab.h> #include <linux/spinlock.h> #include <linux/videodev2.h> -#include <media/s5p_fimc.h> +#include <media/exynos-fimc.h> #include <media/v4l2-of.h> #include <media/v4l2-subdev.h> @@ -730,26 +729,6 @@ static irqreturn_t s5pcsis_irq_handler(int irq, void *dev_id) return IRQ_HANDLED; } -static int s5pcsis_get_platform_data(struct platform_device *pdev, - struct csis_state *state) -{ - struct s5p_platform_mipi_csis *pdata = pdev->dev.platform_data; - - if (pdata == NULL) { - dev_err(&pdev->dev, "Platform data not specified\n"); - return -EINVAL; - } - - state->clk_frequency = pdata->clk_rate; - state->num_lanes = pdata->lanes; - state->hs_settle = pdata->hs_settle; - state->index = max(0, pdev->id); - state->max_num_lanes = state->index ? CSIS1_MAX_LANES : - CSIS0_MAX_LANES; - return 0; -} - -#ifdef CONFIG_OF static int s5pcsis_parse_dt(struct platform_device *pdev, struct csis_state *state) { @@ -787,9 +766,6 @@ static int s5pcsis_parse_dt(struct platform_device *pdev, return 0; } -#else -#define s5pcsis_parse_dt(pdev, state) (-ENOSYS) -#endif static int s5pcsis_pm_resume(struct device *dev, bool runtime); static const struct of_device_id s5pcsis_of_match[]; @@ -812,19 +788,14 @@ static int s5pcsis_probe(struct platform_device *pdev) spin_lock_init(&state->slock); state->pdev = pdev; - if (dev->of_node) { - of_id = of_match_node(s5pcsis_of_match, dev->of_node); - if (WARN_ON(of_id == NULL)) - return -EINVAL; - - drv_data = of_id->data; - state->interrupt_mask = drv_data->interrupt_mask; + of_id = of_match_node(s5pcsis_of_match, dev->of_node); + if (WARN_ON(of_id == NULL)) + return -EINVAL; - ret = s5pcsis_parse_dt(pdev, state); - } else { - ret = s5pcsis_get_platform_data(pdev, state); - } + drv_data = of_id->data; + state->interrupt_mask = drv_data->interrupt_mask; + ret = s5pcsis_parse_dt(pdev, state); if (ret < 0) return ret; diff --git a/drivers/media/platform/fsl-viu.c b/drivers/media/platform/fsl-viu.c index dbf0ce38a8e7..d5dc198502ef 100644 --- a/drivers/media/platform/fsl-viu.c +++ b/drivers/media/platform/fsl-viu.c @@ -964,7 +964,7 @@ static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id id) struct viu_fh *fh = priv; fh->dev->std = id; - decoder_call(fh->dev, core, s_std, id); + decoder_call(fh->dev, video, s_std, id); return 0; } diff --git a/drivers/media/platform/marvell-ccic/mcam-core.c b/drivers/media/platform/marvell-ccic/mcam-core.c index 8b34c485be79..be4b51212106 100644 --- a/drivers/media/platform/marvell-ccic/mcam-core.c +++ b/drivers/media/platform/marvell-ccic/mcam-core.c @@ -1156,7 +1156,7 @@ static int mcam_vb_start_streaming(struct vb2_queue *vq, unsigned int count) return mcam_read_setup(cam); } -static int mcam_vb_stop_streaming(struct vb2_queue *vq) +static void mcam_vb_stop_streaming(struct vb2_queue *vq) { struct mcam_camera *cam = vb2_get_drv_priv(vq); unsigned long flags; @@ -1164,10 +1164,10 @@ static int mcam_vb_stop_streaming(struct vb2_queue *vq) if (cam->state == S_BUFWAIT) { /* They never gave us buffers */ cam->state = S_IDLE; - return 0; + return; } if (cam->state != S_STREAMING) - return -EINVAL; + return; mcam_ctlr_stop_dma(cam); /* * Reset the CCIC PHY after stopping streaming, @@ -1182,7 +1182,6 @@ static int mcam_vb_stop_streaming(struct vb2_queue *vq) spin_lock_irqsave(&cam->dev_lock, flags); INIT_LIST_HEAD(&cam->buffers); spin_unlock_irqrestore(&cam->dev_lock, flags); - return 0; } diff --git a/drivers/media/platform/mem2mem_testdev.c b/drivers/media/platform/mem2mem_testdev.c index 4f3096b17066..0714070ed7fa 100644 --- a/drivers/media/platform/mem2mem_testdev.c +++ b/drivers/media/platform/mem2mem_testdev.c @@ -787,7 +787,7 @@ static int m2mtest_start_streaming(struct vb2_queue *q, unsigned count) return 0; } -static int m2mtest_stop_streaming(struct vb2_queue *q) +static void m2mtest_stop_streaming(struct vb2_queue *q) { struct m2mtest_ctx *ctx = vb2_get_drv_priv(q); struct vb2_buffer *vb; @@ -799,12 +799,11 @@ static int m2mtest_stop_streaming(struct vb2_queue *q) else vb = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); if (vb == NULL) - return 0; + return; spin_lock_irqsave(&ctx->dev->irqlock, flags); v4l2_m2m_buf_done(vb, VB2_BUF_STATE_ERROR); spin_unlock_irqrestore(&ctx->dev->irqlock, flags); } - return 0; } static struct vb2_ops m2mtest_qops = { diff --git a/drivers/media/platform/mx2_emmaprp.c b/drivers/media/platform/mx2_emmaprp.c index 0b7480e82142..fa8f7cabe364 100644 --- a/drivers/media/platform/mx2_emmaprp.c +++ b/drivers/media/platform/mx2_emmaprp.c @@ -207,10 +207,8 @@ struct emmaprp_dev { struct mutex dev_mutex; spinlock_t irqlock; - int irq_emma; void __iomem *base_emma; struct clk *clk_emma_ahb, *clk_emma_ipg; - struct resource *res_emma; struct v4l2_m2m_dev *m2m_dev; struct vb2_alloc_ctx *alloc_ctx; @@ -901,9 +899,8 @@ static int emmaprp_probe(struct platform_device *pdev) { struct emmaprp_dev *pcdev; struct video_device *vfd; - struct resource *res_emma; - int irq_emma; - int ret; + struct resource *res; + int irq, ret; pcdev = devm_kzalloc(&pdev->dev, sizeof(*pcdev), GFP_KERNEL); if (!pcdev) @@ -920,12 +917,10 @@ static int emmaprp_probe(struct platform_device *pdev) if (IS_ERR(pcdev->clk_emma_ahb)) return PTR_ERR(pcdev->clk_emma_ahb); - irq_emma = platform_get_irq(pdev, 0); - res_emma = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (irq_emma < 0 || res_emma == NULL) { - dev_err(&pdev->dev, "Missing platform resources data\n"); - return -ENODEV; - } + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + pcdev->base_emma = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(pcdev->base_emma)) + return PTR_ERR(pcdev->base_emma); ret = v4l2_device_register(&pdev->dev, &pcdev->v4l2_dev); if (ret) @@ -952,20 +947,11 @@ static int emmaprp_probe(struct platform_device *pdev) platform_set_drvdata(pdev, pcdev); - pcdev->base_emma = devm_ioremap_resource(&pdev->dev, res_emma); - if (IS_ERR(pcdev->base_emma)) { - ret = PTR_ERR(pcdev->base_emma); - goto rel_vdev; - } - - pcdev->irq_emma = irq_emma; - pcdev->res_emma = res_emma; - - if (devm_request_irq(&pdev->dev, pcdev->irq_emma, emmaprp_irq, - 0, MEM2MEM_NAME, pcdev) < 0) { - ret = -ENODEV; + irq = platform_get_irq(pdev, 0); + ret = devm_request_irq(&pdev->dev, irq, emmaprp_irq, 0, + dev_name(&pdev->dev), pcdev); + if (ret) goto rel_vdev; - } pcdev->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev); if (IS_ERR(pcdev->alloc_ctx)) { @@ -999,6 +985,8 @@ rel_vdev: unreg_dev: v4l2_device_unregister(&pcdev->v4l2_dev); + mutex_destroy(&pcdev->dev_mutex); + return ret; } @@ -1012,6 +1000,7 @@ static int emmaprp_remove(struct platform_device *pdev) v4l2_m2m_release(pcdev->m2m_dev); vb2_dma_contig_cleanup_ctx(pcdev->alloc_ctx); v4l2_device_unregister(&pcdev->v4l2_dev); + mutex_destroy(&pcdev->dev_mutex); return 0; } diff --git a/drivers/media/platform/s3c-camif/camif-capture.c b/drivers/media/platform/s3c-camif/camif-capture.c index 4e4d1631e042..deba425e3d8f 100644 --- a/drivers/media/platform/s3c-camif/camif-capture.c +++ b/drivers/media/platform/s3c-camif/camif-capture.c @@ -435,10 +435,10 @@ static int start_streaming(struct vb2_queue *vq, unsigned int count) return 0; } -static int stop_streaming(struct vb2_queue *vq) +static void stop_streaming(struct vb2_queue *vq) { struct camif_vp *vp = vb2_get_drv_priv(vq); - return camif_stop_capture(vp); + camif_stop_capture(vp); } static int queue_setup(struct vb2_queue *vq, const struct v4l2_format *pfmt, diff --git a/drivers/media/platform/s5p-jpeg/jpeg-core.c b/drivers/media/platform/s5p-jpeg/jpeg-core.c index 8a18972012f7..0dcb796ecad9 100644 --- a/drivers/media/platform/s5p-jpeg/jpeg-core.c +++ b/drivers/media/platform/s5p-jpeg/jpeg-core.c @@ -192,8 +192,7 @@ static struct s5p_jpeg_fmt sjpeg_formats[] = { .colplanes = 2, .h_align = 4, .v_align = 4, - .flags = SJPEG_FMT_FLAG_ENC_OUTPUT | - SJPEG_FMT_FLAG_DEC_CAPTURE | + .flags = SJPEG_FMT_FLAG_DEC_CAPTURE | SJPEG_FMT_FLAG_S5P | SJPEG_FMT_NON_RGB, .subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_420, @@ -959,7 +958,7 @@ static int s5p_jpeg_g_fmt(struct file *file, void *priv, struct v4l2_format *f) static struct s5p_jpeg_fmt *s5p_jpeg_find_format(struct s5p_jpeg_ctx *ctx, u32 pixelformat, unsigned int fmt_type) { - unsigned int k, fmt_flag, ver_flag; + unsigned int k, fmt_flag; if (ctx->mode == S5P_JPEG_ENCODE) fmt_flag = (fmt_type == FMT_TYPE_OUTPUT) ? @@ -970,16 +969,11 @@ static struct s5p_jpeg_fmt *s5p_jpeg_find_format(struct s5p_jpeg_ctx *ctx, SJPEG_FMT_FLAG_DEC_OUTPUT : SJPEG_FMT_FLAG_DEC_CAPTURE; - if (ctx->jpeg->variant->version == SJPEG_S5P) - ver_flag = SJPEG_FMT_FLAG_S5P; - else - ver_flag = SJPEG_FMT_FLAG_EXYNOS4; - for (k = 0; k < ARRAY_SIZE(sjpeg_formats); k++) { struct s5p_jpeg_fmt *fmt = &sjpeg_formats[k]; if (fmt->fourcc == pixelformat && fmt->flags & fmt_flag && - fmt->flags & ver_flag) { + fmt->flags & ctx->jpeg->variant->fmt_ver_flag) { return fmt; } } @@ -1069,15 +1063,17 @@ static int s5p_jpeg_try_fmt_vid_cap(struct file *file, void *priv, return -EINVAL; } + if ((ctx->jpeg->variant->version != SJPEG_EXYNOS4) || + (ctx->mode != S5P_JPEG_DECODE)) + goto exit; + /* * The exynos4x12 device requires resulting YUV image * subsampling not to be lower than the input jpeg subsampling. * If this requirement is not met then downgrade the requested * capture format to the one with subsampling equal to the input jpeg. */ - if ((ctx->jpeg->variant->version != SJPEG_S5P) && - (ctx->mode == S5P_JPEG_DECODE) && - (fmt->flags & SJPEG_FMT_NON_RGB) && + if ((fmt->flags & SJPEG_FMT_NON_RGB) && (fmt->subsampling < ctx->subsampling)) { ret = s5p_jpeg_adjust_fourcc_to_subsampling(ctx->subsampling, fmt->fourcc, @@ -1090,6 +1086,23 @@ static int s5p_jpeg_try_fmt_vid_cap(struct file *file, void *priv, FMT_TYPE_CAPTURE); } + /* + * Decompression of a JPEG file with 4:2:0 subsampling and odd + * width to the YUV 4:2:0 compliant formats produces a raw image + * with broken luma component. Adjust capture format to RGB565 + * in such a case. + */ + if (ctx->subsampling == V4L2_JPEG_CHROMA_SUBSAMPLING_420 && + (ctx->out_q.w & 1) && + (pix->pixelformat == V4L2_PIX_FMT_NV12 || + pix->pixelformat == V4L2_PIX_FMT_NV21 || + pix->pixelformat == V4L2_PIX_FMT_YUV420)) { + pix->pixelformat = V4L2_PIX_FMT_RGB565; + fmt = s5p_jpeg_find_format(ctx, pix->pixelformat, + FMT_TYPE_CAPTURE); + } + +exit: return vidioc_try_fmt(f, fmt, ctx, FMT_TYPE_CAPTURE); } @@ -1111,6 +1124,32 @@ static int s5p_jpeg_try_fmt_vid_out(struct file *file, void *priv, return vidioc_try_fmt(f, fmt, ctx, FMT_TYPE_OUTPUT); } +static int exynos4_jpeg_get_output_buffer_size(struct s5p_jpeg_ctx *ctx, + struct v4l2_format *f, + int fmt_depth) +{ + struct v4l2_pix_format *pix = &f->fmt.pix; + u32 pix_fmt = f->fmt.pix.pixelformat; + int w = pix->width, h = pix->height, wh_align; + + if (pix_fmt == V4L2_PIX_FMT_RGB32 || + pix_fmt == V4L2_PIX_FMT_NV24 || + pix_fmt == V4L2_PIX_FMT_NV42 || + pix_fmt == V4L2_PIX_FMT_NV12 || + pix_fmt == V4L2_PIX_FMT_NV21 || + pix_fmt == V4L2_PIX_FMT_YUV420) + wh_align = 4; + else + wh_align = 1; + + jpeg_bound_align_image(&w, S5P_JPEG_MIN_WIDTH, + S5P_JPEG_MAX_WIDTH, wh_align, + &h, S5P_JPEG_MIN_HEIGHT, + S5P_JPEG_MAX_HEIGHT, wh_align); + + return w * h * fmt_depth >> 3; +} + static int s5p_jpeg_s_fmt(struct s5p_jpeg_ctx *ct, struct v4l2_format *f) { struct vb2_queue *vq; @@ -1137,10 +1176,24 @@ static int s5p_jpeg_s_fmt(struct s5p_jpeg_ctx *ct, struct v4l2_format *f) q_data->fmt = s5p_jpeg_find_format(ct, pix->pixelformat, f_type); q_data->w = pix->width; q_data->h = pix->height; - if (q_data->fmt->fourcc != V4L2_PIX_FMT_JPEG) - q_data->size = q_data->w * q_data->h * q_data->fmt->depth >> 3; - else + if (q_data->fmt->fourcc != V4L2_PIX_FMT_JPEG) { + /* + * During encoding Exynos4x12 SoCs access wider memory area + * than it results from Image_x and Image_y values written to + * the JPEG_IMAGE_SIZE register. In order to avoid sysmmu + * page fault calculate proper buffer size in such a case. + */ + if (ct->jpeg->variant->version == SJPEG_EXYNOS4 && + f_type == FMT_TYPE_OUTPUT && ct->mode == S5P_JPEG_ENCODE) + q_data->size = exynos4_jpeg_get_output_buffer_size(ct, + f, + q_data->fmt->depth); + else + q_data->size = q_data->w * q_data->h * + q_data->fmt->depth >> 3; + } else { q_data->size = pix->sizeimage; + } if (f_type == FMT_TYPE_OUTPUT) { ctrl_subs = v4l2_ctrl_find(&ct->ctrl_handler, @@ -1182,8 +1235,7 @@ static int s5p_jpeg_g_selection(struct file *file, void *priv, struct s5p_jpeg_ctx *ctx = fh_to_ctx(priv); if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT && - s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE && - ctx->jpeg->variant->version != SJPEG_S5P) + s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; /* For JPEG blob active == default == bounds */ @@ -1571,7 +1623,7 @@ static struct v4l2_m2m_ops s5p_jpeg_m2m_ops = { .job_abort = s5p_jpeg_job_abort, } ; -static struct v4l2_m2m_ops exynos_jpeg_m2m_ops = { +static struct v4l2_m2m_ops exynos4_jpeg_m2m_ops = { .device_run = exynos4_jpeg_device_run, .job_ready = s5p_jpeg_job_ready, .job_abort = s5p_jpeg_job_abort, @@ -1670,13 +1722,11 @@ static int s5p_jpeg_start_streaming(struct vb2_queue *q, unsigned int count) return ret > 0 ? 0 : ret; } -static int s5p_jpeg_stop_streaming(struct vb2_queue *q) +static void s5p_jpeg_stop_streaming(struct vb2_queue *q) { struct s5p_jpeg_ctx *ctx = vb2_get_drv_priv(q); pm_runtime_put(ctx->jpeg->dev); - - return 0; } static struct vb2_ops s5p_jpeg_qops = { @@ -1845,7 +1895,7 @@ static irqreturn_t exynos4_jpeg_irq(int irq, void *priv) return IRQ_HANDLED; } -static void *jpeg_get_drv_data(struct platform_device *pdev); +static void *jpeg_get_drv_data(struct device *dev); /* * ============================================================================ @@ -1857,18 +1907,14 @@ static int s5p_jpeg_probe(struct platform_device *pdev) { struct s5p_jpeg *jpeg; struct resource *res; - struct v4l2_m2m_ops *samsung_jpeg_m2m_ops; int ret; - if (!pdev->dev.of_node) - return -ENODEV; - /* JPEG IP abstraction struct */ jpeg = devm_kzalloc(&pdev->dev, sizeof(struct s5p_jpeg), GFP_KERNEL); if (!jpeg) return -ENOMEM; - jpeg->variant = jpeg_get_drv_data(pdev); + jpeg->variant = jpeg_get_drv_data(&pdev->dev); mutex_init(&jpeg->lock); spin_lock_init(&jpeg->slock); @@ -1911,13 +1957,8 @@ static int s5p_jpeg_probe(struct platform_device *pdev) goto clk_get_rollback; } - if (jpeg->variant->version == SJPEG_S5P) - samsung_jpeg_m2m_ops = &s5p_jpeg_m2m_ops; - else - samsung_jpeg_m2m_ops = &exynos_jpeg_m2m_ops; - /* mem2mem device */ - jpeg->m2m_dev = v4l2_m2m_init(samsung_jpeg_m2m_ops); + jpeg->m2m_dev = v4l2_m2m_init(jpeg->variant->m2m_ops); if (IS_ERR(jpeg->m2m_dev)) { v4l2_err(&jpeg->v4l2_dev, "Failed to init mem2mem device\n"); ret = PTR_ERR(jpeg->m2m_dev); @@ -2102,15 +2143,18 @@ static const struct dev_pm_ops s5p_jpeg_pm_ops = { SET_RUNTIME_PM_OPS(s5p_jpeg_runtime_suspend, s5p_jpeg_runtime_resume, NULL) }; -#ifdef CONFIG_OF static struct s5p_jpeg_variant s5p_jpeg_drvdata = { .version = SJPEG_S5P, .jpeg_irq = s5p_jpeg_irq, + .m2m_ops = &s5p_jpeg_m2m_ops, + .fmt_ver_flag = SJPEG_FMT_FLAG_S5P, }; static struct s5p_jpeg_variant exynos4_jpeg_drvdata = { .version = SJPEG_EXYNOS4, .jpeg_irq = exynos4_jpeg_irq, + .m2m_ops = &exynos4_jpeg_m2m_ops, + .fmt_ver_flag = SJPEG_FMT_FLAG_EXYNOS4, }; static const struct of_device_id samsung_jpeg_match[] = { @@ -2129,19 +2173,21 @@ static const struct of_device_id samsung_jpeg_match[] = { MODULE_DEVICE_TABLE(of, samsung_jpeg_match); -static void *jpeg_get_drv_data(struct platform_device *pdev) +static void *jpeg_get_drv_data(struct device *dev) { struct s5p_jpeg_variant *driver_data = NULL; const struct of_device_id *match; - match = of_match_node(of_match_ptr(samsung_jpeg_match), - pdev->dev.of_node); + if (!IS_ENABLED(CONFIG_OF) || !dev->of_node) + return &s5p_jpeg_drvdata; + + match = of_match_node(samsung_jpeg_match, dev->of_node); + if (match) driver_data = (struct s5p_jpeg_variant *)match->data; return driver_data; } -#endif static struct platform_driver s5p_jpeg_driver = { .probe = s5p_jpeg_probe, diff --git a/drivers/media/platform/s5p-jpeg/jpeg-core.h b/drivers/media/platform/s5p-jpeg/jpeg-core.h index f482dbf55d5f..3e4786329727 100644 --- a/drivers/media/platform/s5p-jpeg/jpeg-core.h +++ b/drivers/media/platform/s5p-jpeg/jpeg-core.h @@ -117,8 +117,10 @@ struct s5p_jpeg { }; struct s5p_jpeg_variant { - unsigned int version; - irqreturn_t (*jpeg_irq)(int irq, void *priv); + unsigned int version; + unsigned int fmt_ver_flag; + struct v4l2_m2m_ops *m2m_ops; + irqreturn_t (*jpeg_irq)(int irq, void *priv); }; /** diff --git a/drivers/media/platform/s5p-mfc/regs-mfc-v6.h b/drivers/media/platform/s5p-mfc/regs-mfc-v6.h index 8d0b686d9adb..51cb2dd0e13a 100644 --- a/drivers/media/platform/s5p-mfc/regs-mfc-v6.h +++ b/drivers/media/platform/s5p-mfc/regs-mfc-v6.h @@ -141,6 +141,7 @@ #define S5P_FIMV_D_SLICE_IF_ENABLE_V6 0xf4c4 #define S5P_FIMV_D_PICTURE_TAG_V6 0xf4c8 #define S5P_FIMV_D_STREAM_DATA_SIZE_V6 0xf4d0 +#define S5P_FIMV_D_INIT_BUFFER_OPTIONS_V6 0xf47c /* Display information register */ #define S5P_FIMV_D_DISPLAY_FRAME_WIDTH_V6 0xf500 @@ -381,8 +382,7 @@ (DIV_ROUND_UP((mbw) * (mbh), 32) * 16)) #define S5P_FIMV_SCRATCH_BUF_SIZE_H264_DEC_V6(w, h) (((w) * 192) + 64) #define S5P_FIMV_SCRATCH_BUF_SIZE_MPEG4_DEC_V6(w, h) \ - ((w) * ((h) * 64 + 144) + (2048/16 * (h) * 64) + \ - (2048/16 * 256 + 8320)) + ((w) * 144 + 8192 * (h) + 49216 + 1048576) #define S5P_FIMV_SCRATCH_BUF_SIZE_VC1_DEC_V6(w, h) \ (2096 * ((w) + (h) + 1)) #define S5P_FIMV_SCRATCH_BUF_SIZE_H263_DEC_V6(w, h) ((w) * 400) diff --git a/drivers/media/platform/s5p-mfc/regs-mfc-v7.h b/drivers/media/platform/s5p-mfc/regs-mfc-v7.h index ea5ec2a711af..1a5c6fdf7846 100644 --- a/drivers/media/platform/s5p-mfc/regs-mfc-v7.h +++ b/drivers/media/platform/s5p-mfc/regs-mfc-v7.h @@ -18,8 +18,6 @@ #define S5P_FIMV_CODEC_VP8_ENC_V7 25 /* Additional registers for v7 */ -#define S5P_FIMV_D_INIT_BUFFER_OPTIONS_V7 0xf47c - #define S5P_FIMV_E_SOURCE_FIRST_ADDR_V7 0xf9e0 #define S5P_FIMV_E_SOURCE_SECOND_ADDR_V7 0xf9e4 #define S5P_FIMV_E_SOURCE_THIRD_ADDR_V7 0xf9e8 @@ -56,6 +54,7 @@ (SZ_1M + ((w) * 144) + (8192 * (h)) + 49216) #define S5P_FIMV_SCRATCH_BUF_SIZE_VP8_ENC_V7(w, h) \ - (((w) * 48) + (((w) + 1) / 2 * 128) + 144 + 8192) + (((w) * 48) + 8192 + ((((w) + 1) / 2) * 128) + 144 + \ + ((((((w) * 16) * ((h) * 16)) * 3) / 2) * 4)) #endif /*_REGS_MFC_V7_H*/ diff --git a/drivers/media/platform/s5p-mfc/regs-mfc-v8.h b/drivers/media/platform/s5p-mfc/regs-mfc-v8.h new file mode 100644 index 000000000000..cc7cbec51b5e --- /dev/null +++ b/drivers/media/platform/s5p-mfc/regs-mfc-v8.h @@ -0,0 +1,124 @@ +/* + * Register definition file for Samsung MFC V8.x Interface (FIMV) driver + * + * Copyright (c) 2014 Samsung Electronics Co., Ltd. + * http://www.samsung.com/ + * + * 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. + */ + +#ifndef _REGS_MFC_V8_H +#define _REGS_MFC_V8_H + +#include <linux/sizes.h> +#include "regs-mfc-v7.h" + +/* Additional registers for v8 */ +#define S5P_FIMV_D_MVC_NUM_VIEWS_V8 0xf104 +#define S5P_FIMV_D_FIRST_PLANE_DPB_SIZE_V8 0xf144 +#define S5P_FIMV_D_SECOND_PLANE_DPB_SIZE_V8 0xf148 +#define S5P_FIMV_D_MV_BUFFER_SIZE_V8 0xf150 + +#define S5P_FIMV_D_FIRST_PLANE_DPB_STRIDE_SIZE_V8 0xf138 +#define S5P_FIMV_D_SECOND_PLANE_DPB_STRIDE_SIZE_V8 0xf13c + +#define S5P_FIMV_D_FIRST_PLANE_DPB_V8 0xf160 +#define S5P_FIMV_D_SECOND_PLANE_DPB_V8 0xf260 +#define S5P_FIMV_D_MV_BUFFER_V8 0xf460 + +#define S5P_FIMV_D_NUM_MV_V8 0xf134 +#define S5P_FIMV_D_INIT_BUFFER_OPTIONS_V8 0xf154 + +#define S5P_FIMV_D_SCRATCH_BUFFER_ADDR_V8 0xf560 +#define S5P_FIMV_D_SCRATCH_BUFFER_SIZE_V8 0xf564 + +#define S5P_FIMV_D_CPB_BUFFER_ADDR_V8 0xf5b0 +#define S5P_FIMV_D_CPB_BUFFER_SIZE_V8 0xf5b4 +#define S5P_FIMV_D_AVAILABLE_DPB_FLAG_LOWER_V8 0xf5bc +#define S5P_FIMV_D_CPB_BUFFER_OFFSET_V8 0xf5c0 +#define S5P_FIMV_D_SLICE_IF_ENABLE_V8 0xf5c4 +#define S5P_FIMV_D_STREAM_DATA_SIZE_V8 0xf5d0 + +/* Display information register */ +#define S5P_FIMV_D_DISPLAY_FRAME_WIDTH_V8 0xf600 +#define S5P_FIMV_D_DISPLAY_FRAME_HEIGHT_V8 0xf604 + +/* Display status */ +#define S5P_FIMV_D_DISPLAY_STATUS_V8 0xf608 + +#define S5P_FIMV_D_DISPLAY_FIRST_PLANE_ADDR_V8 0xf60c +#define S5P_FIMV_D_DISPLAY_SECOND_PLANE_ADDR_V8 0xf610 + +#define S5P_FIMV_D_DISPLAY_FRAME_TYPE_V8 0xf618 +#define S5P_FIMV_D_DISPLAY_CROP_INFO1_V8 0xf61c +#define S5P_FIMV_D_DISPLAY_CROP_INFO2_V8 0xf620 +#define S5P_FIMV_D_DISPLAY_PICTURE_PROFILE_V8 0xf624 + +/* Decoded picture information register */ +#define S5P_FIMV_D_DECODED_STATUS_V8 0xf644 +#define S5P_FIMV_D_DECODED_FIRST_PLANE_ADDR_V8 0xf648 +#define S5P_FIMV_D_DECODED_SECOND_PLANE_ADDR_V8 0xf64c +#define S5P_FIMV_D_DECODED_THIRD_PLANE_ADDR_V8 0xf650 +#define S5P_FIMV_D_DECODED_FRAME_TYPE_V8 0xf654 +#define S5P_FIMV_D_DECODED_NAL_SIZE_V8 0xf664 + +/* Returned value register for specific setting */ +#define S5P_FIMV_D_RET_PICTURE_TAG_TOP_V8 0xf674 +#define S5P_FIMV_D_RET_PICTURE_TAG_BOT_V8 0xf678 +#define S5P_FIMV_D_MVC_VIEW_ID_V8 0xf6d8 + +/* SEI related information */ +#define S5P_FIMV_D_FRAME_PACK_SEI_AVAIL_V8 0xf6dc + +/* Encoder Registers */ +#define S5P_FIMV_E_FIXED_PICTURE_QP_V8 0xf794 +#define S5P_FIMV_E_RC_CONFIG_V8 0xf798 +#define S5P_FIMV_E_RC_QP_BOUND_V8 0xf79c +#define S5P_FIMV_E_RC_RPARAM_V8 0xf7a4 +#define S5P_FIMV_E_MB_RC_CONFIG_V8 0xf7a8 +#define S5P_FIMV_E_PADDING_CTRL_V8 0xf7ac +#define S5P_FIMV_E_MV_HOR_RANGE_V8 0xf7b4 +#define S5P_FIMV_E_MV_VER_RANGE_V8 0xf7b8 + +#define S5P_FIMV_E_VBV_BUFFER_SIZE_V8 0xf78c +#define S5P_FIMV_E_VBV_INIT_DELAY_V8 0xf790 + +#define S5P_FIMV_E_ASPECT_RATIO_V8 0xfb4c +#define S5P_FIMV_E_EXTENDED_SAR_V8 0xfb50 +#define S5P_FIMV_E_H264_OPTIONS_V8 0xfb54 + +/* MFCv8 Context buffer sizes */ +#define MFC_CTX_BUF_SIZE_V8 (30 * SZ_1K) /* 30KB */ +#define MFC_H264_DEC_CTX_BUF_SIZE_V8 (2 * SZ_1M) /* 2MB */ +#define MFC_OTHER_DEC_CTX_BUF_SIZE_V8 (20 * SZ_1K) /* 20KB */ +#define MFC_H264_ENC_CTX_BUF_SIZE_V8 (100 * SZ_1K) /* 100KB */ +#define MFC_OTHER_ENC_CTX_BUF_SIZE_V8 (10 * SZ_1K) /* 10KB */ + +/* Buffer size defines */ +#define S5P_FIMV_TMV_BUFFER_SIZE_V8(w, h) (((w) + 1) * ((h) + 1) * 8) + +#define S5P_FIMV_SCRATCH_BUF_SIZE_H264_DEC_V8(w, h) (((w) * 704) + 2176) +#define S5P_FIMV_SCRATCH_BUF_SIZE_VP8_DEC_V8(w, h) \ + (((w) * 576 + (h) * 128) + 4128) + +#define S5P_FIMV_SCRATCH_BUF_SIZE_H264_ENC_V8(w, h) \ + (((w) * 592) + 2336) +#define S5P_FIMV_SCRATCH_BUF_SIZE_VP8_ENC_V8(w, h) \ + (((w) * 576) + 10512 + \ + ((((((w) * 16) * ((h) * 16)) * 3) / 2) * 4)) +#define S5P_FIMV_ME_BUFFER_SIZE_V8(imw, imh, mbw, mbh) \ + ((DIV_ROUND_UP((mbw * 16), 64) * DIV_ROUND_UP((mbh * 16), 64) * 256) \ + + (DIV_ROUND_UP((mbw) * (mbh), 32) * 16)) + +/* BUffer alignment defines */ +#define S5P_FIMV_D_ALIGN_PLANE_SIZE_V8 64 + +/* MFCv8 variant defines */ +#define MAX_FW_SIZE_V8 (SZ_1M) /* 1MB */ +#define MAX_CPB_SIZE_V8 (3 * SZ_1M) /* 3MB */ +#define MFC_VERSION_V8 0x80 +#define MFC_NUM_PORTS_V8 1 + +#endif /*_REGS_MFC_V8_H*/ diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc.c b/drivers/media/platform/s5p-mfc/s5p_mfc.c index 89356ae90238..41723180d10c 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc.c @@ -309,12 +309,15 @@ static void s5p_mfc_handle_frame(struct s5p_mfc_ctx *ctx, { struct s5p_mfc_dev *dev = ctx->dev; unsigned int dst_frame_status; + unsigned int dec_frame_status; struct s5p_mfc_buf *src_buf; unsigned long flags; unsigned int res_change; dst_frame_status = s5p_mfc_hw_call(dev->mfc_ops, get_dspl_status, dev) & S5P_FIMV_DEC_STATUS_DECODING_STATUS_MASK; + dec_frame_status = s5p_mfc_hw_call(dev->mfc_ops, get_dec_status, dev) + & S5P_FIMV_DEC_STATUS_DECODING_STATUS_MASK; res_change = (s5p_mfc_hw_call(dev->mfc_ops, get_dspl_status, dev) & S5P_FIMV_DEC_STATUS_RESOLUTION_MASK) >> S5P_FIMV_DEC_STATUS_RESOLUTION_SHIFT; @@ -339,16 +342,23 @@ static void s5p_mfc_handle_frame(struct s5p_mfc_ctx *ctx, /* All frames remaining in the buffer have been extracted */ if (dst_frame_status == S5P_FIMV_DEC_STATUS_DECODING_EMPTY) { if (ctx->state == MFCINST_RES_CHANGE_FLUSH) { + static const struct v4l2_event ev_src_ch = { + .type = V4L2_EVENT_SOURCE_CHANGE, + .u.src_change.changes = + V4L2_EVENT_SRC_CH_RESOLUTION, + }; + s5p_mfc_handle_frame_all_extracted(ctx); ctx->state = MFCINST_RES_CHANGE_END; + v4l2_event_queue_fh(&ctx->fh, &ev_src_ch); + goto leave_handle_frame; } else { s5p_mfc_handle_frame_all_extracted(ctx); } } - if (dst_frame_status == S5P_FIMV_DEC_STATUS_DECODING_DISPLAY || - dst_frame_status == S5P_FIMV_DEC_STATUS_DECODING_ONLY) + if (dec_frame_status == S5P_FIMV_DEC_STATUS_DECODING_DISPLAY) s5p_mfc_handle_frame_copy_time(ctx); /* A frame has been decoded and is in the buffer */ @@ -366,6 +376,7 @@ static void s5p_mfc_handle_frame(struct s5p_mfc_ctx *ctx, ctx->consumed_stream += s5p_mfc_hw_call(dev->mfc_ops, get_consumed_stream, dev); if (ctx->codec_mode != S5P_MFC_CODEC_H264_DEC && + ctx->codec_mode != S5P_MFC_CODEC_VP8_DEC && ctx->consumed_stream + STUFF_BYTE < src_buf->b->v4l2_planes[0].bytesused) { /* Run MFC again on the same buffer */ @@ -641,6 +652,7 @@ static irqreturn_t s5p_mfc_irq(int irq, void *priv) case S5P_MFC_R2H_CMD_CLOSE_INSTANCE_RET: clear_work_bit(ctx); + ctx->inst_no = MFC_NO_INSTANCE_SET; ctx->state = MFCINST_FREE; wake_up(&ctx->queue); goto irq_cleanup_hw; @@ -761,7 +773,7 @@ static int s5p_mfc_open(struct file *file) goto err_bad_node; } ctx->fh.ctrl_handler = &ctx->ctrl_handler; - ctx->inst_no = -1; + ctx->inst_no = MFC_NO_INSTANCE_SET; /* Load firmware if this is the first instance */ if (dev->num_inst == 1) { dev->watchdog_timer.expires = jiffies + @@ -871,29 +883,11 @@ static int s5p_mfc_release(struct file *file) vb2_queue_release(&ctx->vq_dst); /* Mark context as idle */ clear_work_bit_irqsave(ctx); - /* If instance was initialised then + /* If instance was initialised and not yet freed, * return instance and free resources */ - if (ctx->inst_no != MFC_NO_INSTANCE_SET) { + if (ctx->state != MFCINST_FREE && ctx->state != MFCINST_INIT) { mfc_debug(2, "Has to free instance\n"); - ctx->state = MFCINST_RETURN_INST; - set_work_bit_irqsave(ctx); - s5p_mfc_clean_ctx_int_flags(ctx); - s5p_mfc_hw_call(dev->mfc_ops, try_run, dev); - /* Wait until instance is returned or timeout occurred */ - if (s5p_mfc_wait_for_done_ctx - (ctx, S5P_MFC_R2H_CMD_CLOSE_INSTANCE_RET, 0)) { - s5p_mfc_clock_off(); - mfc_err("Err returning instance\n"); - } - mfc_debug(2, "After free instance\n"); - /* Free resources */ - s5p_mfc_hw_call(dev->mfc_ops, release_codec_buffers, ctx); - s5p_mfc_hw_call(dev->mfc_ops, release_instance_buffer, ctx); - if (ctx->type == MFCINST_DECODER) - s5p_mfc_hw_call(dev->mfc_ops, release_dec_desc_buffer, - ctx); - - ctx->inst_no = MFC_NO_INSTANCE_SET; + s5p_mfc_close_mfc_inst(dev, ctx); } /* hardware locking scheme */ if (dev->curr_ctx == ctx->num) @@ -1207,6 +1201,7 @@ static int s5p_mfc_probe(struct platform_device *pdev) /* Initialize HW ops and commands based on MFC version */ s5p_mfc_init_hw_ops(dev); s5p_mfc_init_hw_cmds(dev); + s5p_mfc_init_regs(dev); pr_debug("%s--\n", __func__); return 0; @@ -1352,6 +1347,7 @@ struct s5p_mfc_buf_align mfc_buf_align_v5 = { static struct s5p_mfc_variant mfc_drvdata_v5 = { .version = MFC_VERSION, + .version_bit = MFC_V5_BIT, .port_num = MFC_NUM_PORTS, .buf_size = &buf_size_v5, .buf_align = &mfc_buf_align_v5, @@ -1378,6 +1374,7 @@ struct s5p_mfc_buf_align mfc_buf_align_v6 = { static struct s5p_mfc_variant mfc_drvdata_v6 = { .version = MFC_VERSION_V6, + .version_bit = MFC_V6_BIT, .port_num = MFC_NUM_PORTS_V6, .buf_size = &buf_size_v6, .buf_align = &mfc_buf_align_v6, @@ -1404,12 +1401,40 @@ struct s5p_mfc_buf_align mfc_buf_align_v7 = { static struct s5p_mfc_variant mfc_drvdata_v7 = { .version = MFC_VERSION_V7, + .version_bit = MFC_V7_BIT, .port_num = MFC_NUM_PORTS_V7, .buf_size = &buf_size_v7, .buf_align = &mfc_buf_align_v7, .fw_name = "s5p-mfc-v7.fw", }; +struct s5p_mfc_buf_size_v6 mfc_buf_size_v8 = { + .dev_ctx = MFC_CTX_BUF_SIZE_V8, + .h264_dec_ctx = MFC_H264_DEC_CTX_BUF_SIZE_V8, + .other_dec_ctx = MFC_OTHER_DEC_CTX_BUF_SIZE_V8, + .h264_enc_ctx = MFC_H264_ENC_CTX_BUF_SIZE_V8, + .other_enc_ctx = MFC_OTHER_ENC_CTX_BUF_SIZE_V8, +}; + +struct s5p_mfc_buf_size buf_size_v8 = { + .fw = MAX_FW_SIZE_V8, + .cpb = MAX_CPB_SIZE_V8, + .priv = &mfc_buf_size_v8, +}; + +struct s5p_mfc_buf_align mfc_buf_align_v8 = { + .base = 0, +}; + +static struct s5p_mfc_variant mfc_drvdata_v8 = { + .version = MFC_VERSION_V8, + .version_bit = MFC_V8_BIT, + .port_num = MFC_NUM_PORTS_V8, + .buf_size = &buf_size_v8, + .buf_align = &mfc_buf_align_v8, + .fw_name = "s5p-mfc-v8.fw", +}; + static struct platform_device_id mfc_driver_ids[] = { { .name = "s5p-mfc", @@ -1423,6 +1448,9 @@ static struct platform_device_id mfc_driver_ids[] = { }, { .name = "s5p-mfc-v7", .driver_data = (unsigned long)&mfc_drvdata_v7, + }, { + .name = "s5p-mfc-v8", + .driver_data = (unsigned long)&mfc_drvdata_v8, }, {}, }; @@ -1438,6 +1466,9 @@ static const struct of_device_id exynos_mfc_match[] = { }, { .compatible = "samsung,mfc-v7", .data = &mfc_drvdata_v7, + }, { + .compatible = "samsung,mfc-v8", + .data = &mfc_drvdata_v8, }, {}, }; diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_common.h b/drivers/media/platform/s5p-mfc/s5p_mfc_common.h index 5c28cc3e699b..b04360cd34f0 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_common.h +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_common.h @@ -23,8 +23,7 @@ #include <media/v4l2-ioctl.h> #include <media/videobuf2-core.h> #include "regs-mfc.h" -#include "regs-mfc-v6.h" -#include "regs-mfc-v7.h" +#include "regs-mfc-v8.h" /* Definitions related to MFC memory */ @@ -223,6 +222,7 @@ struct s5p_mfc_buf_align { struct s5p_mfc_variant { unsigned int version; unsigned int port_num; + u32 version_bit; struct s5p_mfc_buf_size *buf_size; struct s5p_mfc_buf_align *buf_align; char *fw_name; @@ -330,6 +330,7 @@ struct s5p_mfc_dev { int warn_start; struct s5p_mfc_hw_ops *mfc_ops; struct s5p_mfc_hw_cmds *mfc_cmds; + const struct s5p_mfc_regs *mfc_regs; }; /** @@ -663,6 +664,7 @@ struct s5p_mfc_fmt { u32 codec_mode; enum s5p_mfc_fmt_type type; u32 num_planes; + u32 versions; }; /** @@ -700,6 +702,13 @@ void set_work_bit_irqsave(struct s5p_mfc_ctx *ctx); (dev->variant->port_num ? 1 : 0) : 0) : 0) #define IS_TWOPORT(dev) (dev->variant->port_num == 2 ? 1 : 0) #define IS_MFCV6_PLUS(dev) (dev->variant->version >= 0x60 ? 1 : 0) -#define IS_MFCV7(dev) (dev->variant->version >= 0x70 ? 1 : 0) +#define IS_MFCV7_PLUS(dev) (dev->variant->version >= 0x70 ? 1 : 0) +#define IS_MFCV8(dev) (dev->variant->version >= 0x80 ? 1 : 0) + +#define MFC_V5_BIT BIT(0) +#define MFC_V6_BIT BIT(1) +#define MFC_V7_BIT BIT(2) +#define MFC_V8_BIT BIT(3) + #endif /* S5P_MFC_COMMON_H_ */ diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_ctrl.c b/drivers/media/platform/s5p-mfc/s5p_mfc_ctrl.c index ee05f2dd439b..6c3f8f743900 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_ctrl.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_ctrl.c @@ -400,3 +400,65 @@ int s5p_mfc_wakeup(struct s5p_mfc_dev *dev) return 0; } +int s5p_mfc_open_mfc_inst(struct s5p_mfc_dev *dev, struct s5p_mfc_ctx *ctx) +{ + int ret = 0; + + ret = s5p_mfc_hw_call(dev->mfc_ops, alloc_instance_buffer, ctx); + if (ret) { + mfc_err("Failed allocating instance buffer\n"); + goto err; + } + + if (ctx->type == MFCINST_DECODER) { + ret = s5p_mfc_hw_call(dev->mfc_ops, + alloc_dec_temp_buffers, ctx); + if (ret) { + mfc_err("Failed allocating temporary buffers\n"); + goto err_free_inst_buf; + } + } + + set_work_bit_irqsave(ctx); + s5p_mfc_clean_ctx_int_flags(ctx); + s5p_mfc_hw_call(dev->mfc_ops, try_run, dev); + if (s5p_mfc_wait_for_done_ctx(ctx, + S5P_MFC_R2H_CMD_OPEN_INSTANCE_RET, 0)) { + /* Error or timeout */ + mfc_err("Error getting instance from hardware\n"); + ret = -EIO; + goto err_free_desc_buf; + } + + mfc_debug(2, "Got instance number: %d\n", ctx->inst_no); + return ret; + +err_free_desc_buf: + if (ctx->type == MFCINST_DECODER) + s5p_mfc_hw_call(dev->mfc_ops, release_dec_desc_buffer, ctx); +err_free_inst_buf: + s5p_mfc_hw_call(dev->mfc_ops, release_instance_buffer, ctx); +err: + return ret; +} + +void s5p_mfc_close_mfc_inst(struct s5p_mfc_dev *dev, struct s5p_mfc_ctx *ctx) +{ + ctx->state = MFCINST_RETURN_INST; + set_work_bit_irqsave(ctx); + s5p_mfc_clean_ctx_int_flags(ctx); + s5p_mfc_hw_call(dev->mfc_ops, try_run, dev); + /* Wait until instance is returned or timeout occurred */ + if (s5p_mfc_wait_for_done_ctx(ctx, + S5P_MFC_R2H_CMD_CLOSE_INSTANCE_RET, 0)) + mfc_err("Err returning instance\n"); + + /* Free resources */ + s5p_mfc_hw_call(dev->mfc_ops, release_codec_buffers, ctx); + s5p_mfc_hw_call(dev->mfc_ops, release_instance_buffer, ctx); + if (ctx->type == MFCINST_DECODER) + s5p_mfc_hw_call(dev->mfc_ops, release_dec_desc_buffer, ctx); + + ctx->inst_no = MFC_NO_INSTANCE_SET; + ctx->state = MFCINST_FREE; +} diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_ctrl.h b/drivers/media/platform/s5p-mfc/s5p_mfc_ctrl.h index 6a9b6f8606bb..8e5df041edf7 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_ctrl.h +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_ctrl.h @@ -28,4 +28,7 @@ int s5p_mfc_wakeup(struct s5p_mfc_dev *dev); int s5p_mfc_reset(struct s5p_mfc_dev *dev); +int s5p_mfc_open_mfc_inst(struct s5p_mfc_dev *dev, struct s5p_mfc_ctx *ctx); +void s5p_mfc_close_mfc_inst(struct s5p_mfc_dev *dev, struct s5p_mfc_ctx *ctx); + #endif /* S5P_MFC_CTRL_H */ diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c b/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c index 8faf9691712d..4d93835dec9d 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c @@ -25,15 +25,13 @@ #include <media/v4l2-event.h> #include <media/videobuf2-core.h> #include "s5p_mfc_common.h" +#include "s5p_mfc_ctrl.h" #include "s5p_mfc_debug.h" #include "s5p_mfc_dec.h" #include "s5p_mfc_intr.h" #include "s5p_mfc_opr.h" #include "s5p_mfc_pm.h" -#define DEF_SRC_FMT_DEC V4L2_PIX_FMT_H264 -#define DEF_DST_FMT_DEC V4L2_PIX_FMT_NV12MT_16X16 - static struct s5p_mfc_fmt formats[] = { { .name = "4:2:0 2 Planes 16x16 Tiles", @@ -41,6 +39,7 @@ static struct s5p_mfc_fmt formats[] = { .codec_mode = S5P_MFC_CODEC_NONE, .type = MFC_FMT_RAW, .num_planes = 2, + .versions = MFC_V6_BIT | MFC_V7_BIT, }, { .name = "4:2:0 2 Planes 64x32 Tiles", @@ -48,6 +47,7 @@ static struct s5p_mfc_fmt formats[] = { .codec_mode = S5P_MFC_CODEC_NONE, .type = MFC_FMT_RAW, .num_planes = 2, + .versions = MFC_V5_BIT, }, { .name = "4:2:0 2 Planes Y/CbCr", @@ -55,6 +55,7 @@ static struct s5p_mfc_fmt formats[] = { .codec_mode = S5P_MFC_CODEC_NONE, .type = MFC_FMT_RAW, .num_planes = 2, + .versions = MFC_V6_BIT | MFC_V7_BIT | MFC_V8_BIT, }, { .name = "4:2:0 2 Planes Y/CrCb", @@ -62,6 +63,7 @@ static struct s5p_mfc_fmt formats[] = { .codec_mode = S5P_MFC_CODEC_NONE, .type = MFC_FMT_RAW, .num_planes = 2, + .versions = MFC_V6_BIT | MFC_V7_BIT | MFC_V8_BIT, }, { .name = "H264 Encoded Stream", @@ -69,6 +71,8 @@ static struct s5p_mfc_fmt formats[] = { .codec_mode = S5P_MFC_CODEC_H264_DEC, .type = MFC_FMT_DEC, .num_planes = 1, + .versions = MFC_V5_BIT | MFC_V6_BIT | MFC_V7_BIT | + MFC_V8_BIT, }, { .name = "H264/MVC Encoded Stream", @@ -76,6 +80,7 @@ static struct s5p_mfc_fmt formats[] = { .codec_mode = S5P_MFC_CODEC_H264_MVC_DEC, .type = MFC_FMT_DEC, .num_planes = 1, + .versions = MFC_V6_BIT | MFC_V7_BIT | MFC_V8_BIT, }, { .name = "H263 Encoded Stream", @@ -83,6 +88,8 @@ static struct s5p_mfc_fmt formats[] = { .codec_mode = S5P_MFC_CODEC_H263_DEC, .type = MFC_FMT_DEC, .num_planes = 1, + .versions = MFC_V5_BIT | MFC_V6_BIT | MFC_V7_BIT | + MFC_V8_BIT, }, { .name = "MPEG1 Encoded Stream", @@ -90,6 +97,8 @@ static struct s5p_mfc_fmt formats[] = { .codec_mode = S5P_MFC_CODEC_MPEG2_DEC, .type = MFC_FMT_DEC, .num_planes = 1, + .versions = MFC_V5_BIT | MFC_V6_BIT | MFC_V7_BIT | + MFC_V8_BIT, }, { .name = "MPEG2 Encoded Stream", @@ -97,6 +106,8 @@ static struct s5p_mfc_fmt formats[] = { .codec_mode = S5P_MFC_CODEC_MPEG2_DEC, .type = MFC_FMT_DEC, .num_planes = 1, + .versions = MFC_V5_BIT | MFC_V6_BIT | MFC_V7_BIT | + MFC_V8_BIT, }, { .name = "MPEG4 Encoded Stream", @@ -104,6 +115,8 @@ static struct s5p_mfc_fmt formats[] = { .codec_mode = S5P_MFC_CODEC_MPEG4_DEC, .type = MFC_FMT_DEC, .num_planes = 1, + .versions = MFC_V5_BIT | MFC_V6_BIT | MFC_V7_BIT | + MFC_V8_BIT, }, { .name = "XviD Encoded Stream", @@ -111,6 +124,8 @@ static struct s5p_mfc_fmt formats[] = { .codec_mode = S5P_MFC_CODEC_MPEG4_DEC, .type = MFC_FMT_DEC, .num_planes = 1, + .versions = MFC_V5_BIT | MFC_V6_BIT | MFC_V7_BIT | + MFC_V8_BIT, }, { .name = "VC1 Encoded Stream", @@ -118,6 +133,8 @@ static struct s5p_mfc_fmt formats[] = { .codec_mode = S5P_MFC_CODEC_VC1_DEC, .type = MFC_FMT_DEC, .num_planes = 1, + .versions = MFC_V5_BIT | MFC_V6_BIT | MFC_V7_BIT | + MFC_V8_BIT, }, { .name = "VC1 RCV Encoded Stream", @@ -125,6 +142,8 @@ static struct s5p_mfc_fmt formats[] = { .codec_mode = S5P_MFC_CODEC_VC1RCV_DEC, .type = MFC_FMT_DEC, .num_planes = 1, + .versions = MFC_V5_BIT | MFC_V6_BIT | MFC_V7_BIT | + MFC_V8_BIT, }, { .name = "VP8 Encoded Stream", @@ -132,6 +151,7 @@ static struct s5p_mfc_fmt formats[] = { .codec_mode = S5P_MFC_CODEC_VP8_DEC, .type = MFC_FMT_DEC, .num_planes = 1, + .versions = MFC_V6_BIT | MFC_V7_BIT | MFC_V8_BIT, }, }; @@ -262,8 +282,10 @@ static int vidioc_querycap(struct file *file, void *priv, } /* Enumerate format */ -static int vidioc_enum_fmt(struct v4l2_fmtdesc *f, bool mplane, bool out) +static int vidioc_enum_fmt(struct file *file, struct v4l2_fmtdesc *f, + bool mplane, bool out) { + struct s5p_mfc_dev *dev = video_drvdata(file); struct s5p_mfc_fmt *fmt; int i, j = 0; @@ -276,6 +298,8 @@ static int vidioc_enum_fmt(struct v4l2_fmtdesc *f, bool mplane, bool out) continue; else if (!out && formats[i].type != MFC_FMT_RAW) continue; + else if ((dev->variant->version_bit & formats[i].versions) == 0) + continue; if (j == f->index) break; @@ -292,25 +316,25 @@ static int vidioc_enum_fmt(struct v4l2_fmtdesc *f, bool mplane, bool out) static int vidioc_enum_fmt_vid_cap(struct file *file, void *pirv, struct v4l2_fmtdesc *f) { - return vidioc_enum_fmt(f, false, false); + return vidioc_enum_fmt(file, f, false, false); } static int vidioc_enum_fmt_vid_cap_mplane(struct file *file, void *pirv, struct v4l2_fmtdesc *f) { - return vidioc_enum_fmt(f, true, false); + return vidioc_enum_fmt(file, f, true, false); } -static int vidioc_enum_fmt_vid_out(struct file *file, void *prov, +static int vidioc_enum_fmt_vid_out(struct file *file, void *priv, struct v4l2_fmtdesc *f) { - return vidioc_enum_fmt(f, false, true); + return vidioc_enum_fmt(file, f, false, true); } -static int vidioc_enum_fmt_vid_out_mplane(struct file *file, void *prov, +static int vidioc_enum_fmt_vid_out_mplane(struct file *file, void *priv, struct v4l2_fmtdesc *f) { - return vidioc_enum_fmt(f, true, true); + return vidioc_enum_fmt(file, f, true, true); } /* Get format */ @@ -386,11 +410,9 @@ static int vidioc_try_fmt(struct file *file, void *priv, struct v4l2_format *f) mfc_err("Unknown codec\n"); return -EINVAL; } - if (!IS_MFCV6_PLUS(dev)) { - if (fmt->fourcc == V4L2_PIX_FMT_VP8) { - mfc_err("Not supported format.\n"); - return -EINVAL; - } + if ((dev->variant->version_bit & fmt->versions) == 0) { + mfc_err("Unsupported format by this MFC version.\n"); + return -EINVAL; } } else if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { fmt = find_format(f, MFC_FMT_RAW); @@ -398,13 +420,8 @@ static int vidioc_try_fmt(struct file *file, void *priv, struct v4l2_format *f) mfc_err("Unsupported format for destination.\n"); return -EINVAL; } - if (IS_MFCV6_PLUS(dev) && - (fmt->fourcc == V4L2_PIX_FMT_NV12MT)) { - mfc_err("Not supported format.\n"); - return -EINVAL; - } else if (!IS_MFCV6_PLUS(dev) && - (fmt->fourcc != V4L2_PIX_FMT_NV12MT)) { - mfc_err("Not supported format.\n"); + if ((dev->variant->version_bit & fmt->versions) == 0) { + mfc_err("Unsupported format by this MFC version.\n"); return -EINVAL; } } @@ -462,104 +479,131 @@ out: return ret; } -/* Reqeust buffers */ -static int vidioc_reqbufs(struct file *file, void *priv, - struct v4l2_requestbuffers *reqbufs) +static int reqbufs_output(struct s5p_mfc_dev *dev, struct s5p_mfc_ctx *ctx, + struct v4l2_requestbuffers *reqbufs) { - struct s5p_mfc_dev *dev = video_drvdata(file); - struct s5p_mfc_ctx *ctx = fh_to_ctx(priv); int ret = 0; - if (reqbufs->memory != V4L2_MEMORY_MMAP) { - mfc_err("Only V4L2_MEMORY_MAP is supported\n"); - return -EINVAL; - } - if (reqbufs->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { - /* Can only request buffers after an instance has been opened.*/ - if (ctx->state == MFCINST_INIT) { - ctx->src_bufs_cnt = 0; - if (reqbufs->count == 0) { - mfc_debug(2, "Freeing buffers\n"); - s5p_mfc_clock_on(); - ret = vb2_reqbufs(&ctx->vq_src, reqbufs); - s5p_mfc_clock_off(); - return ret; - } - /* Decoding */ - if (ctx->output_state != QUEUE_FREE) { - mfc_err("Bufs have already been requested\n"); - return -EINVAL; - } - s5p_mfc_clock_on(); - ret = vb2_reqbufs(&ctx->vq_src, reqbufs); - s5p_mfc_clock_off(); - if (ret) { - mfc_err("vb2_reqbufs on output failed\n"); - return ret; - } - mfc_debug(2, "vb2_reqbufs: %d\n", ret); - ctx->output_state = QUEUE_BUFS_REQUESTED; - } - } else if (reqbufs->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { - ctx->dst_bufs_cnt = 0; - if (reqbufs->count == 0) { - mfc_debug(2, "Freeing buffers\n"); - s5p_mfc_clock_on(); - ret = vb2_reqbufs(&ctx->vq_dst, reqbufs); - s5p_mfc_clock_off(); - return ret; + s5p_mfc_clock_on(); + + if (reqbufs->count == 0) { + mfc_debug(2, "Freeing buffers\n"); + ret = vb2_reqbufs(&ctx->vq_src, reqbufs); + if (ret) + goto out; + s5p_mfc_close_mfc_inst(dev, ctx); + ctx->src_bufs_cnt = 0; + ctx->output_state = QUEUE_FREE; + } else if (ctx->output_state == QUEUE_FREE) { + /* Can only request buffers when we have a valid format set. */ + WARN_ON(ctx->src_bufs_cnt != 0); + if (ctx->state != MFCINST_INIT) { + mfc_err("Reqbufs called in an invalid state\n"); + ret = -EINVAL; + goto out; } - if (ctx->capture_state != QUEUE_FREE) { - mfc_err("Bufs have already been requested\n"); - return -EINVAL; - } - ctx->capture_state = QUEUE_BUFS_REQUESTED; - s5p_mfc_clock_on(); - ret = vb2_reqbufs(&ctx->vq_dst, reqbufs); - s5p_mfc_clock_off(); + + mfc_debug(2, "Allocating %d buffers for OUTPUT queue\n", + reqbufs->count); + ret = vb2_reqbufs(&ctx->vq_src, reqbufs); + if (ret) + goto out; + + ret = s5p_mfc_open_mfc_inst(dev, ctx); if (ret) { - mfc_err("vb2_reqbufs on capture failed\n"); - return ret; - } - if (reqbufs->count < ctx->pb_count) { - mfc_err("Not enough buffers allocated\n"); reqbufs->count = 0; - s5p_mfc_clock_on(); - ret = vb2_reqbufs(&ctx->vq_dst, reqbufs); - s5p_mfc_clock_off(); - return -ENOMEM; + vb2_reqbufs(&ctx->vq_src, reqbufs); + goto out; } + + ctx->output_state = QUEUE_BUFS_REQUESTED; + } else { + mfc_err("Buffers have already been requested\n"); + ret = -EINVAL; + } +out: + s5p_mfc_clock_off(); + if (ret) + mfc_err("Failed allocating buffers for OUTPUT queue\n"); + return ret; +} + +static int reqbufs_capture(struct s5p_mfc_dev *dev, struct s5p_mfc_ctx *ctx, + struct v4l2_requestbuffers *reqbufs) +{ + int ret = 0; + + s5p_mfc_clock_on(); + + if (reqbufs->count == 0) { + mfc_debug(2, "Freeing buffers\n"); + ret = vb2_reqbufs(&ctx->vq_dst, reqbufs); + if (ret) + goto out; + s5p_mfc_hw_call(dev->mfc_ops, release_codec_buffers, ctx); + ctx->dst_bufs_cnt = 0; + } else if (ctx->capture_state == QUEUE_FREE) { + WARN_ON(ctx->dst_bufs_cnt != 0); + mfc_debug(2, "Allocating %d buffers for CAPTURE queue\n", + reqbufs->count); + ret = vb2_reqbufs(&ctx->vq_dst, reqbufs); + if (ret) + goto out; + + ctx->capture_state = QUEUE_BUFS_REQUESTED; ctx->total_dpb_count = reqbufs->count; + ret = s5p_mfc_hw_call(dev->mfc_ops, alloc_codec_buffers, ctx); if (ret) { mfc_err("Failed to allocate decoding buffers\n"); reqbufs->count = 0; - s5p_mfc_clock_on(); - ret = vb2_reqbufs(&ctx->vq_dst, reqbufs); - s5p_mfc_clock_off(); - return -ENOMEM; - } - if (ctx->dst_bufs_cnt == ctx->total_dpb_count) { - ctx->capture_state = QUEUE_BUFS_MMAPED; - } else { - mfc_err("Not all buffers passed to buf_init\n"); - reqbufs->count = 0; - s5p_mfc_clock_on(); - ret = vb2_reqbufs(&ctx->vq_dst, reqbufs); - s5p_mfc_hw_call(dev->mfc_ops, release_codec_buffers, - ctx); - s5p_mfc_clock_off(); - return -ENOMEM; + vb2_reqbufs(&ctx->vq_dst, reqbufs); + ret = -ENOMEM; + ctx->capture_state = QUEUE_FREE; + goto out; } + + WARN_ON(ctx->dst_bufs_cnt != ctx->total_dpb_count); + ctx->capture_state = QUEUE_BUFS_MMAPED; + if (s5p_mfc_ctx_ready(ctx)) set_work_bit_irqsave(ctx); s5p_mfc_hw_call(dev->mfc_ops, try_run, dev); - s5p_mfc_wait_for_done_ctx(ctx, - S5P_MFC_R2H_CMD_INIT_BUFFERS_RET, 0); + s5p_mfc_wait_for_done_ctx(ctx, S5P_MFC_R2H_CMD_INIT_BUFFERS_RET, + 0); + } else { + mfc_err("Buffers have already been requested\n"); + ret = -EINVAL; } +out: + s5p_mfc_clock_off(); + if (ret) + mfc_err("Failed allocating buffers for CAPTURE queue\n"); return ret; } +/* Reqeust buffers */ +static int vidioc_reqbufs(struct file *file, void *priv, + struct v4l2_requestbuffers *reqbufs) +{ + struct s5p_mfc_dev *dev = video_drvdata(file); + struct s5p_mfc_ctx *ctx = fh_to_ctx(priv); + + if (reqbufs->memory != V4L2_MEMORY_MMAP) { + mfc_err("Only V4L2_MEMORY_MAP is supported\n"); + return -EINVAL; + } + + if (reqbufs->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { + return reqbufs_output(dev, ctx, reqbufs); + } else if (reqbufs->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { + return reqbufs_capture(dev, ctx, reqbufs); + } else { + mfc_err("Invalid type requested\n"); + return -EINVAL; + } +} + /* Query buffer */ static int vidioc_querybuf(struct file *file, void *priv, struct v4l2_buffer *buf) @@ -573,7 +617,7 @@ static int vidioc_querybuf(struct file *file, void *priv, return -EINVAL; } mfc_debug(2, "State: %d, buf->type: %d\n", ctx->state, buf->type); - if (ctx->state == MFCINST_INIT && + if (ctx->state == MFCINST_GOT_INST && buf->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { ret = vb2_querybuf(&ctx->vq_src, buf); } else if (ctx->state == MFCINST_RUNNING && @@ -649,39 +693,11 @@ static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type type) { struct s5p_mfc_ctx *ctx = fh_to_ctx(priv); - struct s5p_mfc_dev *dev = ctx->dev; int ret = -EINVAL; mfc_debug_enter(); - if (type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { - - if (ctx->state == MFCINST_INIT) { - ctx->dst_bufs_cnt = 0; - ctx->src_bufs_cnt = 0; - ctx->capture_state = QUEUE_FREE; - ctx->output_state = QUEUE_FREE; - s5p_mfc_hw_call(dev->mfc_ops, alloc_instance_buffer, - ctx); - s5p_mfc_hw_call(dev->mfc_ops, alloc_dec_temp_buffers, - ctx); - set_work_bit_irqsave(ctx); - s5p_mfc_clean_ctx_int_flags(ctx); - s5p_mfc_hw_call(dev->mfc_ops, try_run, dev); - - if (s5p_mfc_wait_for_done_ctx(ctx, - S5P_MFC_R2H_CMD_OPEN_INSTANCE_RET, 0)) { - /* Error or timeout */ - mfc_err("Error getting instance from hardware\n"); - s5p_mfc_hw_call(dev->mfc_ops, - release_instance_buffer, ctx); - s5p_mfc_hw_call(dev->mfc_ops, - release_dec_desc_buffer, ctx); - return -EIO; - } - mfc_debug(2, "Got instance number: %d\n", ctx->inst_no); - } + if (type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) ret = vb2_streamon(&ctx->vq_src, type); - } else if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) ret = vb2_streamon(&ctx->vq_dst, type); mfc_debug_leave(); @@ -851,6 +867,8 @@ static int vidioc_subscribe_event(struct v4l2_fh *fh, switch (sub->type) { case V4L2_EVENT_EOS: return v4l2_event_subscribe(fh, sub, 2, NULL); + case V4L2_EVENT_SOURCE_CHANGE: + return v4l2_src_change_event_subscribe(fh, sub); default: return -EINVAL; } @@ -1027,7 +1045,7 @@ static int s5p_mfc_start_streaming(struct vb2_queue *q, unsigned int count) return 0; } -static int s5p_mfc_stop_streaming(struct vb2_queue *q) +static void s5p_mfc_stop_streaming(struct vb2_queue *q) { unsigned long flags; struct s5p_mfc_ctx *ctx = fh_to_ctx(q->drv_priv); @@ -1071,7 +1089,6 @@ static int s5p_mfc_stop_streaming(struct vb2_queue *q) } if (aborted) ctx->state = MFCINST_RUNNING; - return 0; } @@ -1191,9 +1208,14 @@ void s5p_mfc_dec_ctrls_delete(struct s5p_mfc_ctx *ctx) void s5p_mfc_dec_init(struct s5p_mfc_ctx *ctx) { struct v4l2_format f; - f.fmt.pix_mp.pixelformat = DEF_SRC_FMT_DEC; + f.fmt.pix_mp.pixelformat = V4L2_PIX_FMT_H264; ctx->src_fmt = find_format(&f, MFC_FMT_DEC); - f.fmt.pix_mp.pixelformat = DEF_DST_FMT_DEC; + if (IS_MFCV8(ctx->dev)) + f.fmt.pix_mp.pixelformat = V4L2_PIX_FMT_NV12M; + else if (IS_MFCV6_PLUS(ctx->dev)) + f.fmt.pix_mp.pixelformat = V4L2_PIX_FMT_NV12MT_16X16; + else + f.fmt.pix_mp.pixelformat = V4L2_PIX_FMT_NV12MT; ctx->dst_fmt = find_format(&f, MFC_FMT_RAW); mfc_debug(2, "Default src_fmt is %x, dest_fmt is %x\n", (unsigned int)ctx->src_fmt, (unsigned int)ctx->dst_fmt); diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c b/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c index df83cd157bab..d26b2484ca10 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c @@ -26,6 +26,7 @@ #include <media/v4l2-ctrls.h> #include <media/videobuf2-core.h> #include "s5p_mfc_common.h" +#include "s5p_mfc_ctrl.h" #include "s5p_mfc_debug.h" #include "s5p_mfc_enc.h" #include "s5p_mfc_intr.h" @@ -41,6 +42,7 @@ static struct s5p_mfc_fmt formats[] = { .codec_mode = S5P_MFC_CODEC_NONE, .type = MFC_FMT_RAW, .num_planes = 2, + .versions = MFC_V6_BIT | MFC_V7_BIT, }, { .name = "4:2:0 2 Planes 64x32 Tiles", @@ -48,6 +50,7 @@ static struct s5p_mfc_fmt formats[] = { .codec_mode = S5P_MFC_CODEC_NONE, .type = MFC_FMT_RAW, .num_planes = 2, + .versions = MFC_V5_BIT, }, { .name = "4:2:0 2 Planes Y/CbCr", @@ -55,6 +58,8 @@ static struct s5p_mfc_fmt formats[] = { .codec_mode = S5P_MFC_CODEC_NONE, .type = MFC_FMT_RAW, .num_planes = 2, + .versions = MFC_V5_BIT | MFC_V6_BIT | MFC_V7_BIT | + MFC_V8_BIT, }, { .name = "4:2:0 2 Planes Y/CrCb", @@ -62,6 +67,8 @@ static struct s5p_mfc_fmt formats[] = { .codec_mode = S5P_MFC_CODEC_NONE, .type = MFC_FMT_RAW, .num_planes = 2, + .versions = MFC_V5_BIT | MFC_V6_BIT | MFC_V7_BIT | + MFC_V8_BIT, }, { .name = "H264 Encoded Stream", @@ -69,6 +76,8 @@ static struct s5p_mfc_fmt formats[] = { .codec_mode = S5P_MFC_CODEC_H264_ENC, .type = MFC_FMT_ENC, .num_planes = 1, + .versions = MFC_V5_BIT | MFC_V6_BIT | MFC_V7_BIT | + MFC_V8_BIT, }, { .name = "MPEG4 Encoded Stream", @@ -76,6 +85,8 @@ static struct s5p_mfc_fmt formats[] = { .codec_mode = S5P_MFC_CODEC_MPEG4_ENC, .type = MFC_FMT_ENC, .num_planes = 1, + .versions = MFC_V5_BIT | MFC_V6_BIT | MFC_V7_BIT | + MFC_V8_BIT, }, { .name = "H263 Encoded Stream", @@ -83,6 +94,8 @@ static struct s5p_mfc_fmt formats[] = { .codec_mode = S5P_MFC_CODEC_H263_ENC, .type = MFC_FMT_ENC, .num_planes = 1, + .versions = MFC_V5_BIT | MFC_V6_BIT | MFC_V7_BIT | + MFC_V8_BIT, }, { .name = "VP8 Encoded Stream", @@ -90,6 +103,7 @@ static struct s5p_mfc_fmt formats[] = { .codec_mode = S5P_MFC_CODEC_VP8_ENC, .type = MFC_FMT_ENC, .num_planes = 1, + .versions = MFC_V7_BIT | MFC_V8_BIT, }, }; @@ -772,13 +786,16 @@ static int enc_post_seq_start(struct s5p_mfc_ctx *ctx) if (p->seq_hdr_mode == V4L2_MPEG_VIDEO_HEADER_MODE_SEPARATE) { spin_lock_irqsave(&dev->irqlock, flags); - dst_mb = list_entry(ctx->dst_queue.next, - struct s5p_mfc_buf, list); - list_del(&dst_mb->list); - ctx->dst_queue_cnt--; - vb2_set_plane_payload(dst_mb->b, 0, - s5p_mfc_hw_call(dev->mfc_ops, get_enc_strm_size, dev)); - vb2_buffer_done(dst_mb->b, VB2_BUF_STATE_DONE); + if (!list_empty(&ctx->dst_queue)) { + dst_mb = list_entry(ctx->dst_queue.next, + struct s5p_mfc_buf, list); + list_del(&dst_mb->list); + ctx->dst_queue_cnt--; + vb2_set_plane_payload(dst_mb->b, 0, + s5p_mfc_hw_call(dev->mfc_ops, get_enc_strm_size, + dev)); + vb2_buffer_done(dst_mb->b, VB2_BUF_STATE_DONE); + } spin_unlock_irqrestore(&dev->irqlock, flags); } @@ -883,8 +900,7 @@ static int enc_post_frame_start(struct s5p_mfc_ctx *ctx) mfc_debug(2, "enc src count: %d, enc ref count: %d\n", ctx->src_queue_cnt, ctx->ref_queue_cnt); } - if (strm_size > 0) { - /* at least one more dest. buffers exist always */ + if ((ctx->dst_queue_cnt > 0) && (strm_size > 0)) { mb_entry = list_entry(ctx->dst_queue.next, struct s5p_mfc_buf, list); list_del(&mb_entry->list); @@ -937,8 +953,10 @@ static int vidioc_querycap(struct file *file, void *priv, return 0; } -static int vidioc_enum_fmt(struct v4l2_fmtdesc *f, bool mplane, bool out) +static int vidioc_enum_fmt(struct file *file, struct v4l2_fmtdesc *f, + bool mplane, bool out) { + struct s5p_mfc_dev *dev = video_drvdata(file); struct s5p_mfc_fmt *fmt; int i, j = 0; @@ -951,6 +969,9 @@ static int vidioc_enum_fmt(struct v4l2_fmtdesc *f, bool mplane, bool out) continue; else if (!out && formats[i].type != MFC_FMT_ENC) continue; + else if ((dev->variant->version_bit & formats[i].versions) == 0) + continue; + if (j == f->index) { fmt = &formats[i]; strlcpy(f->description, fmt->name, @@ -966,25 +987,25 @@ static int vidioc_enum_fmt(struct v4l2_fmtdesc *f, bool mplane, bool out) static int vidioc_enum_fmt_vid_cap(struct file *file, void *pirv, struct v4l2_fmtdesc *f) { - return vidioc_enum_fmt(f, false, false); + return vidioc_enum_fmt(file, f, false, false); } static int vidioc_enum_fmt_vid_cap_mplane(struct file *file, void *pirv, struct v4l2_fmtdesc *f) { - return vidioc_enum_fmt(f, true, false); + return vidioc_enum_fmt(file, f, true, false); } static int vidioc_enum_fmt_vid_out(struct file *file, void *prov, struct v4l2_fmtdesc *f) { - return vidioc_enum_fmt(f, false, true); + return vidioc_enum_fmt(file, f, false, true); } static int vidioc_enum_fmt_vid_out_mplane(struct file *file, void *prov, struct v4l2_fmtdesc *f) { - return vidioc_enum_fmt(f, true, true); + return vidioc_enum_fmt(file, f, true, true); } static int vidioc_g_fmt(struct file *file, void *priv, struct v4l2_format *f) @@ -1035,16 +1056,14 @@ static int vidioc_try_fmt(struct file *file, void *priv, struct v4l2_format *f) mfc_err("failed to try output format\n"); return -EINVAL; } - - if (!IS_MFCV7(dev) && (fmt->fourcc == V4L2_PIX_FMT_VP8)) { - mfc_err("VP8 is supported only in MFC v7\n"); - return -EINVAL; - } - if (pix_fmt_mp->plane_fmt[0].sizeimage == 0) { mfc_err("must be set encoding output size\n"); return -EINVAL; } + if ((dev->variant->version_bit & fmt->versions) == 0) { + mfc_err("Unsupported format by this MFC version.\n"); + return -EINVAL; + } pix_fmt_mp->plane_fmt[0].bytesperline = pix_fmt_mp->plane_fmt[0].sizeimage; @@ -1055,22 +1074,15 @@ static int vidioc_try_fmt(struct file *file, void *priv, struct v4l2_format *f) return -EINVAL; } - if (!IS_MFCV6_PLUS(dev)) { - if (fmt->fourcc == V4L2_PIX_FMT_NV12MT_16X16) { - mfc_err("Not supported format.\n"); - return -EINVAL; - } - } else if (IS_MFCV6_PLUS(dev)) { - if (fmt->fourcc == V4L2_PIX_FMT_NV12MT) { - mfc_err("Not supported format.\n"); - return -EINVAL; - } - } - if (fmt->num_planes != pix_fmt_mp->num_planes) { mfc_err("failed to try output format\n"); return -EINVAL; } + if ((dev->variant->version_bit & fmt->versions) == 0) { + mfc_err("Unsupported format by this MFC version.\n"); + return -EINVAL; + } + v4l_bound_align_image(&pix_fmt_mp->width, 8, 1920, 1, &pix_fmt_mp->height, 4, 1080, 1, 0); } else { @@ -1104,20 +1116,7 @@ static int vidioc_s_fmt(struct file *file, void *priv, struct v4l2_format *f) pix_fmt_mp->plane_fmt[0].bytesperline = 0; ctx->dst_bufs_cnt = 0; ctx->capture_state = QUEUE_FREE; - s5p_mfc_hw_call(dev->mfc_ops, alloc_instance_buffer, ctx); - set_work_bit_irqsave(ctx); - s5p_mfc_clean_ctx_int_flags(ctx); - s5p_mfc_hw_call(dev->mfc_ops, try_run, dev); - if (s5p_mfc_wait_for_done_ctx(ctx, \ - S5P_MFC_R2H_CMD_OPEN_INSTANCE_RET, 1)) { - /* Error or timeout */ - mfc_err("Error getting instance from hardware\n"); - s5p_mfc_hw_call(dev->mfc_ops, release_instance_buffer, - ctx); - ret = -EIO; - goto out; - } - mfc_debug(2, "Got instance number: %d\n", ctx->inst_no); + ret = s5p_mfc_open_mfc_inst(dev, ctx); } else if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { /* src_fmt is validated by call to vidioc_try_fmt */ ctx->src_fmt = find_format(f, MFC_FMT_RAW); @@ -1138,7 +1137,7 @@ static int vidioc_s_fmt(struct file *file, void *priv, struct v4l2_format *f) ctx->output_state = QUEUE_FREE; } else { mfc_err("invalid buf type\n"); - return -EINVAL; + ret = -EINVAL; } out: mfc_debug_leave(); @@ -1954,7 +1953,7 @@ static int s5p_mfc_start_streaming(struct vb2_queue *q, unsigned int count) return 0; } -static int s5p_mfc_stop_streaming(struct vb2_queue *q) +static void s5p_mfc_stop_streaming(struct vb2_queue *q) { unsigned long flags; struct s5p_mfc_ctx *ctx = fh_to_ctx(q->drv_priv); @@ -1983,7 +1982,6 @@ static int s5p_mfc_stop_streaming(struct vb2_queue *q) ctx->src_queue_cnt = 0; } spin_unlock_irqrestore(&dev->irqlock, flags); - return 0; } static void s5p_mfc_buf_queue(struct vb2_buffer *vb) diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_opr.c b/drivers/media/platform/s5p-mfc/s5p_mfc_opr.c index 3c01c339d696..c9a227428e6a 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_opr.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_opr.c @@ -31,6 +31,12 @@ void s5p_mfc_init_hw_ops(struct s5p_mfc_dev *dev) dev->mfc_ops = s5p_mfc_ops; } +void s5p_mfc_init_regs(struct s5p_mfc_dev *dev) +{ + if (IS_MFCV6_PLUS(dev)) + dev->mfc_regs = s5p_mfc_init_regs_v6_plus(dev); +} + int s5p_mfc_alloc_priv_buf(struct device *dev, struct s5p_mfc_priv_buf *b) { diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_opr.h b/drivers/media/platform/s5p-mfc/s5p_mfc_opr.h index 754c540e7a7e..7a7ad32ee608 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_opr.h +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_opr.h @@ -17,6 +17,259 @@ #include "s5p_mfc_common.h" +struct s5p_mfc_regs { + + /* codec common registers */ + void *risc_on; + void *risc2host_int; + void *host2risc_int; + void *risc_base_address; + void *mfc_reset; + void *host2risc_command; + void *risc2host_command; + void *mfc_bus_reset_ctrl; + void *firmware_version; + void *instance_id; + void *codec_type; + void *context_mem_addr; + void *context_mem_size; + void *pixel_format; + void *metadata_enable; + void *mfc_version; + void *dbg_info_enable; + void *dbg_buffer_addr; + void *dbg_buffer_size; + void *hed_control; + void *mfc_timeout_value; + void *hed_shared_mem_addr; + void *dis_shared_mem_addr;/* only v7 */ + void *ret_instance_id; + void *error_code; + void *dbg_buffer_output_size; + void *metadata_status; + void *metadata_addr_mb_info; + void *metadata_size_mb_info; + void *dbg_info_stage_counter; + + /* decoder registers */ + void *d_crc_ctrl; + void *d_dec_options; + void *d_display_delay; + void *d_set_frame_width; + void *d_set_frame_height; + void *d_sei_enable; + void *d_min_num_dpb; + void *d_min_first_plane_dpb_size; + void *d_min_second_plane_dpb_size; + void *d_min_third_plane_dpb_size;/* only v8 */ + void *d_min_num_mv; + void *d_mvc_num_views; + void *d_min_num_dis;/* only v7 */ + void *d_min_first_dis_size;/* only v7 */ + void *d_min_second_dis_size;/* only v7 */ + void *d_min_third_dis_size;/* only v7 */ + void *d_post_filter_luma_dpb0;/* v7 and v8 */ + void *d_post_filter_luma_dpb1;/* v7 and v8 */ + void *d_post_filter_luma_dpb2;/* only v7 */ + void *d_post_filter_chroma_dpb0;/* v7 and v8 */ + void *d_post_filter_chroma_dpb1;/* v7 and v8 */ + void *d_post_filter_chroma_dpb2;/* only v7 */ + void *d_num_dpb; + void *d_num_mv; + void *d_init_buffer_options; + void *d_first_plane_dpb_stride_size;/* only v8 */ + void *d_second_plane_dpb_stride_size;/* only v8 */ + void *d_third_plane_dpb_stride_size;/* only v8 */ + void *d_first_plane_dpb_size; + void *d_second_plane_dpb_size; + void *d_third_plane_dpb_size;/* only v8 */ + void *d_mv_buffer_size; + void *d_first_plane_dpb; + void *d_second_plane_dpb; + void *d_third_plane_dpb; + void *d_mv_buffer; + void *d_scratch_buffer_addr; + void *d_scratch_buffer_size; + void *d_metadata_buffer_addr; + void *d_metadata_buffer_size; + void *d_nal_start_options;/* v7 and v8 */ + void *d_cpb_buffer_addr; + void *d_cpb_buffer_size; + void *d_available_dpb_flag_upper; + void *d_available_dpb_flag_lower; + void *d_cpb_buffer_offset; + void *d_slice_if_enable; + void *d_picture_tag; + void *d_stream_data_size; + void *d_dynamic_dpb_flag_upper;/* v7 and v8 */ + void *d_dynamic_dpb_flag_lower;/* v7 and v8 */ + void *d_display_frame_width; + void *d_display_frame_height; + void *d_display_status; + void *d_display_first_plane_addr; + void *d_display_second_plane_addr; + void *d_display_third_plane_addr;/* only v8 */ + void *d_display_frame_type; + void *d_display_crop_info1; + void *d_display_crop_info2; + void *d_display_picture_profile; + void *d_display_luma_crc;/* v7 and v8 */ + void *d_display_chroma0_crc;/* v7 and v8 */ + void *d_display_chroma1_crc;/* only v8 */ + void *d_display_luma_crc_top;/* only v6 */ + void *d_display_chroma_crc_top;/* only v6 */ + void *d_display_luma_crc_bot;/* only v6 */ + void *d_display_chroma_crc_bot;/* only v6 */ + void *d_display_aspect_ratio; + void *d_display_extended_ar; + void *d_decoded_frame_width; + void *d_decoded_frame_height; + void *d_decoded_status; + void *d_decoded_first_plane_addr; + void *d_decoded_second_plane_addr; + void *d_decoded_third_plane_addr;/* only v8 */ + void *d_decoded_frame_type; + void *d_decoded_crop_info1; + void *d_decoded_crop_info2; + void *d_decoded_picture_profile; + void *d_decoded_nal_size; + void *d_decoded_luma_crc; + void *d_decoded_chroma0_crc; + void *d_decoded_chroma1_crc;/* only v8 */ + void *d_ret_picture_tag_top; + void *d_ret_picture_tag_bot; + void *d_ret_picture_time_top; + void *d_ret_picture_time_bot; + void *d_chroma_format; + void *d_vc1_info;/* v7 and v8 */ + void *d_mpeg4_info; + void *d_h264_info; + void *d_metadata_addr_concealed_mb; + void *d_metadata_size_concealed_mb; + void *d_metadata_addr_vc1_param; + void *d_metadata_size_vc1_param; + void *d_metadata_addr_sei_nal; + void *d_metadata_size_sei_nal; + void *d_metadata_addr_vui; + void *d_metadata_size_vui; + void *d_metadata_addr_mvcvui;/* v7 and v8 */ + void *d_metadata_size_mvcvui;/* v7 and v8 */ + void *d_mvc_view_id; + void *d_frame_pack_sei_avail; + void *d_frame_pack_arrgment_id; + void *d_frame_pack_sei_info; + void *d_frame_pack_grid_pos; + void *d_display_recovery_sei_info;/* v7 and v8 */ + void *d_decoded_recovery_sei_info;/* v7 and v8 */ + void *d_display_first_addr;/* only v7 */ + void *d_display_second_addr;/* only v7 */ + void *d_display_third_addr;/* only v7 */ + void *d_decoded_first_addr;/* only v7 */ + void *d_decoded_second_addr;/* only v7 */ + void *d_decoded_third_addr;/* only v7 */ + void *d_used_dpb_flag_upper;/* v7 and v8 */ + void *d_used_dpb_flag_lower;/* v7 and v8 */ + + /* encoder registers */ + void *e_frame_width; + void *e_frame_height; + void *e_cropped_frame_width; + void *e_cropped_frame_height; + void *e_frame_crop_offset; + void *e_enc_options; + void *e_picture_profile; + void *e_vbv_buffer_size; + void *e_vbv_init_delay; + void *e_fixed_picture_qp; + void *e_rc_config; + void *e_rc_qp_bound; + void *e_rc_qp_bound_pb;/* v7 and v8 */ + void *e_rc_mode; + void *e_mb_rc_config; + void *e_padding_ctrl; + void *e_air_threshold; + void *e_mv_hor_range; + void *e_mv_ver_range; + void *e_num_dpb; + void *e_luma_dpb; + void *e_chroma_dpb; + void *e_me_buffer; + void *e_scratch_buffer_addr; + void *e_scratch_buffer_size; + void *e_tmv_buffer0; + void *e_tmv_buffer1; + void *e_ir_buffer_addr;/* v7 and v8 */ + void *e_source_first_plane_addr; + void *e_source_second_plane_addr; + void *e_source_third_plane_addr;/* v7 and v8 */ + void *e_source_first_plane_stride;/* v7 and v8 */ + void *e_source_second_plane_stride;/* v7 and v8 */ + void *e_source_third_plane_stride;/* v7 and v8 */ + void *e_stream_buffer_addr; + void *e_stream_buffer_size; + void *e_roi_buffer_addr; + void *e_param_change; + void *e_ir_size; + void *e_gop_config; + void *e_mslice_mode; + void *e_mslice_size_mb; + void *e_mslice_size_bits; + void *e_frame_insertion; + void *e_rc_frame_rate; + void *e_rc_bit_rate; + void *e_rc_roi_ctrl; + void *e_picture_tag; + void *e_bit_count_enable; + void *e_max_bit_count; + void *e_min_bit_count; + void *e_metadata_buffer_addr; + void *e_metadata_buffer_size; + void *e_encoded_source_first_plane_addr; + void *e_encoded_source_second_plane_addr; + void *e_encoded_source_third_plane_addr;/* v7 and v8 */ + void *e_stream_size; + void *e_slice_type; + void *e_picture_count; + void *e_ret_picture_tag; + void *e_stream_buffer_write_pointer; /* only v6 */ + void *e_recon_luma_dpb_addr; + void *e_recon_chroma_dpb_addr; + void *e_metadata_addr_enc_slice; + void *e_metadata_size_enc_slice; + void *e_mpeg4_options; + void *e_mpeg4_hec_period; + void *e_aspect_ratio; + void *e_extended_sar; + void *e_h264_options; + void *e_h264_options_2;/* v7 and v8 */ + void *e_h264_lf_alpha_offset; + void *e_h264_lf_beta_offset; + void *e_h264_i_period; + void *e_h264_fmo_slice_grp_map_type; + void *e_h264_fmo_num_slice_grp_minus1; + void *e_h264_fmo_slice_grp_change_dir; + void *e_h264_fmo_slice_grp_change_rate_minus1; + void *e_h264_fmo_run_length_minus1_0; + void *e_h264_aso_slice_order_0; + void *e_h264_chroma_qp_offset; + void *e_h264_num_t_layer; + void *e_h264_hierarchical_qp_layer0; + void *e_h264_frame_packing_sei_info; + void *e_h264_nal_control;/* v7 and v8 */ + void *e_mvc_frame_qp_view1; + void *e_mvc_rc_bit_rate_view1; + void *e_mvc_rc_qbound_view1; + void *e_mvc_rc_mode_view1; + void *e_mvc_inter_view_prediction_on; + void *e_vp8_options;/* v7 and v8 */ + void *e_vp8_filter_options;/* v7 and v8 */ + void *e_vp8_golden_frame_option;/* v7 and v8 */ + void *e_vp8_num_t_layer;/* v7 and v8 */ + void *e_vp8_hierarchical_qp_layer0;/* v7 and v8 */ + void *e_vp8_hierarchical_qp_layer1;/* v7 and v8 */ + void *e_vp8_hierarchical_qp_layer2;/* v7 and v8 */ +}; + struct s5p_mfc_hw_ops { int (*alloc_dec_temp_buffers)(struct s5p_mfc_ctx *ctx); void (*release_dec_desc_buffer)(struct s5p_mfc_ctx *ctx); @@ -80,6 +333,7 @@ struct s5p_mfc_hw_ops { }; void s5p_mfc_init_hw_ops(struct s5p_mfc_dev *dev); +void s5p_mfc_init_regs(struct s5p_mfc_dev *dev); int s5p_mfc_alloc_priv_buf(struct device *dev, struct s5p_mfc_priv_buf *b); void s5p_mfc_release_priv_buf(struct device *dev, diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c b/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c index f64621ae9b5a..4f5e0ead90c6 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c @@ -43,10 +43,10 @@ } while (0) #endif /* S5P_MFC_DEBUG_REGWRITE */ -#define READL(offset) readl(dev->regs_base + (offset)) -#define WRITEL(data, offset) writel((data), dev->regs_base + (offset)) -#define OFFSETA(x) (((x) - dev->port_a) >> S5P_FIMV_MEM_OFFSET) -#define OFFSETB(x) (((x) - dev->port_b) >> S5P_FIMV_MEM_OFFSET) +#define READL(reg) \ + (WARN_ON_ONCE(!(reg)) ? 0 : readl(reg)) +#define WRITEL(data, reg) \ + (WARN_ON_ONCE(!(reg)) ? 0 : writel((data), (reg))) /* Allocate temporary buffers for decoding */ static int s5p_mfc_alloc_dec_temp_buffers_v6(struct s5p_mfc_ctx *ctx) @@ -77,7 +77,12 @@ static int s5p_mfc_alloc_codec_buffers_v6(struct s5p_mfc_ctx *ctx) ctx->luma_size, ctx->chroma_size, ctx->mv_size); mfc_debug(2, "Totals bufs: %d\n", ctx->total_dpb_count); } else if (ctx->type == MFCINST_ENCODER) { - ctx->tmv_buffer_size = S5P_FIMV_NUM_TMV_BUFFERS_V6 * + if (IS_MFCV8(dev)) + ctx->tmv_buffer_size = S5P_FIMV_NUM_TMV_BUFFERS_V6 * + ALIGN(S5P_FIMV_TMV_BUFFER_SIZE_V8(mb_width, mb_height), + S5P_FIMV_TMV_BUFFER_ALIGN_V6); + else + ctx->tmv_buffer_size = S5P_FIMV_NUM_TMV_BUFFERS_V6 * ALIGN(S5P_FIMV_TMV_BUFFER_SIZE_V6(mb_width, mb_height), S5P_FIMV_TMV_BUFFER_ALIGN_V6); @@ -87,10 +92,16 @@ static int s5p_mfc_alloc_codec_buffers_v6(struct s5p_mfc_ctx *ctx) ctx->chroma_dpb_size = ALIGN((mb_width * mb_height) * S5P_FIMV_CHROMA_MB_TO_PIXEL_V6, S5P_FIMV_CHROMA_DPB_BUFFER_ALIGN_V6); - ctx->me_buffer_size = ALIGN(S5P_FIMV_ME_BUFFER_SIZE_V6( - ctx->img_width, ctx->img_height, - mb_width, mb_height), - S5P_FIMV_ME_BUFFER_ALIGN_V6); + if (IS_MFCV8(dev)) + ctx->me_buffer_size = ALIGN(S5P_FIMV_ME_BUFFER_SIZE_V8( + ctx->img_width, ctx->img_height, + mb_width, mb_height), + S5P_FIMV_ME_BUFFER_ALIGN_V6); + else + ctx->me_buffer_size = ALIGN(S5P_FIMV_ME_BUFFER_SIZE_V6( + ctx->img_width, ctx->img_height, + mb_width, mb_height), + S5P_FIMV_ME_BUFFER_ALIGN_V6); mfc_debug(2, "recon luma size: %d chroma size: %d\n", ctx->luma_dpb_size, ctx->chroma_dpb_size); @@ -102,8 +113,14 @@ static int s5p_mfc_alloc_codec_buffers_v6(struct s5p_mfc_ctx *ctx) switch (ctx->codec_mode) { case S5P_MFC_CODEC_H264_DEC: case S5P_MFC_CODEC_H264_MVC_DEC: - ctx->scratch_buf_size = - S5P_FIMV_SCRATCH_BUF_SIZE_H264_DEC_V6( + if (IS_MFCV8(dev)) + ctx->scratch_buf_size = + S5P_FIMV_SCRATCH_BUF_SIZE_H264_DEC_V8( + mb_width, + mb_height); + else + ctx->scratch_buf_size = + S5P_FIMV_SCRATCH_BUF_SIZE_H264_DEC_V6( mb_width, mb_height); ctx->scratch_buf_size = ALIGN(ctx->scratch_buf_size, @@ -113,7 +130,7 @@ static int s5p_mfc_alloc_codec_buffers_v6(struct s5p_mfc_ctx *ctx) (ctx->mv_count * ctx->mv_size); break; case S5P_MFC_CODEC_MPEG4_DEC: - if (IS_MFCV7(dev)) { + if (IS_MFCV7_PLUS(dev)) { ctx->scratch_buf_size = S5P_FIMV_SCRATCH_BUF_SIZE_MPEG4_DEC_V7( mb_width, @@ -153,19 +170,31 @@ static int s5p_mfc_alloc_codec_buffers_v6(struct s5p_mfc_ctx *ctx) ctx->bank1.size = ctx->scratch_buf_size; break; case S5P_MFC_CODEC_VP8_DEC: - ctx->scratch_buf_size = - S5P_FIMV_SCRATCH_BUF_SIZE_VP8_DEC_V6( - mb_width, - mb_height); + if (IS_MFCV8(dev)) + ctx->scratch_buf_size = + S5P_FIMV_SCRATCH_BUF_SIZE_VP8_DEC_V8( + mb_width, + mb_height); + else + ctx->scratch_buf_size = + S5P_FIMV_SCRATCH_BUF_SIZE_VP8_DEC_V6( + mb_width, + mb_height); ctx->scratch_buf_size = ALIGN(ctx->scratch_buf_size, S5P_FIMV_SCRATCH_BUFFER_ALIGN_V6); ctx->bank1.size = ctx->scratch_buf_size; break; case S5P_MFC_CODEC_H264_ENC: - ctx->scratch_buf_size = - S5P_FIMV_SCRATCH_BUF_SIZE_H264_ENC_V6( + if (IS_MFCV8(dev)) + ctx->scratch_buf_size = + S5P_FIMV_SCRATCH_BUF_SIZE_H264_ENC_V8( mb_width, mb_height); + else + ctx->scratch_buf_size = + S5P_FIMV_SCRATCH_BUF_SIZE_H264_ENC_V6( + mb_width, + mb_height); ctx->scratch_buf_size = ALIGN(ctx->scratch_buf_size, S5P_FIMV_SCRATCH_BUFFER_ALIGN_V6); ctx->bank1.size = @@ -189,10 +218,16 @@ static int s5p_mfc_alloc_codec_buffers_v6(struct s5p_mfc_ctx *ctx) ctx->bank2.size = 0; break; case S5P_MFC_CODEC_VP8_ENC: - ctx->scratch_buf_size = - S5P_FIMV_SCRATCH_BUF_SIZE_VP8_ENC_V7( + if (IS_MFCV8(dev)) + ctx->scratch_buf_size = + S5P_FIMV_SCRATCH_BUF_SIZE_VP8_ENC_V8( mb_width, mb_height); + else + ctx->scratch_buf_size = + S5P_FIMV_SCRATCH_BUF_SIZE_VP8_ENC_V7( + mb_width, + mb_height); ctx->scratch_buf_size = ALIGN(ctx->scratch_buf_size, S5P_FIMV_SCRATCH_BUFFER_ALIGN_V6); ctx->bank1.size = @@ -332,6 +367,12 @@ static void s5p_mfc_dec_calc_dpb_size_v6(struct s5p_mfc_ctx *ctx) ctx->luma_size = calc_plane(ctx->img_width, ctx->img_height); ctx->chroma_size = calc_plane(ctx->img_width, (ctx->img_height >> 1)); + if (IS_MFCV8(ctx->dev)) { + /* MFCv8 needs additional 64 bytes for luma,chroma dpb*/ + ctx->luma_size += S5P_FIMV_D_ALIGN_PLANE_SIZE_V8; + ctx->chroma_size += S5P_FIMV_D_ALIGN_PLANE_SIZE_V8; + } + if (ctx->codec_mode == S5P_MFC_CODEC_H264_DEC || ctx->codec_mode == S5P_MFC_CODEC_H264_MVC_DEC) { ctx->mv_size = S5P_MFC_DEC_MV_SIZE_V6(ctx->img_width, @@ -354,7 +395,7 @@ static void s5p_mfc_enc_calc_src_size_v6(struct s5p_mfc_ctx *ctx) ctx->chroma_size = ALIGN((mb_width * mb_height) * 128, 256); /* MFCv7 needs pad bytes for Luma and Chroma */ - if (IS_MFCV7(ctx->dev)) { + if (IS_MFCV7_PLUS(ctx->dev)) { ctx->luma_size += MFC_LUMA_PAD_BYTES_V7; ctx->chroma_size += MFC_CHROMA_PAD_BYTES_V7; } @@ -366,16 +407,17 @@ static int s5p_mfc_set_dec_stream_buffer_v6(struct s5p_mfc_ctx *ctx, unsigned int strm_size) { struct s5p_mfc_dev *dev = ctx->dev; + const struct s5p_mfc_regs *mfc_regs = dev->mfc_regs; struct s5p_mfc_buf_size *buf_size = dev->variant->buf_size; mfc_debug_enter(); mfc_debug(2, "inst_no: %d, buf_addr: 0x%08x,\n" "buf_size: 0x%08x (%d)\n", ctx->inst_no, buf_addr, strm_size, strm_size); - WRITEL(strm_size, S5P_FIMV_D_STREAM_DATA_SIZE_V6); - WRITEL(buf_addr, S5P_FIMV_D_CPB_BUFFER_ADDR_V6); - WRITEL(buf_size->cpb, S5P_FIMV_D_CPB_BUFFER_SIZE_V6); - WRITEL(start_num_byte, S5P_FIMV_D_CPB_BUFFER_OFFSET_V6); + WRITEL(strm_size, mfc_regs->d_stream_data_size); + WRITEL(buf_addr, mfc_regs->d_cpb_buffer_addr); + WRITEL(buf_size->cpb, mfc_regs->d_cpb_buffer_size); + WRITEL(start_num_byte, mfc_regs->d_cpb_buffer_offset); mfc_debug_leave(); return 0; @@ -387,6 +429,7 @@ static int s5p_mfc_set_dec_frame_buffer_v6(struct s5p_mfc_ctx *ctx) unsigned int frame_size, i; unsigned int frame_size_ch, frame_size_mv; struct s5p_mfc_dev *dev = ctx->dev; + const struct s5p_mfc_regs *mfc_regs = dev->mfc_regs; size_t buf_addr1; int buf_size1; int align_gap; @@ -398,19 +441,27 @@ static int s5p_mfc_set_dec_frame_buffer_v6(struct s5p_mfc_ctx *ctx) mfc_debug(2, "Total DPB COUNT: %d\n", ctx->total_dpb_count); mfc_debug(2, "Setting display delay to %d\n", ctx->display_delay); - WRITEL(ctx->total_dpb_count, S5P_FIMV_D_NUM_DPB_V6); - WRITEL(ctx->luma_size, S5P_FIMV_D_LUMA_DPB_SIZE_V6); - WRITEL(ctx->chroma_size, S5P_FIMV_D_CHROMA_DPB_SIZE_V6); + WRITEL(ctx->total_dpb_count, mfc_regs->d_num_dpb); + WRITEL(ctx->luma_size, mfc_regs->d_first_plane_dpb_size); + WRITEL(ctx->chroma_size, mfc_regs->d_second_plane_dpb_size); + + WRITEL(buf_addr1, mfc_regs->d_scratch_buffer_addr); + WRITEL(ctx->scratch_buf_size, mfc_regs->d_scratch_buffer_size); + + if (IS_MFCV8(dev)) { + WRITEL(ctx->img_width, + mfc_regs->d_first_plane_dpb_stride_size); + WRITEL(ctx->img_width, + mfc_regs->d_second_plane_dpb_stride_size); + } - WRITEL(buf_addr1, S5P_FIMV_D_SCRATCH_BUFFER_ADDR_V6); - WRITEL(ctx->scratch_buf_size, S5P_FIMV_D_SCRATCH_BUFFER_SIZE_V6); buf_addr1 += ctx->scratch_buf_size; buf_size1 -= ctx->scratch_buf_size; if (ctx->codec_mode == S5P_FIMV_CODEC_H264_DEC || ctx->codec_mode == S5P_FIMV_CODEC_H264_MVC_DEC){ - WRITEL(ctx->mv_size, S5P_FIMV_D_MV_BUFFER_SIZE_V6); - WRITEL(ctx->mv_count, S5P_FIMV_D_NUM_MV_V6); + WRITEL(ctx->mv_size, mfc_regs->d_mv_buffer_size); + WRITEL(ctx->mv_count, mfc_regs->d_num_mv); } frame_size = ctx->luma_size; @@ -424,11 +475,11 @@ static int s5p_mfc_set_dec_frame_buffer_v6(struct s5p_mfc_ctx *ctx) mfc_debug(2, "Luma %d: %x\n", i, ctx->dst_bufs[i].cookie.raw.luma); WRITEL(ctx->dst_bufs[i].cookie.raw.luma, - S5P_FIMV_D_LUMA_DPB_V6 + i * 4); + mfc_regs->d_first_plane_dpb + i * 4); mfc_debug(2, "\tChroma %d: %x\n", i, ctx->dst_bufs[i].cookie.raw.chroma); WRITEL(ctx->dst_bufs[i].cookie.raw.chroma, - S5P_FIMV_D_CHROMA_DPB_V6 + i * 4); + mfc_regs->d_second_plane_dpb + i * 4); } if (ctx->codec_mode == S5P_MFC_CODEC_H264_DEC || ctx->codec_mode == S5P_MFC_CODEC_H264_MVC_DEC) { @@ -441,7 +492,7 @@ static int s5p_mfc_set_dec_frame_buffer_v6(struct s5p_mfc_ctx *ctx) mfc_debug(2, "\tBuf1: %x, size: %d\n", buf_addr1, buf_size1); - WRITEL(buf_addr1, S5P_FIMV_D_MV_BUFFER_V6 + i * 4); + WRITEL(buf_addr1, mfc_regs->d_mv_buffer + i * 4); buf_addr1 += frame_size_mv; buf_size1 -= frame_size_mv; } @@ -454,7 +505,7 @@ static int s5p_mfc_set_dec_frame_buffer_v6(struct s5p_mfc_ctx *ctx) return -ENOMEM; } - WRITEL(ctx->inst_no, S5P_FIMV_INSTANCE_ID_V6); + WRITEL(ctx->inst_no, mfc_regs->instance_id); s5p_mfc_hw_call(dev->mfc_cmds, cmd_host2risc, dev, S5P_FIMV_CH_INIT_BUFS_V6, NULL); @@ -467,9 +518,10 @@ static int s5p_mfc_set_enc_stream_buffer_v6(struct s5p_mfc_ctx *ctx, unsigned long addr, unsigned int size) { struct s5p_mfc_dev *dev = ctx->dev; + const struct s5p_mfc_regs *mfc_regs = dev->mfc_regs; - WRITEL(addr, S5P_FIMV_E_STREAM_BUFFER_ADDR_V6); /* 16B align */ - WRITEL(size, S5P_FIMV_E_STREAM_BUFFER_SIZE_V6); + WRITEL(addr, mfc_regs->e_stream_buffer_addr); /* 16B align */ + WRITEL(size, mfc_regs->e_stream_buffer_size); mfc_debug(2, "stream buf addr: 0x%08lx, size: 0x%d\n", addr, size); @@ -481,14 +533,10 @@ static void s5p_mfc_set_enc_frame_buffer_v6(struct s5p_mfc_ctx *ctx, unsigned long y_addr, unsigned long c_addr) { struct s5p_mfc_dev *dev = ctx->dev; + const struct s5p_mfc_regs *mfc_regs = dev->mfc_regs; - if (IS_MFCV7(dev)) { - WRITEL(y_addr, S5P_FIMV_E_SOURCE_FIRST_ADDR_V7); - WRITEL(c_addr, S5P_FIMV_E_SOURCE_SECOND_ADDR_V7); - } else { - WRITEL(y_addr, S5P_FIMV_E_SOURCE_LUMA_ADDR_V6); - WRITEL(c_addr, S5P_FIMV_E_SOURCE_CHROMA_ADDR_V6); - } + WRITEL(y_addr, mfc_regs->e_source_first_plane_addr); + WRITEL(c_addr, mfc_regs->e_source_second_plane_addr); mfc_debug(2, "enc src y buf addr: 0x%08lx\n", y_addr); mfc_debug(2, "enc src c buf addr: 0x%08lx\n", c_addr); @@ -498,18 +546,14 @@ static void s5p_mfc_get_enc_frame_buffer_v6(struct s5p_mfc_ctx *ctx, unsigned long *y_addr, unsigned long *c_addr) { struct s5p_mfc_dev *dev = ctx->dev; + const struct s5p_mfc_regs *mfc_regs = dev->mfc_regs; unsigned long enc_recon_y_addr, enc_recon_c_addr; - if (IS_MFCV7(dev)) { - *y_addr = READL(S5P_FIMV_E_ENCODED_SOURCE_FIRST_ADDR_V7); - *c_addr = READL(S5P_FIMV_E_ENCODED_SOURCE_SECOND_ADDR_V7); - } else { - *y_addr = READL(S5P_FIMV_E_ENCODED_SOURCE_LUMA_ADDR_V6); - *c_addr = READL(S5P_FIMV_E_ENCODED_SOURCE_CHROMA_ADDR_V6); - } + *y_addr = READL(mfc_regs->e_encoded_source_first_plane_addr); + *c_addr = READL(mfc_regs->e_encoded_source_second_plane_addr); - enc_recon_y_addr = READL(S5P_FIMV_E_RECON_LUMA_DPB_ADDR_V6); - enc_recon_c_addr = READL(S5P_FIMV_E_RECON_CHROMA_DPB_ADDR_V6); + enc_recon_y_addr = READL(mfc_regs->e_recon_luma_dpb_addr); + enc_recon_c_addr = READL(mfc_regs->e_recon_chroma_dpb_addr); mfc_debug(2, "recon y addr: 0x%08lx\n", enc_recon_y_addr); mfc_debug(2, "recon c addr: 0x%08lx\n", enc_recon_c_addr); @@ -519,6 +563,7 @@ static void s5p_mfc_get_enc_frame_buffer_v6(struct s5p_mfc_ctx *ctx, static int s5p_mfc_set_enc_ref_buffer_v6(struct s5p_mfc_ctx *ctx) { struct s5p_mfc_dev *dev = ctx->dev; + const struct s5p_mfc_regs *mfc_regs = dev->mfc_regs; size_t buf_addr1; int i, buf_size1; @@ -530,24 +575,24 @@ static int s5p_mfc_set_enc_ref_buffer_v6(struct s5p_mfc_ctx *ctx) mfc_debug(2, "Buf1: %p (%d)\n", (void *)buf_addr1, buf_size1); for (i = 0; i < ctx->pb_count; i++) { - WRITEL(buf_addr1, S5P_FIMV_E_LUMA_DPB_V6 + (4 * i)); + WRITEL(buf_addr1, mfc_regs->e_luma_dpb + (4 * i)); buf_addr1 += ctx->luma_dpb_size; - WRITEL(buf_addr1, S5P_FIMV_E_CHROMA_DPB_V6 + (4 * i)); + WRITEL(buf_addr1, mfc_regs->e_chroma_dpb + (4 * i)); buf_addr1 += ctx->chroma_dpb_size; - WRITEL(buf_addr1, S5P_FIMV_E_ME_BUFFER_V6 + (4 * i)); + WRITEL(buf_addr1, mfc_regs->e_me_buffer + (4 * i)); buf_addr1 += ctx->me_buffer_size; buf_size1 -= (ctx->luma_dpb_size + ctx->chroma_dpb_size + ctx->me_buffer_size); } - WRITEL(buf_addr1, S5P_FIMV_E_SCRATCH_BUFFER_ADDR_V6); - WRITEL(ctx->scratch_buf_size, S5P_FIMV_E_SCRATCH_BUFFER_SIZE_V6); + WRITEL(buf_addr1, mfc_regs->e_scratch_buffer_addr); + WRITEL(ctx->scratch_buf_size, mfc_regs->e_scratch_buffer_size); buf_addr1 += ctx->scratch_buf_size; buf_size1 -= ctx->scratch_buf_size; - WRITEL(buf_addr1, S5P_FIMV_E_TMV_BUFFER0_V6); + WRITEL(buf_addr1, mfc_regs->e_tmv_buffer0); buf_addr1 += ctx->tmv_buffer_size >> 1; - WRITEL(buf_addr1, S5P_FIMV_E_TMV_BUFFER1_V6); + WRITEL(buf_addr1, mfc_regs->e_tmv_buffer1); buf_addr1 += ctx->tmv_buffer_size >> 1; buf_size1 -= ctx->tmv_buffer_size; @@ -558,7 +603,7 @@ static int s5p_mfc_set_enc_ref_buffer_v6(struct s5p_mfc_ctx *ctx) return -ENOMEM; } - WRITEL(ctx->inst_no, S5P_FIMV_INSTANCE_ID_V6); + WRITEL(ctx->inst_no, mfc_regs->instance_id); s5p_mfc_hw_call(dev->mfc_cmds, cmd_host2risc, dev, S5P_FIMV_CH_INIT_BUFS_V6, NULL); @@ -570,18 +615,19 @@ static int s5p_mfc_set_enc_ref_buffer_v6(struct s5p_mfc_ctx *ctx) static int s5p_mfc_set_slice_mode(struct s5p_mfc_ctx *ctx) { struct s5p_mfc_dev *dev = ctx->dev; + const struct s5p_mfc_regs *mfc_regs = dev->mfc_regs; /* multi-slice control */ /* multi-slice MB number or bit size */ - WRITEL(ctx->slice_mode, S5P_FIMV_E_MSLICE_MODE_V6); + WRITEL(ctx->slice_mode, mfc_regs->e_mslice_mode); if (ctx->slice_mode == V4L2_MPEG_VIDEO_MULTI_SICE_MODE_MAX_MB) { - WRITEL(ctx->slice_size.mb, S5P_FIMV_E_MSLICE_SIZE_MB_V6); + WRITEL(ctx->slice_size.mb, mfc_regs->e_mslice_size_mb); } else if (ctx->slice_mode == V4L2_MPEG_VIDEO_MULTI_SICE_MODE_MAX_BYTES) { - WRITEL(ctx->slice_size.bits, S5P_FIMV_E_MSLICE_SIZE_BITS_V6); + WRITEL(ctx->slice_size.bits, mfc_regs->e_mslice_size_bits); } else { - WRITEL(0x0, S5P_FIMV_E_MSLICE_SIZE_MB_V6); - WRITEL(0x0, S5P_FIMV_E_MSLICE_SIZE_BITS_V6); + WRITEL(0x0, mfc_regs->e_mslice_size_mb); + WRITEL(0x0, mfc_regs->e_mslice_size_bits); } return 0; @@ -590,27 +636,28 @@ static int s5p_mfc_set_slice_mode(struct s5p_mfc_ctx *ctx) static int s5p_mfc_set_enc_params(struct s5p_mfc_ctx *ctx) { struct s5p_mfc_dev *dev = ctx->dev; + const struct s5p_mfc_regs *mfc_regs = dev->mfc_regs; struct s5p_mfc_enc_params *p = &ctx->enc_params; unsigned int reg = 0; mfc_debug_enter(); /* width */ - WRITEL(ctx->img_width, S5P_FIMV_E_FRAME_WIDTH_V6); /* 16 align */ + WRITEL(ctx->img_width, mfc_regs->e_frame_width); /* 16 align */ /* height */ - WRITEL(ctx->img_height, S5P_FIMV_E_FRAME_HEIGHT_V6); /* 16 align */ + WRITEL(ctx->img_height, mfc_regs->e_frame_height); /* 16 align */ /* cropped width */ - WRITEL(ctx->img_width, S5P_FIMV_E_CROPPED_FRAME_WIDTH_V6); + WRITEL(ctx->img_width, mfc_regs->e_cropped_frame_width); /* cropped height */ - WRITEL(ctx->img_height, S5P_FIMV_E_CROPPED_FRAME_HEIGHT_V6); + WRITEL(ctx->img_height, mfc_regs->e_cropped_frame_height); /* cropped offset */ - WRITEL(0x0, S5P_FIMV_E_FRAME_CROP_OFFSET_V6); + WRITEL(0x0, mfc_regs->e_frame_crop_offset); /* pictype : IDR period */ reg = 0; reg |= p->gop_size & 0xFFFF; - WRITEL(reg, S5P_FIMV_E_GOP_CONFIG_V6); + WRITEL(reg, mfc_regs->e_gop_config); /* multi-slice control */ /* multi-slice MB number or bit size */ @@ -618,65 +665,65 @@ static int s5p_mfc_set_enc_params(struct s5p_mfc_ctx *ctx) reg = 0; if (p->slice_mode == V4L2_MPEG_VIDEO_MULTI_SICE_MODE_MAX_MB) { reg |= (0x1 << 3); - WRITEL(reg, S5P_FIMV_E_ENC_OPTIONS_V6); + WRITEL(reg, mfc_regs->e_enc_options); ctx->slice_size.mb = p->slice_mb; } else if (p->slice_mode == V4L2_MPEG_VIDEO_MULTI_SICE_MODE_MAX_BYTES) { reg |= (0x1 << 3); - WRITEL(reg, S5P_FIMV_E_ENC_OPTIONS_V6); + WRITEL(reg, mfc_regs->e_enc_options); ctx->slice_size.bits = p->slice_bit; } else { reg &= ~(0x1 << 3); - WRITEL(reg, S5P_FIMV_E_ENC_OPTIONS_V6); + WRITEL(reg, mfc_regs->e_enc_options); } s5p_mfc_set_slice_mode(ctx); /* cyclic intra refresh */ - WRITEL(p->intra_refresh_mb, S5P_FIMV_E_IR_SIZE_V6); - reg = READL(S5P_FIMV_E_ENC_OPTIONS_V6); + WRITEL(p->intra_refresh_mb, mfc_regs->e_ir_size); + reg = READL(mfc_regs->e_enc_options); if (p->intra_refresh_mb == 0) reg &= ~(0x1 << 4); else reg |= (0x1 << 4); - WRITEL(reg, S5P_FIMV_E_ENC_OPTIONS_V6); + WRITEL(reg, mfc_regs->e_enc_options); /* 'NON_REFERENCE_STORE_ENABLE' for debugging */ - reg = READL(S5P_FIMV_E_ENC_OPTIONS_V6); + reg = READL(mfc_regs->e_enc_options); reg &= ~(0x1 << 9); - WRITEL(reg, S5P_FIMV_E_ENC_OPTIONS_V6); + WRITEL(reg, mfc_regs->e_enc_options); /* memory structure cur. frame */ if (ctx->src_fmt->fourcc == V4L2_PIX_FMT_NV12M) { /* 0: Linear, 1: 2D tiled*/ - reg = READL(S5P_FIMV_E_ENC_OPTIONS_V6); + reg = READL(mfc_regs->e_enc_options); reg &= ~(0x1 << 7); - WRITEL(reg, S5P_FIMV_E_ENC_OPTIONS_V6); + WRITEL(reg, mfc_regs->e_enc_options); /* 0: NV12(CbCr), 1: NV21(CrCb) */ - WRITEL(0x0, S5P_FIMV_PIXEL_FORMAT_V6); + WRITEL(0x0, mfc_regs->pixel_format); } else if (ctx->src_fmt->fourcc == V4L2_PIX_FMT_NV21M) { /* 0: Linear, 1: 2D tiled*/ - reg = READL(S5P_FIMV_E_ENC_OPTIONS_V6); + reg = READL(mfc_regs->e_enc_options); reg &= ~(0x1 << 7); - WRITEL(reg, S5P_FIMV_E_ENC_OPTIONS_V6); + WRITEL(reg, mfc_regs->e_enc_options); /* 0: NV12(CbCr), 1: NV21(CrCb) */ - WRITEL(0x1, S5P_FIMV_PIXEL_FORMAT_V6); + WRITEL(0x1, mfc_regs->pixel_format); } else if (ctx->src_fmt->fourcc == V4L2_PIX_FMT_NV12MT_16X16) { /* 0: Linear, 1: 2D tiled*/ - reg = READL(S5P_FIMV_E_ENC_OPTIONS_V6); + reg = READL(mfc_regs->e_enc_options); reg |= (0x1 << 7); - WRITEL(reg, S5P_FIMV_E_ENC_OPTIONS_V6); + WRITEL(reg, mfc_regs->e_enc_options); /* 0: NV12(CbCr), 1: NV21(CrCb) */ - WRITEL(0x0, S5P_FIMV_PIXEL_FORMAT_V6); + WRITEL(0x0, mfc_regs->pixel_format); } /* memory structure recon. frame */ /* 0: Linear, 1: 2D tiled */ - reg = READL(S5P_FIMV_E_ENC_OPTIONS_V6); + reg = READL(mfc_regs->e_enc_options); reg |= (0x1 << 8); - WRITEL(reg, S5P_FIMV_E_ENC_OPTIONS_V6); + WRITEL(reg, mfc_regs->e_enc_options); /* padding control & value */ - WRITEL(0x0, S5P_FIMV_E_PADDING_CTRL_V6); + WRITEL(0x0, mfc_regs->e_padding_ctrl); if (p->pad) { reg = 0; /** enable */ @@ -687,64 +734,64 @@ static int s5p_mfc_set_enc_params(struct s5p_mfc_ctx *ctx) reg |= ((p->pad_cb & 0xFF) << 8); /** y value */ reg |= p->pad_luma & 0xFF; - WRITEL(reg, S5P_FIMV_E_PADDING_CTRL_V6); + WRITEL(reg, mfc_regs->e_padding_ctrl); } /* rate control config. */ reg = 0; /* frame-level rate control */ reg |= ((p->rc_frame & 0x1) << 9); - WRITEL(reg, S5P_FIMV_E_RC_CONFIG_V6); + WRITEL(reg, mfc_regs->e_rc_config); /* bit rate */ if (p->rc_frame) WRITEL(p->rc_bitrate, - S5P_FIMV_E_RC_BIT_RATE_V6); + mfc_regs->e_rc_bit_rate); else - WRITEL(1, S5P_FIMV_E_RC_BIT_RATE_V6); + WRITEL(1, mfc_regs->e_rc_bit_rate); /* reaction coefficient */ if (p->rc_frame) { if (p->rc_reaction_coeff < TIGHT_CBR_MAX) /* tight CBR */ - WRITEL(1, S5P_FIMV_E_RC_RPARAM_V6); + WRITEL(1, mfc_regs->e_rc_mode); else /* loose CBR */ - WRITEL(2, S5P_FIMV_E_RC_RPARAM_V6); + WRITEL(2, mfc_regs->e_rc_mode); } /* seq header ctrl */ - reg = READL(S5P_FIMV_E_ENC_OPTIONS_V6); + reg = READL(mfc_regs->e_enc_options); reg &= ~(0x1 << 2); reg |= ((p->seq_hdr_mode & 0x1) << 2); /* frame skip mode */ reg &= ~(0x3); reg |= (p->frame_skip_mode & 0x3); - WRITEL(reg, S5P_FIMV_E_ENC_OPTIONS_V6); + WRITEL(reg, mfc_regs->e_enc_options); /* 'DROP_CONTROL_ENABLE', disable */ - reg = READL(S5P_FIMV_E_RC_CONFIG_V6); + reg = READL(mfc_regs->e_rc_config); reg &= ~(0x1 << 10); - WRITEL(reg, S5P_FIMV_E_RC_CONFIG_V6); + WRITEL(reg, mfc_regs->e_rc_config); /* setting for MV range [16, 256] */ reg = (p->mv_h_range & S5P_FIMV_E_MV_RANGE_V6_MASK); - WRITEL(reg, S5P_FIMV_E_MV_HOR_RANGE_V6); + WRITEL(reg, mfc_regs->e_mv_hor_range); reg = (p->mv_v_range & S5P_FIMV_E_MV_RANGE_V6_MASK); - WRITEL(reg, S5P_FIMV_E_MV_VER_RANGE_V6); + WRITEL(reg, mfc_regs->e_mv_ver_range); - WRITEL(0x0, S5P_FIMV_E_FRAME_INSERTION_V6); - WRITEL(0x0, S5P_FIMV_E_ROI_BUFFER_ADDR_V6); - WRITEL(0x0, S5P_FIMV_E_PARAM_CHANGE_V6); - WRITEL(0x0, S5P_FIMV_E_RC_ROI_CTRL_V6); - WRITEL(0x0, S5P_FIMV_E_PICTURE_TAG_V6); + WRITEL(0x0, mfc_regs->e_frame_insertion); + WRITEL(0x0, mfc_regs->e_roi_buffer_addr); + WRITEL(0x0, mfc_regs->e_param_change); + WRITEL(0x0, mfc_regs->e_rc_roi_ctrl); + WRITEL(0x0, mfc_regs->e_picture_tag); - WRITEL(0x0, S5P_FIMV_E_BIT_COUNT_ENABLE_V6); - WRITEL(0x0, S5P_FIMV_E_MAX_BIT_COUNT_V6); - WRITEL(0x0, S5P_FIMV_E_MIN_BIT_COUNT_V6); + WRITEL(0x0, mfc_regs->e_bit_count_enable); + WRITEL(0x0, mfc_regs->e_max_bit_count); + WRITEL(0x0, mfc_regs->e_min_bit_count); - WRITEL(0x0, S5P_FIMV_E_METADATA_BUFFER_ADDR_V6); - WRITEL(0x0, S5P_FIMV_E_METADATA_BUFFER_SIZE_V6); + WRITEL(0x0, mfc_regs->e_metadata_buffer_addr); + WRITEL(0x0, mfc_regs->e_metadata_buffer_size); mfc_debug_leave(); @@ -754,6 +801,7 @@ static int s5p_mfc_set_enc_params(struct s5p_mfc_ctx *ctx) static int s5p_mfc_set_enc_params_h264(struct s5p_mfc_ctx *ctx) { struct s5p_mfc_dev *dev = ctx->dev; + const struct s5p_mfc_regs *mfc_regs = dev->mfc_regs; struct s5p_mfc_enc_params *p = &ctx->enc_params; struct s5p_mfc_h264_enc_params *p_h264 = &p->codec.h264; unsigned int reg = 0; @@ -764,10 +812,10 @@ static int s5p_mfc_set_enc_params_h264(struct s5p_mfc_ctx *ctx) s5p_mfc_set_enc_params(ctx); /* pictype : number of B */ - reg = READL(S5P_FIMV_E_GOP_CONFIG_V6); + reg = READL(mfc_regs->e_gop_config); reg &= ~(0x3 << 16); reg |= ((p->num_b_frame & 0x3) << 16); - WRITEL(reg, S5P_FIMV_E_GOP_CONFIG_V6); + WRITEL(reg, mfc_regs->e_gop_config); /* profile & level */ reg = 0; @@ -775,18 +823,19 @@ static int s5p_mfc_set_enc_params_h264(struct s5p_mfc_ctx *ctx) reg |= ((p_h264->level & 0xFF) << 8); /** profile - 0 ~ 3 */ reg |= p_h264->profile & 0x3F; - WRITEL(reg, S5P_FIMV_E_PICTURE_PROFILE_V6); + WRITEL(reg, mfc_regs->e_picture_profile); /* rate control config. */ - reg = READL(S5P_FIMV_E_RC_CONFIG_V6); + reg = READL(mfc_regs->e_rc_config); /** macroblock level rate control */ reg &= ~(0x1 << 8); reg |= ((p->rc_mb & 0x1) << 8); - WRITEL(reg, S5P_FIMV_E_RC_CONFIG_V6); + WRITEL(reg, mfc_regs->e_rc_config); + /** frame QP */ reg &= ~(0x3F); reg |= p_h264->rc_frame_qp & 0x3F; - WRITEL(reg, S5P_FIMV_E_RC_CONFIG_V6); + WRITEL(reg, mfc_regs->e_rc_config); /* max & min value of QP */ reg = 0; @@ -794,16 +843,16 @@ static int s5p_mfc_set_enc_params_h264(struct s5p_mfc_ctx *ctx) reg |= ((p_h264->rc_max_qp & 0x3F) << 8); /** min QP */ reg |= p_h264->rc_min_qp & 0x3F; - WRITEL(reg, S5P_FIMV_E_RC_QP_BOUND_V6); + WRITEL(reg, mfc_regs->e_rc_qp_bound); /* other QPs */ - WRITEL(0x0, S5P_FIMV_E_FIXED_PICTURE_QP_V6); + WRITEL(0x0, mfc_regs->e_fixed_picture_qp); if (!p->rc_frame && !p->rc_mb) { reg = 0; reg |= ((p_h264->rc_b_frame_qp & 0x3F) << 16); reg |= ((p_h264->rc_p_frame_qp & 0x3F) << 8); reg |= p_h264->rc_frame_qp & 0x3F; - WRITEL(reg, S5P_FIMV_E_FIXED_PICTURE_QP_V6); + WRITEL(reg, mfc_regs->e_fixed_picture_qp); } /* frame rate */ @@ -811,38 +860,38 @@ static int s5p_mfc_set_enc_params_h264(struct s5p_mfc_ctx *ctx) reg = 0; reg |= ((p->rc_framerate_num & 0xFFFF) << 16); reg |= p->rc_framerate_denom & 0xFFFF; - WRITEL(reg, S5P_FIMV_E_RC_FRAME_RATE_V6); + WRITEL(reg, mfc_regs->e_rc_frame_rate); } /* vbv buffer size */ if (p->frame_skip_mode == V4L2_MPEG_MFC51_VIDEO_FRAME_SKIP_MODE_BUF_LIMIT) { WRITEL(p_h264->cpb_size & 0xFFFF, - S5P_FIMV_E_VBV_BUFFER_SIZE_V6); + mfc_regs->e_vbv_buffer_size); if (p->rc_frame) - WRITEL(p->vbv_delay, S5P_FIMV_E_VBV_INIT_DELAY_V6); + WRITEL(p->vbv_delay, mfc_regs->e_vbv_init_delay); } /* interlace */ reg = 0; reg |= ((p_h264->interlace & 0x1) << 3); - WRITEL(reg, S5P_FIMV_E_H264_OPTIONS_V6); + WRITEL(reg, mfc_regs->e_h264_options); /* height */ if (p_h264->interlace) { WRITEL(ctx->img_height >> 1, - S5P_FIMV_E_FRAME_HEIGHT_V6); /* 32 align */ + mfc_regs->e_frame_height); /* 32 align */ /* cropped height */ WRITEL(ctx->img_height >> 1, - S5P_FIMV_E_CROPPED_FRAME_HEIGHT_V6); + mfc_regs->e_cropped_frame_height); } /* loop filter ctrl */ - reg = READL(S5P_FIMV_E_H264_OPTIONS_V6); + reg = READL(mfc_regs->e_h264_options); reg &= ~(0x3 << 1); reg |= ((p_h264->loop_filter_mode & 0x3) << 1); - WRITEL(reg, S5P_FIMV_E_H264_OPTIONS_V6); + WRITEL(reg, mfc_regs->e_h264_options); /* loopfilter alpha offset */ if (p_h264->loop_filter_alpha < 0) { @@ -852,7 +901,7 @@ static int s5p_mfc_set_enc_params_h264(struct s5p_mfc_ctx *ctx) reg = 0x00; reg |= (p_h264->loop_filter_alpha & 0xF); } - WRITEL(reg, S5P_FIMV_E_H264_LF_ALPHA_OFFSET_V6); + WRITEL(reg, mfc_regs->e_h264_lf_alpha_offset); /* loopfilter beta offset */ if (p_h264->loop_filter_beta < 0) { @@ -862,28 +911,28 @@ static int s5p_mfc_set_enc_params_h264(struct s5p_mfc_ctx *ctx) reg = 0x00; reg |= (p_h264->loop_filter_beta & 0xF); } - WRITEL(reg, S5P_FIMV_E_H264_LF_BETA_OFFSET_V6); + WRITEL(reg, mfc_regs->e_h264_lf_beta_offset); /* entropy coding mode */ - reg = READL(S5P_FIMV_E_H264_OPTIONS_V6); + reg = READL(mfc_regs->e_h264_options); reg &= ~(0x1); reg |= p_h264->entropy_mode & 0x1; - WRITEL(reg, S5P_FIMV_E_H264_OPTIONS_V6); + WRITEL(reg, mfc_regs->e_h264_options); /* number of ref. picture */ - reg = READL(S5P_FIMV_E_H264_OPTIONS_V6); + reg = READL(mfc_regs->e_h264_options); reg &= ~(0x1 << 7); reg |= (((p_h264->num_ref_pic_4p - 1) & 0x1) << 7); - WRITEL(reg, S5P_FIMV_E_H264_OPTIONS_V6); + WRITEL(reg, mfc_regs->e_h264_options); /* 8x8 transform enable */ - reg = READL(S5P_FIMV_E_H264_OPTIONS_V6); + reg = READL(mfc_regs->e_h264_options); reg &= ~(0x3 << 12); reg |= ((p_h264->_8x8_transform & 0x3) << 12); - WRITEL(reg, S5P_FIMV_E_H264_OPTIONS_V6); + WRITEL(reg, mfc_regs->e_h264_options); /* macroblock adaptive scaling features */ - WRITEL(0x0, S5P_FIMV_E_MB_RC_CONFIG_V6); + WRITEL(0x0, mfc_regs->e_mb_rc_config); if (p->rc_mb) { reg = 0; /** dark region */ @@ -894,92 +943,95 @@ static int s5p_mfc_set_enc_params_h264(struct s5p_mfc_ctx *ctx) reg |= ((p_h264->rc_mb_static & 0x1) << 1); /** high activity region */ reg |= p_h264->rc_mb_activity & 0x1; - WRITEL(reg, S5P_FIMV_E_MB_RC_CONFIG_V6); + WRITEL(reg, mfc_regs->e_mb_rc_config); } /* aspect ratio VUI */ - reg = READL(S5P_FIMV_E_H264_OPTIONS_V6); + READL(mfc_regs->e_h264_options); reg &= ~(0x1 << 5); reg |= ((p_h264->vui_sar & 0x1) << 5); - WRITEL(reg, S5P_FIMV_E_H264_OPTIONS_V6); + WRITEL(reg, mfc_regs->e_h264_options); - WRITEL(0x0, S5P_FIMV_E_ASPECT_RATIO_V6); - WRITEL(0x0, S5P_FIMV_E_EXTENDED_SAR_V6); + WRITEL(0x0, mfc_regs->e_aspect_ratio); + WRITEL(0x0, mfc_regs->e_extended_sar); if (p_h264->vui_sar) { /* aspect ration IDC */ reg = 0; reg |= p_h264->vui_sar_idc & 0xFF; - WRITEL(reg, S5P_FIMV_E_ASPECT_RATIO_V6); + WRITEL(reg, mfc_regs->e_aspect_ratio); if (p_h264->vui_sar_idc == 0xFF) { /* extended SAR */ reg = 0; reg |= (p_h264->vui_ext_sar_width & 0xFFFF) << 16; reg |= p_h264->vui_ext_sar_height & 0xFFFF; - WRITEL(reg, S5P_FIMV_E_EXTENDED_SAR_V6); + WRITEL(reg, mfc_regs->e_extended_sar); } } /* intra picture period for H.264 open GOP */ /* control */ - reg = READL(S5P_FIMV_E_H264_OPTIONS_V6); + READL(mfc_regs->e_h264_options); reg &= ~(0x1 << 4); reg |= ((p_h264->open_gop & 0x1) << 4); - WRITEL(reg, S5P_FIMV_E_H264_OPTIONS_V6); + WRITEL(reg, mfc_regs->e_h264_options); + /* value */ - WRITEL(0x0, S5P_FIMV_E_H264_I_PERIOD_V6); + WRITEL(0x0, mfc_regs->e_h264_i_period); if (p_h264->open_gop) { reg = 0; reg |= p_h264->open_gop_size & 0xFFFF; - WRITEL(reg, S5P_FIMV_E_H264_I_PERIOD_V6); + WRITEL(reg, mfc_regs->e_h264_i_period); } /* 'WEIGHTED_BI_PREDICTION' for B is disable */ - reg = READL(S5P_FIMV_E_H264_OPTIONS_V6); + READL(mfc_regs->e_h264_options); reg &= ~(0x3 << 9); - WRITEL(reg, S5P_FIMV_E_H264_OPTIONS_V6); + WRITEL(reg, mfc_regs->e_h264_options); /* 'CONSTRAINED_INTRA_PRED_ENABLE' is disable */ - reg = READL(S5P_FIMV_E_H264_OPTIONS_V6); + READL(mfc_regs->e_h264_options); reg &= ~(0x1 << 14); - WRITEL(reg, S5P_FIMV_E_H264_OPTIONS_V6); + WRITEL(reg, mfc_regs->e_h264_options); /* ASO */ - reg = READL(S5P_FIMV_E_H264_OPTIONS_V6); + READL(mfc_regs->e_h264_options); reg &= ~(0x1 << 6); reg |= ((p_h264->aso & 0x1) << 6); - WRITEL(reg, S5P_FIMV_E_H264_OPTIONS_V6); + WRITEL(reg, mfc_regs->e_h264_options); /* hier qp enable */ - reg = READL(S5P_FIMV_E_H264_OPTIONS_V6); + READL(mfc_regs->e_h264_options); reg &= ~(0x1 << 8); reg |= ((p_h264->open_gop & 0x1) << 8); - WRITEL(reg, S5P_FIMV_E_H264_OPTIONS_V6); + WRITEL(reg, mfc_regs->e_h264_options); reg = 0; if (p_h264->hier_qp && p_h264->hier_qp_layer) { reg |= (p_h264->hier_qp_type & 0x1) << 0x3; reg |= p_h264->hier_qp_layer & 0x7; - WRITEL(reg, S5P_FIMV_E_H264_NUM_T_LAYER_V6); + WRITEL(reg, mfc_regs->e_h264_num_t_layer); /* QP value for each layer */ - for (i = 0; i < (p_h264->hier_qp_layer & 0x7); i++) + for (i = 0; i < p_h264->hier_qp_layer && + i < ARRAY_SIZE(p_h264->hier_qp_layer_qp); i++) { WRITEL(p_h264->hier_qp_layer_qp[i], - S5P_FIMV_E_H264_HIERARCHICAL_QP_LAYER0_V6 + - i * 4); + mfc_regs->e_h264_hierarchical_qp_layer0 + + i * 4); + } } /* number of coding layer should be zero when hierarchical is disable */ - WRITEL(reg, S5P_FIMV_E_H264_NUM_T_LAYER_V6); + WRITEL(reg, mfc_regs->e_h264_num_t_layer); /* frame packing SEI generation */ - reg = READL(S5P_FIMV_E_H264_OPTIONS_V6); + READL(mfc_regs->e_h264_options); reg &= ~(0x1 << 25); reg |= ((p_h264->sei_frame_packing & 0x1) << 25); - WRITEL(reg, S5P_FIMV_E_H264_OPTIONS_V6); + WRITEL(reg, mfc_regs->e_h264_options); if (p_h264->sei_frame_packing) { reg = 0; /** current frame0 flag */ reg |= ((p_h264->sei_fp_curr_frame_0 & 0x1) << 2); /** arrangement type */ reg |= p_h264->sei_fp_arrangement_type & 0x3; - WRITEL(reg, S5P_FIMV_E_H264_FRAME_PACKING_SEI_INFO_V6); + WRITEL(reg, mfc_regs->e_h264_frame_packing_sei_info); } if (p_h264->fmo) { @@ -989,8 +1041,8 @@ static int s5p_mfc_set_enc_params_h264(struct s5p_mfc_ctx *ctx) p_h264->fmo_slice_grp = 4; for (i = 0; i < (p_h264->fmo_slice_grp & 0xF); i++) WRITEL(p_h264->fmo_run_len[i] - 1, - S5P_FIMV_E_H264_FMO_RUN_LENGTH_MINUS1_0_V6 + - i * 4); + mfc_regs->e_h264_fmo_run_length_minus1_0 + + i * 4); break; case V4L2_MPEG_VIDEO_H264_FMO_MAP_TYPE_SCATTERED_SLICES: if (p_h264->fmo_slice_grp > 4) @@ -1001,10 +1053,10 @@ static int s5p_mfc_set_enc_params_h264(struct s5p_mfc_ctx *ctx) if (p_h264->fmo_slice_grp > 2) p_h264->fmo_slice_grp = 2; WRITEL(p_h264->fmo_chg_dir & 0x1, - S5P_FIMV_E_H264_FMO_SLICE_GRP_CHANGE_DIR_V6); + mfc_regs->e_h264_fmo_slice_grp_change_dir); /* the valid range is 0 ~ number of macroblocks -1 */ WRITEL(p_h264->fmo_chg_rate, - S5P_FIMV_E_H264_FMO_SLICE_GRP_CHANGE_RATE_MINUS1_V6); + mfc_regs->e_h264_fmo_slice_grp_change_rate_minus1); break; default: mfc_err("Unsupported map type for FMO: %d\n", @@ -1015,11 +1067,11 @@ static int s5p_mfc_set_enc_params_h264(struct s5p_mfc_ctx *ctx) } WRITEL(p_h264->fmo_map_type, - S5P_FIMV_E_H264_FMO_SLICE_GRP_MAP_TYPE_V6); + mfc_regs->e_h264_fmo_slice_grp_map_type); WRITEL(p_h264->fmo_slice_grp - 1, - S5P_FIMV_E_H264_FMO_NUM_SLICE_GRP_MINUS1_V6); + mfc_regs->e_h264_fmo_num_slice_grp_minus1); } else { - WRITEL(0, S5P_FIMV_E_H264_FMO_NUM_SLICE_GRP_MINUS1_V6); + WRITEL(0, mfc_regs->e_h264_fmo_num_slice_grp_minus1); } mfc_debug_leave(); @@ -1030,6 +1082,7 @@ static int s5p_mfc_set_enc_params_h264(struct s5p_mfc_ctx *ctx) static int s5p_mfc_set_enc_params_mpeg4(struct s5p_mfc_ctx *ctx) { struct s5p_mfc_dev *dev = ctx->dev; + const struct s5p_mfc_regs *mfc_regs = dev->mfc_regs; struct s5p_mfc_enc_params *p = &ctx->enc_params; struct s5p_mfc_mpeg4_enc_params *p_mpeg4 = &p->codec.mpeg4; unsigned int reg = 0; @@ -1039,10 +1092,10 @@ static int s5p_mfc_set_enc_params_mpeg4(struct s5p_mfc_ctx *ctx) s5p_mfc_set_enc_params(ctx); /* pictype : number of B */ - reg = READL(S5P_FIMV_E_GOP_CONFIG_V6); + reg = READL(mfc_regs->e_gop_config); reg &= ~(0x3 << 16); reg |= ((p->num_b_frame & 0x3) << 16); - WRITEL(reg, S5P_FIMV_E_GOP_CONFIG_V6); + WRITEL(reg, mfc_regs->e_gop_config); /* profile & level */ reg = 0; @@ -1050,18 +1103,19 @@ static int s5p_mfc_set_enc_params_mpeg4(struct s5p_mfc_ctx *ctx) reg |= ((p_mpeg4->level & 0xFF) << 8); /** profile - 0 ~ 1 */ reg |= p_mpeg4->profile & 0x3F; - WRITEL(reg, S5P_FIMV_E_PICTURE_PROFILE_V6); + WRITEL(reg, mfc_regs->e_picture_profile); /* rate control config. */ - reg = READL(S5P_FIMV_E_RC_CONFIG_V6); + reg = READL(mfc_regs->e_rc_config); /** macroblock level rate control */ reg &= ~(0x1 << 8); reg |= ((p->rc_mb & 0x1) << 8); - WRITEL(reg, S5P_FIMV_E_RC_CONFIG_V6); + WRITEL(reg, mfc_regs->e_rc_config); + /** frame QP */ reg &= ~(0x3F); reg |= p_mpeg4->rc_frame_qp & 0x3F; - WRITEL(reg, S5P_FIMV_E_RC_CONFIG_V6); + WRITEL(reg, mfc_regs->e_rc_config); /* max & min value of QP */ reg = 0; @@ -1069,16 +1123,16 @@ static int s5p_mfc_set_enc_params_mpeg4(struct s5p_mfc_ctx *ctx) reg |= ((p_mpeg4->rc_max_qp & 0x3F) << 8); /** min QP */ reg |= p_mpeg4->rc_min_qp & 0x3F; - WRITEL(reg, S5P_FIMV_E_RC_QP_BOUND_V6); + WRITEL(reg, mfc_regs->e_rc_qp_bound); /* other QPs */ - WRITEL(0x0, S5P_FIMV_E_FIXED_PICTURE_QP_V6); + WRITEL(0x0, mfc_regs->e_fixed_picture_qp); if (!p->rc_frame && !p->rc_mb) { reg = 0; reg |= ((p_mpeg4->rc_b_frame_qp & 0x3F) << 16); reg |= ((p_mpeg4->rc_p_frame_qp & 0x3F) << 8); reg |= p_mpeg4->rc_frame_qp & 0x3F; - WRITEL(reg, S5P_FIMV_E_FIXED_PICTURE_QP_V6); + WRITEL(reg, mfc_regs->e_fixed_picture_qp); } /* frame rate */ @@ -1086,21 +1140,21 @@ static int s5p_mfc_set_enc_params_mpeg4(struct s5p_mfc_ctx *ctx) reg = 0; reg |= ((p->rc_framerate_num & 0xFFFF) << 16); reg |= p->rc_framerate_denom & 0xFFFF; - WRITEL(reg, S5P_FIMV_E_RC_FRAME_RATE_V6); + WRITEL(reg, mfc_regs->e_rc_frame_rate); } /* vbv buffer size */ if (p->frame_skip_mode == V4L2_MPEG_MFC51_VIDEO_FRAME_SKIP_MODE_BUF_LIMIT) { - WRITEL(p->vbv_size & 0xFFFF, S5P_FIMV_E_VBV_BUFFER_SIZE_V6); + WRITEL(p->vbv_size & 0xFFFF, mfc_regs->e_vbv_buffer_size); if (p->rc_frame) - WRITEL(p->vbv_delay, S5P_FIMV_E_VBV_INIT_DELAY_V6); + WRITEL(p->vbv_delay, mfc_regs->e_vbv_init_delay); } /* Disable HEC */ - WRITEL(0x0, S5P_FIMV_E_MPEG4_OPTIONS_V6); - WRITEL(0x0, S5P_FIMV_E_MPEG4_HEC_PERIOD_V6); + WRITEL(0x0, mfc_regs->e_mpeg4_options); + WRITEL(0x0, mfc_regs->e_mpeg4_hec_period); mfc_debug_leave(); @@ -1110,6 +1164,7 @@ static int s5p_mfc_set_enc_params_mpeg4(struct s5p_mfc_ctx *ctx) static int s5p_mfc_set_enc_params_h263(struct s5p_mfc_ctx *ctx) { struct s5p_mfc_dev *dev = ctx->dev; + const struct s5p_mfc_regs *mfc_regs = dev->mfc_regs; struct s5p_mfc_enc_params *p = &ctx->enc_params; struct s5p_mfc_mpeg4_enc_params *p_h263 = &p->codec.mpeg4; unsigned int reg = 0; @@ -1122,18 +1177,19 @@ static int s5p_mfc_set_enc_params_h263(struct s5p_mfc_ctx *ctx) reg = 0; /** profile */ reg |= (0x1 << 4); - WRITEL(reg, S5P_FIMV_E_PICTURE_PROFILE_V6); + WRITEL(reg, mfc_regs->e_picture_profile); /* rate control config. */ - reg = READL(S5P_FIMV_E_RC_CONFIG_V6); + reg = READL(mfc_regs->e_rc_config); /** macroblock level rate control */ reg &= ~(0x1 << 8); reg |= ((p->rc_mb & 0x1) << 8); - WRITEL(reg, S5P_FIMV_E_RC_CONFIG_V6); + WRITEL(reg, mfc_regs->e_rc_config); + /** frame QP */ reg &= ~(0x3F); reg |= p_h263->rc_frame_qp & 0x3F; - WRITEL(reg, S5P_FIMV_E_RC_CONFIG_V6); + WRITEL(reg, mfc_regs->e_rc_config); /* max & min value of QP */ reg = 0; @@ -1141,16 +1197,16 @@ static int s5p_mfc_set_enc_params_h263(struct s5p_mfc_ctx *ctx) reg |= ((p_h263->rc_max_qp & 0x3F) << 8); /** min QP */ reg |= p_h263->rc_min_qp & 0x3F; - WRITEL(reg, S5P_FIMV_E_RC_QP_BOUND_V6); + WRITEL(reg, mfc_regs->e_rc_qp_bound); /* other QPs */ - WRITEL(0x0, S5P_FIMV_E_FIXED_PICTURE_QP_V6); + WRITEL(0x0, mfc_regs->e_fixed_picture_qp); if (!p->rc_frame && !p->rc_mb) { reg = 0; reg |= ((p_h263->rc_b_frame_qp & 0x3F) << 16); reg |= ((p_h263->rc_p_frame_qp & 0x3F) << 8); reg |= p_h263->rc_frame_qp & 0x3F; - WRITEL(reg, S5P_FIMV_E_FIXED_PICTURE_QP_V6); + WRITEL(reg, mfc_regs->e_fixed_picture_qp); } /* frame rate */ @@ -1158,16 +1214,16 @@ static int s5p_mfc_set_enc_params_h263(struct s5p_mfc_ctx *ctx) reg = 0; reg |= ((p->rc_framerate_num & 0xFFFF) << 16); reg |= p->rc_framerate_denom & 0xFFFF; - WRITEL(reg, S5P_FIMV_E_RC_FRAME_RATE_V6); + WRITEL(reg, mfc_regs->e_rc_frame_rate); } /* vbv buffer size */ if (p->frame_skip_mode == V4L2_MPEG_MFC51_VIDEO_FRAME_SKIP_MODE_BUF_LIMIT) { - WRITEL(p->vbv_size & 0xFFFF, S5P_FIMV_E_VBV_BUFFER_SIZE_V6); + WRITEL(p->vbv_size & 0xFFFF, mfc_regs->e_vbv_buffer_size); if (p->rc_frame) - WRITEL(p->vbv_delay, S5P_FIMV_E_VBV_INIT_DELAY_V6); + WRITEL(p->vbv_delay, mfc_regs->e_vbv_init_delay); } mfc_debug_leave(); @@ -1178,6 +1234,7 @@ static int s5p_mfc_set_enc_params_h263(struct s5p_mfc_ctx *ctx) static int s5p_mfc_set_enc_params_vp8(struct s5p_mfc_ctx *ctx) { struct s5p_mfc_dev *dev = ctx->dev; + const struct s5p_mfc_regs *mfc_regs = dev->mfc_regs; struct s5p_mfc_enc_params *p = &ctx->enc_params; struct s5p_mfc_vp8_enc_params *p_vp8 = &p->codec.vp8; unsigned int reg = 0; @@ -1188,57 +1245,57 @@ static int s5p_mfc_set_enc_params_vp8(struct s5p_mfc_ctx *ctx) s5p_mfc_set_enc_params(ctx); /* pictype : number of B */ - reg = READL(S5P_FIMV_E_GOP_CONFIG_V6); + reg = READL(mfc_regs->e_gop_config); reg &= ~(0x3 << 16); reg |= ((p->num_b_frame & 0x3) << 16); - WRITEL(reg, S5P_FIMV_E_GOP_CONFIG_V6); + WRITEL(reg, mfc_regs->e_gop_config); /* profile - 0 ~ 3 */ reg = p_vp8->profile & 0x3; - WRITEL(reg, S5P_FIMV_E_PICTURE_PROFILE_V6); + WRITEL(reg, mfc_regs->e_picture_profile); /* rate control config. */ - reg = READL(S5P_FIMV_E_RC_CONFIG_V6); + reg = READL(mfc_regs->e_rc_config); /** macroblock level rate control */ reg &= ~(0x1 << 8); reg |= ((p->rc_mb & 0x1) << 8); - WRITEL(reg, S5P_FIMV_E_RC_CONFIG_V6); + WRITEL(reg, mfc_regs->e_rc_config); /* frame rate */ if (p->rc_frame && p->rc_framerate_num && p->rc_framerate_denom) { reg = 0; reg |= ((p->rc_framerate_num & 0xFFFF) << 16); reg |= p->rc_framerate_denom & 0xFFFF; - WRITEL(reg, S5P_FIMV_E_RC_FRAME_RATE_V6); + WRITEL(reg, mfc_regs->e_rc_frame_rate); } /* frame QP */ reg &= ~(0x7F); reg |= p_vp8->rc_frame_qp & 0x7F; - WRITEL(reg, S5P_FIMV_E_RC_CONFIG_V6); + WRITEL(reg, mfc_regs->e_rc_config); /* other QPs */ - WRITEL(0x0, S5P_FIMV_E_FIXED_PICTURE_QP_V6); + WRITEL(0x0, mfc_regs->e_fixed_picture_qp); if (!p->rc_frame && !p->rc_mb) { reg = 0; reg |= ((p_vp8->rc_p_frame_qp & 0x7F) << 8); reg |= p_vp8->rc_frame_qp & 0x7F; - WRITEL(reg, S5P_FIMV_E_FIXED_PICTURE_QP_V6); + WRITEL(reg, mfc_regs->e_fixed_picture_qp); } /* max QP */ reg = ((p_vp8->rc_max_qp & 0x7F) << 8); /* min QP */ reg |= p_vp8->rc_min_qp & 0x7F; - WRITEL(reg, S5P_FIMV_E_RC_QP_BOUND_V6); + WRITEL(reg, mfc_regs->e_rc_qp_bound); /* vbv buffer size */ if (p->frame_skip_mode == V4L2_MPEG_MFC51_VIDEO_FRAME_SKIP_MODE_BUF_LIMIT) { - WRITEL(p->vbv_size & 0xFFFF, S5P_FIMV_E_VBV_BUFFER_SIZE_V6); + WRITEL(p->vbv_size & 0xFFFF, mfc_regs->e_vbv_buffer_size); if (p->rc_frame) - WRITEL(p->vbv_delay, S5P_FIMV_E_VBV_INIT_DELAY_V6); + WRITEL(p->vbv_delay, mfc_regs->e_vbv_init_delay); } /* VP8 specific params */ @@ -1260,7 +1317,7 @@ static int s5p_mfc_set_enc_params_vp8(struct s5p_mfc_ctx *ctx) } reg |= (val & 0xF) << 3; reg |= (p_vp8->num_ref & 0x2); - WRITEL(reg, S5P_FIMV_E_VP8_OPTIONS_V7); + WRITEL(reg, mfc_regs->e_vp8_options); mfc_debug_leave(); @@ -1271,6 +1328,7 @@ static int s5p_mfc_set_enc_params_vp8(struct s5p_mfc_ctx *ctx) static int s5p_mfc_init_decode_v6(struct s5p_mfc_ctx *ctx) { struct s5p_mfc_dev *dev = ctx->dev; + const struct s5p_mfc_regs *mfc_regs = dev->mfc_regs; unsigned int reg = 0; int fmo_aso_ctrl = 0; @@ -1278,9 +1336,9 @@ static int s5p_mfc_init_decode_v6(struct s5p_mfc_ctx *ctx) mfc_debug(2, "InstNo: %d/%d\n", ctx->inst_no, S5P_FIMV_CH_SEQ_HEADER_V6); mfc_debug(2, "BUFs: %08x %08x %08x\n", - READL(S5P_FIMV_D_CPB_BUFFER_ADDR_V6), - READL(S5P_FIMV_D_CPB_BUFFER_ADDR_V6), - READL(S5P_FIMV_D_CPB_BUFFER_ADDR_V6)); + READL(mfc_regs->d_cpb_buffer_addr), + READL(mfc_regs->d_cpb_buffer_addr), + READL(mfc_regs->d_cpb_buffer_addr)); /* FMO_ASO_CTRL - 0: Enable, 1: Disable */ reg |= (fmo_aso_ctrl << S5P_FIMV_D_OPT_FMO_ASO_CTRL_MASK_V6); @@ -1291,11 +1349,11 @@ static int s5p_mfc_init_decode_v6(struct s5p_mfc_ctx *ctx) * set to negative value. */ if (ctx->display_delay >= 0) { reg |= (0x1 << S5P_FIMV_D_OPT_DDELAY_EN_SHIFT_V6); - WRITEL(ctx->display_delay, S5P_FIMV_D_DISPLAY_DELAY_V6); + WRITEL(ctx->display_delay, mfc_regs->d_display_delay); } - if (IS_MFCV7(dev)) { - WRITEL(reg, S5P_FIMV_D_DEC_OPTIONS_V6); + if (IS_MFCV7_PLUS(dev)) { + WRITEL(reg, mfc_regs->d_dec_options); reg = 0; } @@ -1309,22 +1367,22 @@ static int s5p_mfc_init_decode_v6(struct s5p_mfc_ctx *ctx) if (ctx->dst_fmt->fourcc == V4L2_PIX_FMT_NV12MT_16X16) reg |= (0x1 << S5P_FIMV_D_OPT_TILE_MODE_SHIFT_V6); - if (IS_MFCV7(dev)) - WRITEL(reg, S5P_FIMV_D_INIT_BUFFER_OPTIONS_V7); + if (IS_MFCV7_PLUS(dev)) + WRITEL(reg, mfc_regs->d_init_buffer_options); else - WRITEL(reg, S5P_FIMV_D_DEC_OPTIONS_V6); + WRITEL(reg, mfc_regs->d_dec_options); /* 0: NV12(CbCr), 1: NV21(CrCb) */ if (ctx->dst_fmt->fourcc == V4L2_PIX_FMT_NV21M) - WRITEL(0x1, S5P_FIMV_PIXEL_FORMAT_V6); + WRITEL(0x1, mfc_regs->pixel_format); else - WRITEL(0x0, S5P_FIMV_PIXEL_FORMAT_V6); + WRITEL(0x0, mfc_regs->pixel_format); /* sei parse */ - WRITEL(ctx->sei_fp_parse & 0x1, S5P_FIMV_D_SEI_ENABLE_V6); + WRITEL(ctx->sei_fp_parse & 0x1, mfc_regs->d_sei_enable); - WRITEL(ctx->inst_no, S5P_FIMV_INSTANCE_ID_V6); + WRITEL(ctx->inst_no, mfc_regs->instance_id); s5p_mfc_hw_call(dev->mfc_cmds, cmd_host2risc, dev, S5P_FIMV_CH_SEQ_HEADER_V6, NULL); @@ -1335,11 +1393,12 @@ static int s5p_mfc_init_decode_v6(struct s5p_mfc_ctx *ctx) static inline void s5p_mfc_set_flush(struct s5p_mfc_ctx *ctx, int flush) { struct s5p_mfc_dev *dev = ctx->dev; + const struct s5p_mfc_regs *mfc_regs = dev->mfc_regs; if (flush) { dev->curr_ctx = ctx->num; s5p_mfc_clean_ctx_int_flags(ctx); - WRITEL(ctx->inst_no, S5P_FIMV_INSTANCE_ID_V6); + WRITEL(ctx->inst_no, mfc_regs->instance_id); s5p_mfc_hw_call(dev->mfc_cmds, cmd_host2risc, dev, S5P_FIMV_H2R_CMD_FLUSH_V6, NULL); } @@ -1350,11 +1409,12 @@ static int s5p_mfc_decode_one_frame_v6(struct s5p_mfc_ctx *ctx, enum s5p_mfc_decode_arg last_frame) { struct s5p_mfc_dev *dev = ctx->dev; + const struct s5p_mfc_regs *mfc_regs = dev->mfc_regs; - WRITEL(ctx->dec_dst_flag, S5P_FIMV_D_AVAILABLE_DPB_FLAG_LOWER_V6); - WRITEL(ctx->slice_interface & 0x1, S5P_FIMV_D_SLICE_IF_ENABLE_V6); + WRITEL(ctx->dec_dst_flag, mfc_regs->d_available_dpb_flag_lower); + WRITEL(ctx->slice_interface & 0x1, mfc_regs->d_slice_if_enable); - WRITEL(ctx->inst_no, S5P_FIMV_INSTANCE_ID_V6); + WRITEL(ctx->inst_no, mfc_regs->instance_id); /* Issue different commands to instance basing on whether it * is the last frame or not. */ switch (last_frame) { @@ -1378,6 +1438,7 @@ static int s5p_mfc_decode_one_frame_v6(struct s5p_mfc_ctx *ctx, static int s5p_mfc_init_encode_v6(struct s5p_mfc_ctx *ctx) { struct s5p_mfc_dev *dev = ctx->dev; + const struct s5p_mfc_regs *mfc_regs = dev->mfc_regs; if (ctx->codec_mode == S5P_MFC_CODEC_H264_ENC) s5p_mfc_set_enc_params_h264(ctx); @@ -1393,13 +1454,13 @@ static int s5p_mfc_init_encode_v6(struct s5p_mfc_ctx *ctx) return -EINVAL; } - /* Set stride lengths */ - if (IS_MFCV7(dev)) { - WRITEL(ctx->img_width, S5P_FIMV_E_SOURCE_FIRST_STRIDE_V7); - WRITEL(ctx->img_width, S5P_FIMV_E_SOURCE_SECOND_STRIDE_V7); + /* Set stride lengths for v7 & above */ + if (IS_MFCV7_PLUS(dev)) { + WRITEL(ctx->img_width, mfc_regs->e_source_first_plane_stride); + WRITEL(ctx->img_width, mfc_regs->e_source_second_plane_stride); } - WRITEL(ctx->inst_no, S5P_FIMV_INSTANCE_ID_V6); + WRITEL(ctx->inst_no, mfc_regs->instance_id); s5p_mfc_hw_call(dev->mfc_cmds, cmd_host2risc, dev, S5P_FIMV_CH_SEQ_HEADER_V6, NULL); @@ -1409,14 +1470,16 @@ static int s5p_mfc_init_encode_v6(struct s5p_mfc_ctx *ctx) static int s5p_mfc_h264_set_aso_slice_order_v6(struct s5p_mfc_ctx *ctx) { struct s5p_mfc_dev *dev = ctx->dev; + const struct s5p_mfc_regs *mfc_regs = dev->mfc_regs; struct s5p_mfc_enc_params *p = &ctx->enc_params; struct s5p_mfc_h264_enc_params *p_h264 = &p->codec.h264; int i; if (p_h264->aso) { - for (i = 0; i < 8; i++) + for (i = 0; i < ARRAY_SIZE(p_h264->aso_slice_order); i++) { WRITEL(p_h264->aso_slice_order[i], - S5P_FIMV_E_H264_ASO_SLICE_ORDER_0_V6 + i * 4); + mfc_regs->e_h264_aso_slice_order_0 + i * 4); + } } return 0; } @@ -1425,6 +1488,7 @@ static int s5p_mfc_h264_set_aso_slice_order_v6(struct s5p_mfc_ctx *ctx) static int s5p_mfc_encode_one_frame_v6(struct s5p_mfc_ctx *ctx) { struct s5p_mfc_dev *dev = ctx->dev; + const struct s5p_mfc_regs *mfc_regs = dev->mfc_regs; mfc_debug(2, "++\n"); @@ -1435,7 +1499,7 @@ static int s5p_mfc_encode_one_frame_v6(struct s5p_mfc_ctx *ctx) s5p_mfc_set_slice_mode(ctx); - WRITEL(ctx->inst_no, S5P_FIMV_INSTANCE_ID_V6); + WRITEL(ctx->inst_no, mfc_regs->instance_id); s5p_mfc_hw_call(dev->mfc_cmds, cmd_host2risc, dev, S5P_FIMV_CH_FRAME_START_V6, NULL); @@ -1810,28 +1874,26 @@ static void s5p_mfc_cleanup_queue_v6(struct list_head *lh, struct vb2_queue *vq) static void s5p_mfc_clear_int_flags_v6(struct s5p_mfc_dev *dev) { - mfc_write(dev, 0, S5P_FIMV_RISC2HOST_CMD_V6); - mfc_write(dev, 0, S5P_FIMV_RISC2HOST_INT_V6); + const struct s5p_mfc_regs *mfc_regs = dev->mfc_regs; + WRITEL(0, mfc_regs->risc2host_command); + WRITEL(0, mfc_regs->risc2host_int); } static void s5p_mfc_write_info_v6(struct s5p_mfc_ctx *ctx, unsigned int data, unsigned int ofs) { - struct s5p_mfc_dev *dev = ctx->dev; - s5p_mfc_clock_on(); - WRITEL(data, ofs); + WRITEL(data, (void *)ofs); s5p_mfc_clock_off(); } static unsigned int s5p_mfc_read_info_v6(struct s5p_mfc_ctx *ctx, unsigned int ofs) { - struct s5p_mfc_dev *dev = ctx->dev; int ret; s5p_mfc_clock_on(); - ret = READL(ofs); + ret = READL((void *)ofs); s5p_mfc_clock_off(); return ret; @@ -1839,50 +1901,51 @@ s5p_mfc_read_info_v6(struct s5p_mfc_ctx *ctx, unsigned int ofs) static int s5p_mfc_get_dspl_y_adr_v6(struct s5p_mfc_dev *dev) { - return mfc_read(dev, S5P_FIMV_D_DISPLAY_LUMA_ADDR_V6); + return READL(dev->mfc_regs->d_display_first_plane_addr); } static int s5p_mfc_get_dec_y_adr_v6(struct s5p_mfc_dev *dev) { - return mfc_read(dev, S5P_FIMV_D_DECODED_LUMA_ADDR_V6); + return READL(dev->mfc_regs->d_decoded_first_plane_addr); } static int s5p_mfc_get_dspl_status_v6(struct s5p_mfc_dev *dev) { - return mfc_read(dev, S5P_FIMV_D_DISPLAY_STATUS_V6); + return READL(dev->mfc_regs->d_display_status); } static int s5p_mfc_get_dec_status_v6(struct s5p_mfc_dev *dev) { - return mfc_read(dev, S5P_FIMV_D_DECODED_STATUS_V6); + return READL(dev->mfc_regs->d_decoded_status); } static int s5p_mfc_get_dec_frame_type_v6(struct s5p_mfc_dev *dev) { - return mfc_read(dev, S5P_FIMV_D_DECODED_FRAME_TYPE_V6) & + return READL(dev->mfc_regs->d_decoded_frame_type) & S5P_FIMV_DECODE_FRAME_MASK_V6; } static int s5p_mfc_get_disp_frame_type_v6(struct s5p_mfc_ctx *ctx) { - return mfc_read(ctx->dev, S5P_FIMV_D_DISPLAY_FRAME_TYPE_V6) & + struct s5p_mfc_dev *dev = ctx->dev; + return READL(dev->mfc_regs->d_display_frame_type) & S5P_FIMV_DECODE_FRAME_MASK_V6; } static int s5p_mfc_get_consumed_stream_v6(struct s5p_mfc_dev *dev) { - return mfc_read(dev, S5P_FIMV_D_DECODED_NAL_SIZE_V6); + return READL(dev->mfc_regs->d_decoded_nal_size); } static int s5p_mfc_get_int_reason_v6(struct s5p_mfc_dev *dev) { - return mfc_read(dev, S5P_FIMV_RISC2HOST_CMD_V6) & + return READL(dev->mfc_regs->risc2host_command) & S5P_FIMV_RISC2HOST_CMD_MASK; } static int s5p_mfc_get_int_err_v6(struct s5p_mfc_dev *dev) { - return mfc_read(dev, S5P_FIMV_ERROR_CODE_V6); + return READL(dev->mfc_regs->error_code); } static int s5p_mfc_err_dec_v6(unsigned int err) @@ -1897,82 +1960,323 @@ static int s5p_mfc_err_dspl_v6(unsigned int err) static int s5p_mfc_get_img_width_v6(struct s5p_mfc_dev *dev) { - return mfc_read(dev, S5P_FIMV_D_DISPLAY_FRAME_WIDTH_V6); + return READL(dev->mfc_regs->d_display_frame_width); } static int s5p_mfc_get_img_height_v6(struct s5p_mfc_dev *dev) { - return mfc_read(dev, S5P_FIMV_D_DISPLAY_FRAME_HEIGHT_V6); + return READL(dev->mfc_regs->d_display_frame_height); } static int s5p_mfc_get_dpb_count_v6(struct s5p_mfc_dev *dev) { - return mfc_read(dev, S5P_FIMV_D_MIN_NUM_DPB_V6); + return READL(dev->mfc_regs->d_min_num_dpb); } static int s5p_mfc_get_mv_count_v6(struct s5p_mfc_dev *dev) { - return mfc_read(dev, S5P_FIMV_D_MIN_NUM_MV_V6); + return READL(dev->mfc_regs->d_min_num_mv); } static int s5p_mfc_get_inst_no_v6(struct s5p_mfc_dev *dev) { - return mfc_read(dev, S5P_FIMV_RET_INSTANCE_ID_V6); + return READL(dev->mfc_regs->ret_instance_id); } static int s5p_mfc_get_enc_dpb_count_v6(struct s5p_mfc_dev *dev) { - return mfc_read(dev, S5P_FIMV_E_NUM_DPB_V6); + return READL(dev->mfc_regs->e_num_dpb); } static int s5p_mfc_get_enc_strm_size_v6(struct s5p_mfc_dev *dev) { - return mfc_read(dev, S5P_FIMV_E_STREAM_SIZE_V6); + return READL(dev->mfc_regs->e_stream_size); } static int s5p_mfc_get_enc_slice_type_v6(struct s5p_mfc_dev *dev) { - return mfc_read(dev, S5P_FIMV_E_SLICE_TYPE_V6); + return READL(dev->mfc_regs->e_slice_type); } static int s5p_mfc_get_enc_pic_count_v6(struct s5p_mfc_dev *dev) { - return mfc_read(dev, S5P_FIMV_E_PICTURE_COUNT_V6); + return READL(dev->mfc_regs->e_picture_count); } static int s5p_mfc_get_sei_avail_status_v6(struct s5p_mfc_ctx *ctx) { - return mfc_read(ctx->dev, S5P_FIMV_D_FRAME_PACK_SEI_AVAIL_V6); + struct s5p_mfc_dev *dev = ctx->dev; + return READL(dev->mfc_regs->d_frame_pack_sei_avail); } static int s5p_mfc_get_mvc_num_views_v6(struct s5p_mfc_dev *dev) { - return mfc_read(dev, S5P_FIMV_D_MVC_NUM_VIEWS_V6); + return READL(dev->mfc_regs->d_mvc_num_views); } static int s5p_mfc_get_mvc_view_id_v6(struct s5p_mfc_dev *dev) { - return mfc_read(dev, S5P_FIMV_D_MVC_VIEW_ID_V6); + return READL(dev->mfc_regs->d_mvc_view_id); } static unsigned int s5p_mfc_get_pic_type_top_v6(struct s5p_mfc_ctx *ctx) { - return s5p_mfc_read_info_v6(ctx, PIC_TIME_TOP_V6); + return s5p_mfc_read_info_v6(ctx, + (unsigned int) ctx->dev->mfc_regs->d_ret_picture_tag_top); } static unsigned int s5p_mfc_get_pic_type_bot_v6(struct s5p_mfc_ctx *ctx) { - return s5p_mfc_read_info_v6(ctx, PIC_TIME_BOT_V6); + return s5p_mfc_read_info_v6(ctx, + (unsigned int) ctx->dev->mfc_regs->d_ret_picture_tag_bot); } static unsigned int s5p_mfc_get_crop_info_h_v6(struct s5p_mfc_ctx *ctx) { - return s5p_mfc_read_info_v6(ctx, CROP_INFO_H_V6); + return s5p_mfc_read_info_v6(ctx, + (unsigned int) ctx->dev->mfc_regs->d_display_crop_info1); } static unsigned int s5p_mfc_get_crop_info_v_v6(struct s5p_mfc_ctx *ctx) { - return s5p_mfc_read_info_v6(ctx, CROP_INFO_V_V6); + return s5p_mfc_read_info_v6(ctx, + (unsigned int) ctx->dev->mfc_regs->d_display_crop_info2); +} + +static struct s5p_mfc_regs mfc_regs; + +/* Initialize registers for MFC v6 onwards */ +const struct s5p_mfc_regs *s5p_mfc_init_regs_v6_plus(struct s5p_mfc_dev *dev) +{ + memset(&mfc_regs, 0, sizeof(mfc_regs)); + +#define S5P_MFC_REG_ADDR(dev, reg) ((dev)->regs_base + (reg)) +#define R(m, r) mfc_regs.m = S5P_MFC_REG_ADDR(dev, r) + /* codec common registers */ + R(risc_on, S5P_FIMV_RISC_ON_V6); + R(risc2host_int, S5P_FIMV_RISC2HOST_INT_V6); + R(host2risc_int, S5P_FIMV_HOST2RISC_INT_V6); + R(risc_base_address, S5P_FIMV_RISC_BASE_ADDRESS_V6); + R(mfc_reset, S5P_FIMV_MFC_RESET_V6); + R(host2risc_command, S5P_FIMV_HOST2RISC_CMD_V6); + R(risc2host_command, S5P_FIMV_RISC2HOST_CMD_V6); + R(firmware_version, S5P_FIMV_FW_VERSION_V6); + R(instance_id, S5P_FIMV_INSTANCE_ID_V6); + R(codec_type, S5P_FIMV_CODEC_TYPE_V6); + R(context_mem_addr, S5P_FIMV_CONTEXT_MEM_ADDR_V6); + R(context_mem_size, S5P_FIMV_CONTEXT_MEM_SIZE_V6); + R(pixel_format, S5P_FIMV_PIXEL_FORMAT_V6); + R(ret_instance_id, S5P_FIMV_RET_INSTANCE_ID_V6); + R(error_code, S5P_FIMV_ERROR_CODE_V6); + + /* decoder registers */ + R(d_crc_ctrl, S5P_FIMV_D_CRC_CTRL_V6); + R(d_dec_options, S5P_FIMV_D_DEC_OPTIONS_V6); + R(d_display_delay, S5P_FIMV_D_DISPLAY_DELAY_V6); + R(d_sei_enable, S5P_FIMV_D_SEI_ENABLE_V6); + R(d_min_num_dpb, S5P_FIMV_D_MIN_NUM_DPB_V6); + R(d_min_num_mv, S5P_FIMV_D_MIN_NUM_MV_V6); + R(d_mvc_num_views, S5P_FIMV_D_MVC_NUM_VIEWS_V6); + R(d_num_dpb, S5P_FIMV_D_NUM_DPB_V6); + R(d_num_mv, S5P_FIMV_D_NUM_MV_V6); + R(d_init_buffer_options, S5P_FIMV_D_INIT_BUFFER_OPTIONS_V6); + R(d_first_plane_dpb_size, S5P_FIMV_D_LUMA_DPB_SIZE_V6); + R(d_second_plane_dpb_size, S5P_FIMV_D_CHROMA_DPB_SIZE_V6); + R(d_mv_buffer_size, S5P_FIMV_D_MV_BUFFER_SIZE_V6); + R(d_first_plane_dpb, S5P_FIMV_D_LUMA_DPB_V6); + R(d_second_plane_dpb, S5P_FIMV_D_CHROMA_DPB_V6); + R(d_mv_buffer, S5P_FIMV_D_MV_BUFFER_V6); + R(d_scratch_buffer_addr, S5P_FIMV_D_SCRATCH_BUFFER_ADDR_V6); + R(d_scratch_buffer_size, S5P_FIMV_D_SCRATCH_BUFFER_SIZE_V6); + R(d_cpb_buffer_addr, S5P_FIMV_D_CPB_BUFFER_ADDR_V6); + R(d_cpb_buffer_size, S5P_FIMV_D_CPB_BUFFER_SIZE_V6); + R(d_available_dpb_flag_lower, S5P_FIMV_D_AVAILABLE_DPB_FLAG_LOWER_V6); + R(d_cpb_buffer_offset, S5P_FIMV_D_CPB_BUFFER_OFFSET_V6); + R(d_slice_if_enable, S5P_FIMV_D_SLICE_IF_ENABLE_V6); + R(d_stream_data_size, S5P_FIMV_D_STREAM_DATA_SIZE_V6); + R(d_display_frame_width, S5P_FIMV_D_DISPLAY_FRAME_WIDTH_V6); + R(d_display_frame_height, S5P_FIMV_D_DISPLAY_FRAME_HEIGHT_V6); + R(d_display_status, S5P_FIMV_D_DISPLAY_STATUS_V6); + R(d_display_first_plane_addr, S5P_FIMV_D_DISPLAY_LUMA_ADDR_V6); + R(d_display_second_plane_addr, S5P_FIMV_D_DISPLAY_CHROMA_ADDR_V6); + R(d_display_frame_type, S5P_FIMV_D_DISPLAY_FRAME_TYPE_V6); + R(d_display_crop_info1, S5P_FIMV_D_DISPLAY_CROP_INFO1_V6); + R(d_display_crop_info2, S5P_FIMV_D_DISPLAY_CROP_INFO2_V6); + R(d_display_aspect_ratio, S5P_FIMV_D_DISPLAY_ASPECT_RATIO_V6); + R(d_display_extended_ar, S5P_FIMV_D_DISPLAY_EXTENDED_AR_V6); + R(d_decoded_status, S5P_FIMV_D_DECODED_STATUS_V6); + R(d_decoded_first_plane_addr, S5P_FIMV_D_DECODED_LUMA_ADDR_V6); + R(d_decoded_second_plane_addr, S5P_FIMV_D_DECODED_CHROMA_ADDR_V6); + R(d_decoded_frame_type, S5P_FIMV_D_DECODED_FRAME_TYPE_V6); + R(d_decoded_nal_size, S5P_FIMV_D_DECODED_NAL_SIZE_V6); + R(d_ret_picture_tag_top, S5P_FIMV_D_RET_PICTURE_TAG_TOP_V6); + R(d_ret_picture_tag_bot, S5P_FIMV_D_RET_PICTURE_TAG_BOT_V6); + R(d_h264_info, S5P_FIMV_D_H264_INFO_V6); + R(d_mvc_view_id, S5P_FIMV_D_MVC_VIEW_ID_V6); + R(d_frame_pack_sei_avail, S5P_FIMV_D_FRAME_PACK_SEI_AVAIL_V6); + + /* encoder registers */ + R(e_frame_width, S5P_FIMV_E_FRAME_WIDTH_V6); + R(e_frame_height, S5P_FIMV_E_FRAME_HEIGHT_V6); + R(e_cropped_frame_width, S5P_FIMV_E_CROPPED_FRAME_WIDTH_V6); + R(e_cropped_frame_height, S5P_FIMV_E_CROPPED_FRAME_HEIGHT_V6); + R(e_frame_crop_offset, S5P_FIMV_E_FRAME_CROP_OFFSET_V6); + R(e_enc_options, S5P_FIMV_E_ENC_OPTIONS_V6); + R(e_picture_profile, S5P_FIMV_E_PICTURE_PROFILE_V6); + R(e_vbv_buffer_size, S5P_FIMV_E_VBV_BUFFER_SIZE_V6); + R(e_vbv_init_delay, S5P_FIMV_E_VBV_INIT_DELAY_V6); + R(e_fixed_picture_qp, S5P_FIMV_E_FIXED_PICTURE_QP_V6); + R(e_rc_config, S5P_FIMV_E_RC_CONFIG_V6); + R(e_rc_qp_bound, S5P_FIMV_E_RC_QP_BOUND_V6); + R(e_rc_mode, S5P_FIMV_E_RC_RPARAM_V6); + R(e_mb_rc_config, S5P_FIMV_E_MB_RC_CONFIG_V6); + R(e_padding_ctrl, S5P_FIMV_E_PADDING_CTRL_V6); + R(e_mv_hor_range, S5P_FIMV_E_MV_HOR_RANGE_V6); + R(e_mv_ver_range, S5P_FIMV_E_MV_VER_RANGE_V6); + R(e_num_dpb, S5P_FIMV_E_NUM_DPB_V6); + R(e_luma_dpb, S5P_FIMV_E_LUMA_DPB_V6); + R(e_chroma_dpb, S5P_FIMV_E_CHROMA_DPB_V6); + R(e_me_buffer, S5P_FIMV_E_ME_BUFFER_V6); + R(e_scratch_buffer_addr, S5P_FIMV_E_SCRATCH_BUFFER_ADDR_V6); + R(e_scratch_buffer_size, S5P_FIMV_E_SCRATCH_BUFFER_SIZE_V6); + R(e_tmv_buffer0, S5P_FIMV_E_TMV_BUFFER0_V6); + R(e_tmv_buffer1, S5P_FIMV_E_TMV_BUFFER1_V6); + R(e_source_first_plane_addr, S5P_FIMV_E_SOURCE_LUMA_ADDR_V6); + R(e_source_second_plane_addr, S5P_FIMV_E_SOURCE_CHROMA_ADDR_V6); + R(e_stream_buffer_addr, S5P_FIMV_E_STREAM_BUFFER_ADDR_V6); + R(e_stream_buffer_size, S5P_FIMV_E_STREAM_BUFFER_SIZE_V6); + R(e_roi_buffer_addr, S5P_FIMV_E_ROI_BUFFER_ADDR_V6); + R(e_param_change, S5P_FIMV_E_PARAM_CHANGE_V6); + R(e_ir_size, S5P_FIMV_E_IR_SIZE_V6); + R(e_gop_config, S5P_FIMV_E_GOP_CONFIG_V6); + R(e_mslice_mode, S5P_FIMV_E_MSLICE_MODE_V6); + R(e_mslice_size_mb, S5P_FIMV_E_MSLICE_SIZE_MB_V6); + R(e_mslice_size_bits, S5P_FIMV_E_MSLICE_SIZE_BITS_V6); + R(e_frame_insertion, S5P_FIMV_E_FRAME_INSERTION_V6); + R(e_rc_frame_rate, S5P_FIMV_E_RC_FRAME_RATE_V6); + R(e_rc_bit_rate, S5P_FIMV_E_RC_BIT_RATE_V6); + R(e_rc_roi_ctrl, S5P_FIMV_E_RC_ROI_CTRL_V6); + R(e_picture_tag, S5P_FIMV_E_PICTURE_TAG_V6); + R(e_bit_count_enable, S5P_FIMV_E_BIT_COUNT_ENABLE_V6); + R(e_max_bit_count, S5P_FIMV_E_MAX_BIT_COUNT_V6); + R(e_min_bit_count, S5P_FIMV_E_MIN_BIT_COUNT_V6); + R(e_metadata_buffer_addr, S5P_FIMV_E_METADATA_BUFFER_ADDR_V6); + R(e_metadata_buffer_size, S5P_FIMV_E_METADATA_BUFFER_SIZE_V6); + R(e_encoded_source_first_plane_addr, + S5P_FIMV_E_ENCODED_SOURCE_LUMA_ADDR_V6); + R(e_encoded_source_second_plane_addr, + S5P_FIMV_E_ENCODED_SOURCE_CHROMA_ADDR_V6); + R(e_stream_size, S5P_FIMV_E_STREAM_SIZE_V6); + R(e_slice_type, S5P_FIMV_E_SLICE_TYPE_V6); + R(e_picture_count, S5P_FIMV_E_PICTURE_COUNT_V6); + R(e_ret_picture_tag, S5P_FIMV_E_RET_PICTURE_TAG_V6); + R(e_recon_luma_dpb_addr, S5P_FIMV_E_RECON_LUMA_DPB_ADDR_V6); + R(e_recon_chroma_dpb_addr, S5P_FIMV_E_RECON_CHROMA_DPB_ADDR_V6); + R(e_mpeg4_options, S5P_FIMV_E_MPEG4_OPTIONS_V6); + R(e_mpeg4_hec_period, S5P_FIMV_E_MPEG4_HEC_PERIOD_V6); + R(e_aspect_ratio, S5P_FIMV_E_ASPECT_RATIO_V6); + R(e_extended_sar, S5P_FIMV_E_EXTENDED_SAR_V6); + R(e_h264_options, S5P_FIMV_E_H264_OPTIONS_V6); + R(e_h264_lf_alpha_offset, S5P_FIMV_E_H264_LF_ALPHA_OFFSET_V6); + R(e_h264_lf_beta_offset, S5P_FIMV_E_H264_LF_BETA_OFFSET_V6); + R(e_h264_i_period, S5P_FIMV_E_H264_I_PERIOD_V6); + R(e_h264_fmo_slice_grp_map_type, + S5P_FIMV_E_H264_FMO_SLICE_GRP_MAP_TYPE_V6); + R(e_h264_fmo_num_slice_grp_minus1, + S5P_FIMV_E_H264_FMO_NUM_SLICE_GRP_MINUS1_V6); + R(e_h264_fmo_slice_grp_change_dir, + S5P_FIMV_E_H264_FMO_SLICE_GRP_CHANGE_DIR_V6); + R(e_h264_fmo_slice_grp_change_rate_minus1, + S5P_FIMV_E_H264_FMO_SLICE_GRP_CHANGE_RATE_MINUS1_V6); + R(e_h264_fmo_run_length_minus1_0, + S5P_FIMV_E_H264_FMO_RUN_LENGTH_MINUS1_0_V6); + R(e_h264_aso_slice_order_0, S5P_FIMV_E_H264_ASO_SLICE_ORDER_0_V6); + R(e_h264_num_t_layer, S5P_FIMV_E_H264_NUM_T_LAYER_V6); + R(e_h264_hierarchical_qp_layer0, + S5P_FIMV_E_H264_HIERARCHICAL_QP_LAYER0_V6); + R(e_h264_frame_packing_sei_info, + S5P_FIMV_E_H264_FRAME_PACKING_SEI_INFO_V6); + + if (!IS_MFCV7_PLUS(dev)) + goto done; + + /* Initialize registers used in MFC v7+ */ + R(e_source_first_plane_addr, S5P_FIMV_E_SOURCE_FIRST_ADDR_V7); + R(e_source_second_plane_addr, S5P_FIMV_E_SOURCE_SECOND_ADDR_V7); + R(e_source_third_plane_addr, S5P_FIMV_E_SOURCE_THIRD_ADDR_V7); + R(e_source_first_plane_stride, S5P_FIMV_E_SOURCE_FIRST_STRIDE_V7); + R(e_source_second_plane_stride, S5P_FIMV_E_SOURCE_SECOND_STRIDE_V7); + R(e_source_third_plane_stride, S5P_FIMV_E_SOURCE_THIRD_STRIDE_V7); + R(e_encoded_source_first_plane_addr, + S5P_FIMV_E_ENCODED_SOURCE_FIRST_ADDR_V7); + R(e_encoded_source_second_plane_addr, + S5P_FIMV_E_ENCODED_SOURCE_SECOND_ADDR_V7); + R(e_vp8_options, S5P_FIMV_E_VP8_OPTIONS_V7); + + if (!IS_MFCV8(dev)) + goto done; + + /* Initialize registers used in MFC v8 only. + * Also, over-write the registers which have + * a different offset for MFC v8. */ + R(d_stream_data_size, S5P_FIMV_D_STREAM_DATA_SIZE_V8); + R(d_cpb_buffer_addr, S5P_FIMV_D_CPB_BUFFER_ADDR_V8); + R(d_cpb_buffer_size, S5P_FIMV_D_CPB_BUFFER_SIZE_V8); + R(d_cpb_buffer_offset, S5P_FIMV_D_CPB_BUFFER_OFFSET_V8); + R(d_first_plane_dpb_size, S5P_FIMV_D_FIRST_PLANE_DPB_SIZE_V8); + R(d_second_plane_dpb_size, S5P_FIMV_D_SECOND_PLANE_DPB_SIZE_V8); + R(d_scratch_buffer_addr, S5P_FIMV_D_SCRATCH_BUFFER_ADDR_V8); + R(d_scratch_buffer_size, S5P_FIMV_D_SCRATCH_BUFFER_SIZE_V8); + R(d_first_plane_dpb_stride_size, + S5P_FIMV_D_FIRST_PLANE_DPB_STRIDE_SIZE_V8); + R(d_second_plane_dpb_stride_size, + S5P_FIMV_D_SECOND_PLANE_DPB_STRIDE_SIZE_V8); + R(d_mv_buffer_size, S5P_FIMV_D_MV_BUFFER_SIZE_V8); + R(d_num_mv, S5P_FIMV_D_NUM_MV_V8); + R(d_first_plane_dpb, S5P_FIMV_D_FIRST_PLANE_DPB_V8); + R(d_second_plane_dpb, S5P_FIMV_D_SECOND_PLANE_DPB_V8); + R(d_mv_buffer, S5P_FIMV_D_MV_BUFFER_V8); + R(d_init_buffer_options, S5P_FIMV_D_INIT_BUFFER_OPTIONS_V8); + R(d_available_dpb_flag_lower, S5P_FIMV_D_AVAILABLE_DPB_FLAG_LOWER_V8); + R(d_slice_if_enable, S5P_FIMV_D_SLICE_IF_ENABLE_V8); + R(d_display_first_plane_addr, S5P_FIMV_D_DISPLAY_FIRST_PLANE_ADDR_V8); + R(d_display_second_plane_addr, S5P_FIMV_D_DISPLAY_SECOND_PLANE_ADDR_V8); + R(d_decoded_first_plane_addr, S5P_FIMV_D_DECODED_FIRST_PLANE_ADDR_V8); + R(d_decoded_second_plane_addr, S5P_FIMV_D_DECODED_SECOND_PLANE_ADDR_V8); + R(d_display_status, S5P_FIMV_D_DISPLAY_STATUS_V8); + R(d_decoded_status, S5P_FIMV_D_DECODED_STATUS_V8); + R(d_decoded_frame_type, S5P_FIMV_D_DECODED_FRAME_TYPE_V8); + R(d_display_frame_type, S5P_FIMV_D_DISPLAY_FRAME_TYPE_V8); + R(d_decoded_nal_size, S5P_FIMV_D_DECODED_NAL_SIZE_V8); + R(d_display_frame_width, S5P_FIMV_D_DISPLAY_FRAME_WIDTH_V8); + R(d_display_frame_height, S5P_FIMV_D_DISPLAY_FRAME_HEIGHT_V8); + R(d_frame_pack_sei_avail, S5P_FIMV_D_FRAME_PACK_SEI_AVAIL_V8); + R(d_mvc_num_views, S5P_FIMV_D_MVC_NUM_VIEWS_V8); + R(d_mvc_view_id, S5P_FIMV_D_MVC_VIEW_ID_V8); + R(d_ret_picture_tag_top, S5P_FIMV_D_RET_PICTURE_TAG_TOP_V8); + R(d_ret_picture_tag_bot, S5P_FIMV_D_RET_PICTURE_TAG_BOT_V8); + R(d_display_crop_info1, S5P_FIMV_D_DISPLAY_CROP_INFO1_V8); + R(d_display_crop_info2, S5P_FIMV_D_DISPLAY_CROP_INFO2_V8); + + /* encoder registers */ + R(e_padding_ctrl, S5P_FIMV_E_PADDING_CTRL_V8); + R(e_rc_config, S5P_FIMV_E_RC_CONFIG_V8); + R(e_rc_mode, S5P_FIMV_E_RC_RPARAM_V8); + R(e_mv_hor_range, S5P_FIMV_E_MV_HOR_RANGE_V8); + R(e_mv_ver_range, S5P_FIMV_E_MV_VER_RANGE_V8); + R(e_rc_qp_bound, S5P_FIMV_E_RC_QP_BOUND_V8); + R(e_fixed_picture_qp, S5P_FIMV_E_FIXED_PICTURE_QP_V8); + R(e_vbv_buffer_size, S5P_FIMV_E_VBV_BUFFER_SIZE_V8); + R(e_vbv_init_delay, S5P_FIMV_E_VBV_INIT_DELAY_V8); + R(e_mb_rc_config, S5P_FIMV_E_MB_RC_CONFIG_V8); + R(e_aspect_ratio, S5P_FIMV_E_ASPECT_RATIO_V8); + R(e_extended_sar, S5P_FIMV_E_EXTENDED_SAR_V8); + R(e_h264_options, S5P_FIMV_E_H264_OPTIONS_V8); + +done: + return &mfc_regs; +#undef S5P_MFC_REG_ADDR +#undef R } /* Initialize opr function pointers for MFC v6 */ diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.h b/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.h index ab164efa127e..80558484bb40 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.h +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.h @@ -40,11 +40,6 @@ #define FRAME_DELTA_H264_H263 1 #define TIGHT_CBR_MAX 10 -/* Definitions for shared memory compatibility */ -#define PIC_TIME_TOP_V6 S5P_FIMV_D_RET_PICTURE_TAG_TOP_V6 -#define PIC_TIME_BOT_V6 S5P_FIMV_D_RET_PICTURE_TAG_BOT_V6 -#define CROP_INFO_H_V6 S5P_FIMV_D_DISPLAY_CROP_INFO1_V6 -#define CROP_INFO_V_V6 S5P_FIMV_D_DISPLAY_CROP_INFO2_V6 - struct s5p_mfc_hw_ops *s5p_mfc_init_hw_ops_v6(void); +const struct s5p_mfc_regs *s5p_mfc_init_regs_v6_plus(struct s5p_mfc_dev *dev); #endif /* S5P_MFC_OPR_V6_H_ */ diff --git a/drivers/media/platform/s5p-tv/hdmi_drv.c b/drivers/media/platform/s5p-tv/hdmi_drv.c index 534722c04ec5..754740f4b671 100644 --- a/drivers/media/platform/s5p-tv/hdmi_drv.c +++ b/drivers/media/platform/s5p-tv/hdmi_drv.c @@ -674,6 +674,8 @@ static int hdmi_g_mbus_fmt(struct v4l2_subdev *sd, static int hdmi_enum_dv_timings(struct v4l2_subdev *sd, struct v4l2_enum_dv_timings *timings) { + if (timings->pad != 0) + return -EINVAL; if (timings->index >= ARRAY_SIZE(hdmi_timings)) return -EINVAL; timings->timings = hdmi_timings[timings->index].dv_timings; @@ -687,8 +689,11 @@ static int hdmi_dv_timings_cap(struct v4l2_subdev *sd, { struct hdmi_device *hdev = sd_to_hdmi_dev(sd); + if (cap->pad != 0) + return -EINVAL; + /* Let the phy fill in the pixelclock range */ - v4l2_subdev_call(hdev->phy_sd, video, dv_timings_cap, cap); + v4l2_subdev_call(hdev->phy_sd, pad, dv_timings_cap, cap); cap->type = V4L2_DV_BT_656_1120; cap->bt.min_width = 720; cap->bt.max_width = 1920; @@ -707,12 +712,15 @@ static const struct v4l2_subdev_core_ops hdmi_sd_core_ops = { static const struct v4l2_subdev_video_ops hdmi_sd_video_ops = { .s_dv_timings = hdmi_s_dv_timings, .g_dv_timings = hdmi_g_dv_timings, - .enum_dv_timings = hdmi_enum_dv_timings, - .dv_timings_cap = hdmi_dv_timings_cap, .g_mbus_fmt = hdmi_g_mbus_fmt, .s_stream = hdmi_s_stream, }; +static const struct v4l2_subdev_pad_ops hdmi_sd_pad_ops = { + .enum_dv_timings = hdmi_enum_dv_timings, + .dv_timings_cap = hdmi_dv_timings_cap, +}; + static const struct v4l2_subdev_ops hdmi_sd_ops = { .core = &hdmi_sd_core_ops, .video = &hdmi_sd_video_ops, diff --git a/drivers/media/platform/s5p-tv/hdmiphy_drv.c b/drivers/media/platform/s5p-tv/hdmiphy_drv.c index e19a0af1ea4f..c2f2e35642f2 100644 --- a/drivers/media/platform/s5p-tv/hdmiphy_drv.c +++ b/drivers/media/platform/s5p-tv/hdmiphy_drv.c @@ -225,6 +225,9 @@ static int hdmiphy_s_dv_timings(struct v4l2_subdev *sd, static int hdmiphy_dv_timings_cap(struct v4l2_subdev *sd, struct v4l2_dv_timings_cap *cap) { + if (cap->pad != 0) + return -EINVAL; + cap->type = V4L2_DV_BT_656_1120; /* The phy only determines the pixelclock, leave the other values * at 0 to signify that we have no information for them. */ @@ -259,13 +262,17 @@ static const struct v4l2_subdev_core_ops hdmiphy_core_ops = { static const struct v4l2_subdev_video_ops hdmiphy_video_ops = { .s_dv_timings = hdmiphy_s_dv_timings, - .dv_timings_cap = hdmiphy_dv_timings_cap, .s_stream = hdmiphy_s_stream, }; +static const struct v4l2_subdev_pad_ops hdmiphy_pad_ops = { + .dv_timings_cap = hdmiphy_dv_timings_cap, +}; + static const struct v4l2_subdev_ops hdmiphy_ops = { .core = &hdmiphy_core_ops, .video = &hdmiphy_video_ops, + .pad = &hdmiphy_pad_ops, }; static int hdmiphy_probe(struct i2c_client *client, diff --git a/drivers/media/platform/s5p-tv/mixer_video.c b/drivers/media/platform/s5p-tv/mixer_video.c index a1ce55fd30f3..8a8dbc8fdfde 100644 --- a/drivers/media/platform/s5p-tv/mixer_video.c +++ b/drivers/media/platform/s5p-tv/mixer_video.c @@ -509,9 +509,11 @@ static int mxr_enum_dv_timings(struct file *file, void *fh, struct mxr_device *mdev = layer->mdev; int ret; + timings->pad = 0; + /* lock protects from changing sd_out */ mutex_lock(&mdev->mutex); - ret = v4l2_subdev_call(to_outsd(mdev), video, enum_dv_timings, timings); + ret = v4l2_subdev_call(to_outsd(mdev), pad, enum_dv_timings, timings); mutex_unlock(&mdev->mutex); return ret ? -EINVAL : 0; @@ -567,9 +569,11 @@ static int mxr_dv_timings_cap(struct file *file, void *fh, struct mxr_device *mdev = layer->mdev; int ret; + cap->pad = 0; + /* lock protects from changing sd_out */ mutex_lock(&mdev->mutex); - ret = v4l2_subdev_call(to_outsd(mdev), video, dv_timings_cap, cap); + ret = v4l2_subdev_call(to_outsd(mdev), pad, dv_timings_cap, cap); mutex_unlock(&mdev->mutex); return ret ? -EINVAL : 0; @@ -985,7 +989,7 @@ static void mxr_watchdog(unsigned long arg) spin_unlock_irqrestore(&layer->enq_slock, flags); } -static int stop_streaming(struct vb2_queue *vq) +static void stop_streaming(struct vb2_queue *vq) { struct mxr_layer *layer = vb2_get_drv_priv(vq); struct mxr_device *mdev = layer->mdev; @@ -1031,7 +1035,6 @@ static int stop_streaming(struct vb2_queue *vq) mxr_streamer_put(mdev); /* allow changes in output configuration */ mxr_output_put(mdev); - return 0; } static struct vb2_ops mxr_video_qops = { diff --git a/drivers/media/platform/soc_camera/atmel-isi.c b/drivers/media/platform/soc_camera/atmel-isi.c index f0b6c900034d..38c723aca438 100644 --- a/drivers/media/platform/soc_camera/atmel-isi.c +++ b/drivers/media/platform/soc_camera/atmel-isi.c @@ -406,7 +406,7 @@ static int start_streaming(struct vb2_queue *vq, unsigned int count) } /* abort streaming and wait for last buffer */ -static int stop_streaming(struct vb2_queue *vq) +static void stop_streaming(struct vb2_queue *vq) { struct soc_camera_device *icd = soc_camera_from_vb2q(vq); struct soc_camera_host *ici = to_soc_camera_host(icd->parent); @@ -433,7 +433,7 @@ static int stop_streaming(struct vb2_queue *vq) if (time_after(jiffies, timeout)) { dev_err(icd->parent, "Timeout waiting for finishing codec request\n"); - return -ETIMEDOUT; + return; } /* Disable interrupts */ @@ -444,8 +444,6 @@ static int stop_streaming(struct vb2_queue *vq) ret = atmel_isi_wait_status(isi, WAIT_ISI_DISABLE); if (ret < 0) dev_err(icd->parent, "Disable ISI timed out\n"); - - return ret; } static struct vb2_ops isi_video_qops = { diff --git a/drivers/media/platform/soc_camera/mx2_camera.c b/drivers/media/platform/soc_camera/mx2_camera.c index 3e844803bdca..b40bc2e5ba47 100644 --- a/drivers/media/platform/soc_camera/mx2_camera.c +++ b/drivers/media/platform/soc_camera/mx2_camera.c @@ -741,7 +741,7 @@ static int mx2_start_streaming(struct vb2_queue *q, unsigned int count) return 0; } -static int mx2_stop_streaming(struct vb2_queue *q) +static void mx2_stop_streaming(struct vb2_queue *q) { struct soc_camera_device *icd = soc_camera_from_vb2q(q); struct soc_camera_host *ici = @@ -773,8 +773,6 @@ static int mx2_stop_streaming(struct vb2_queue *q) dma_free_coherent(ici->v4l2_dev.dev, pcdev->discard_size, b, pcdev->discard_buffer_dma); - - return 0; } static struct vb2_ops mx2_videobuf_ops = { diff --git a/drivers/media/platform/soc_camera/mx3_camera.c b/drivers/media/platform/soc_camera/mx3_camera.c index 9ed81ac6881c..83315dfeef62 100644 --- a/drivers/media/platform/soc_camera/mx3_camera.c +++ b/drivers/media/platform/soc_camera/mx3_camera.c @@ -406,7 +406,7 @@ static int mx3_videobuf_init(struct vb2_buffer *vb) return 0; } -static int mx3_stop_streaming(struct vb2_queue *q) +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); @@ -430,8 +430,6 @@ static int mx3_stop_streaming(struct vb2_queue *q) } spin_unlock_irqrestore(&mx3_cam->lock, flags); - - return 0; } static struct vb2_ops mx3_videobuf_ops = { diff --git a/drivers/media/platform/soc_camera/rcar_vin.c b/drivers/media/platform/soc_camera/rcar_vin.c index 704eee766487..e594230e84d3 100644 --- a/drivers/media/platform/soc_camera/rcar_vin.c +++ b/drivers/media/platform/soc_camera/rcar_vin.c @@ -513,7 +513,7 @@ static int rcar_vin_videobuf_init(struct vb2_buffer *vb) return 0; } -static int rcar_vin_stop_streaming(struct vb2_queue *vq) +static void rcar_vin_stop_streaming(struct vb2_queue *vq) { struct soc_camera_device *icd = soc_camera_from_vb2q(vq); struct soc_camera_host *ici = to_soc_camera_host(icd->parent); @@ -524,8 +524,6 @@ static int rcar_vin_stop_streaming(struct vb2_queue *vq) list_for_each_safe(buf_head, tmp, &priv->capture) list_del_init(buf_head); spin_unlock_irq(&priv->lock); - - return 0; } static struct vb2_ops rcar_vin_vb2_ops = { diff --git a/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c b/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c index 3e75a469cd49..20ad4a571d37 100644 --- a/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c +++ b/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c @@ -471,7 +471,7 @@ static int sh_mobile_ceu_videobuf_init(struct vb2_buffer *vb) return 0; } -static int sh_mobile_ceu_stop_streaming(struct vb2_queue *q) +static void sh_mobile_ceu_stop_streaming(struct vb2_queue *q) { struct soc_camera_device *icd = container_of(q, struct soc_camera_device, vb2_vidq); struct soc_camera_host *ici = to_soc_camera_host(icd->parent); @@ -487,7 +487,7 @@ static int sh_mobile_ceu_stop_streaming(struct vb2_queue *q) spin_unlock_irq(&pcdev->lock); - return sh_mobile_ceu_soft_reset(pcdev); + sh_mobile_ceu_soft_reset(pcdev); } static struct vb2_ops sh_mobile_ceu_videobuf_ops = { diff --git a/drivers/media/platform/soc_camera/soc_camera.c b/drivers/media/platform/soc_camera/soc_camera.c index 4b8c024fc487..7fec8cdaf095 100644 --- a/drivers/media/platform/soc_camera/soc_camera.c +++ b/drivers/media/platform/soc_camera/soc_camera.c @@ -314,7 +314,7 @@ static int soc_camera_s_std(struct file *file, void *priv, v4l2_std_id a) struct soc_camera_device *icd = file->private_data; struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - return v4l2_subdev_call(sd, core, s_std, a); + return v4l2_subdev_call(sd, video, s_std, a); } static int soc_camera_g_std(struct file *file, void *priv, v4l2_std_id *a) @@ -322,7 +322,7 @@ static int soc_camera_g_std(struct file *file, void *priv, v4l2_std_id *a) struct soc_camera_device *icd = file->private_data; struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - return v4l2_subdev_call(sd, core, g_std, a); + return v4l2_subdev_call(sd, video, g_std, a); } static int soc_camera_enum_framesizes(struct file *file, void *fh, @@ -1277,6 +1277,8 @@ static int soc_camera_probe_finish(struct soc_camera_device *icd) sd->grp_id = soc_camera_grp_id(icd); v4l2_set_subdev_hostdata(sd, icd); + v4l2_subdev_call(sd, video, g_tvnorms, &icd->vdev->tvnorms); + ret = v4l2_ctrl_add_handler(&icd->ctrl_handler, sd->ctrl_handler, NULL); if (ret < 0) return ret; @@ -1997,6 +1999,12 @@ static int soc_camera_video_start(struct soc_camera_device *icd) return -ENODEV; video_set_drvdata(icd->vdev, icd); + if (icd->vdev->tvnorms == 0) { + /* disable the STD API if there are no tvnorms defined */ + v4l2_disable_ioctl(icd->vdev, VIDIOC_G_STD); + v4l2_disable_ioctl(icd->vdev, VIDIOC_S_STD); + v4l2_disable_ioctl(icd->vdev, VIDIOC_ENUMSTD); + } ret = video_register_device(icd->vdev, VFL_TYPE_GRABBER, -1); if (ret < 0) { dev_err(icd->pdev, "video_register_device failed: %d\n", ret); diff --git a/drivers/media/platform/ti-vpe/csc.c b/drivers/media/platform/ti-vpe/csc.c index acfea500710e..940df4000c42 100644 --- a/drivers/media/platform/ti-vpe/csc.c +++ b/drivers/media/platform/ti-vpe/csc.c @@ -180,16 +180,16 @@ struct csc_data *csc_create(struct platform_device *pdev) csc->pdev = pdev; csc->res = platform_get_resource_byname(pdev, IORESOURCE_MEM, - "vpe_csc"); + "csc"); if (csc->res == NULL) { dev_err(&pdev->dev, "missing platform resources data\n"); return ERR_PTR(-ENODEV); } csc->base = devm_ioremap_resource(&pdev->dev, csc->res); - if (!csc->base) { + if (IS_ERR(csc->base)) { dev_err(&pdev->dev, "failed to ioremap\n"); - return ERR_PTR(-ENOMEM); + return csc->base; } return csc; diff --git a/drivers/media/platform/ti-vpe/sc.c b/drivers/media/platform/ti-vpe/sc.c index 93f0af546b76..6314171ffe9b 100644 --- a/drivers/media/platform/ti-vpe/sc.c +++ b/drivers/media/platform/ti-vpe/sc.c @@ -302,9 +302,9 @@ struct sc_data *sc_create(struct platform_device *pdev) } sc->base = devm_ioremap_resource(&pdev->dev, sc->res); - if (!sc->base) { + if (IS_ERR(sc->base)) { dev_err(&pdev->dev, "failed to ioremap\n"); - return ERR_PTR(-ENOMEM); + return sc->base; } return sc; diff --git a/drivers/media/platform/ti-vpe/vpdma.c b/drivers/media/platform/ti-vpe/vpdma.c index e8175e7938ed..a51a01359805 100644 --- a/drivers/media/platform/ti-vpe/vpdma.c +++ b/drivers/media/platform/ti-vpe/vpdma.c @@ -614,8 +614,17 @@ static void dump_dtd(struct vpdma_dtd *dtd) /* * append an outbound data transfer descriptor to the given descriptor list, * this sets up a 'client to memory' VPDMA transfer for the given VPDMA channel + * + * @list: vpdma desc list to which we add this decriptor + * @width: width of the image in pixels in memory + * @c_rect: compose params of output image + * @fmt: vpdma data format of the buffer + * dma_addr: dma address as seen by VPDMA + * chan: VPDMA channel + * flags: VPDMA flags to configure some descriptor fileds */ -void vpdma_add_out_dtd(struct vpdma_desc_list *list, struct v4l2_rect *c_rect, +void vpdma_add_out_dtd(struct vpdma_desc_list *list, int width, + const struct v4l2_rect *c_rect, const struct vpdma_data_format *fmt, dma_addr_t dma_addr, enum vpdma_channel chan, u32 flags) { @@ -623,6 +632,7 @@ void vpdma_add_out_dtd(struct vpdma_desc_list *list, struct v4l2_rect *c_rect, int field = 0; int notify = 1; int channel, next_chan; + struct v4l2_rect rect = *c_rect; int depth = fmt->depth; int stride; struct vpdma_dtd *dtd; @@ -630,11 +640,15 @@ void vpdma_add_out_dtd(struct vpdma_desc_list *list, struct v4l2_rect *c_rect, channel = next_chan = chan_info[chan].num; if (fmt->type == VPDMA_DATA_FMT_TYPE_YUV && - fmt->data_type == DATA_TYPE_C420) + fmt->data_type == DATA_TYPE_C420) { + rect.height >>= 1; + rect.top >>= 1; depth = 8; + } - stride = ALIGN((depth * c_rect->width) >> 3, VPDMA_STRIDE_ALIGN); - dma_addr += (c_rect->left * depth) >> 3; + stride = ALIGN((depth * width) >> 3, VPDMA_STRIDE_ALIGN); + + dma_addr += rect.top * stride + (rect.left * depth >> 3); dtd = list->next; WARN_ON((void *)(dtd + 1) > (list->buf.addr + list->buf.size)); @@ -664,31 +678,48 @@ void vpdma_add_out_dtd(struct vpdma_desc_list *list, struct v4l2_rect *c_rect, /* * append an inbound data transfer descriptor to the given descriptor list, * this sets up a 'memory to client' VPDMA transfer for the given VPDMA channel + * + * @list: vpdma desc list to which we add this decriptor + * @width: width of the image in pixels in memory(not the cropped width) + * @c_rect: crop params of input image + * @fmt: vpdma data format of the buffer + * dma_addr: dma address as seen by VPDMA + * chan: VPDMA channel + * field: top or bottom field info of the input image + * flags: VPDMA flags to configure some descriptor fileds + * frame_width/height: the complete width/height of the image presented to the + * client (this makes sense when multiple channels are + * connected to the same client, forming a larger frame) + * start_h, start_v: position where the given channel starts providing pixel + * data to the client (makes sense when multiple channels + * contribute to the client) */ -void vpdma_add_in_dtd(struct vpdma_desc_list *list, int frame_width, - int frame_height, struct v4l2_rect *c_rect, +void vpdma_add_in_dtd(struct vpdma_desc_list *list, int width, + const struct v4l2_rect *c_rect, const struct vpdma_data_format *fmt, dma_addr_t dma_addr, - enum vpdma_channel chan, int field, u32 flags) + enum vpdma_channel chan, int field, u32 flags, int frame_width, + int frame_height, int start_h, int start_v) { int priority = 0; int notify = 1; int depth = fmt->depth; int channel, next_chan; + struct v4l2_rect rect = *c_rect; int stride; - int height = c_rect->height; struct vpdma_dtd *dtd; channel = next_chan = chan_info[chan].num; if (fmt->type == VPDMA_DATA_FMT_TYPE_YUV && fmt->data_type == DATA_TYPE_C420) { - height >>= 1; - frame_height >>= 1; + rect.height >>= 1; + rect.top >>= 1; depth = 8; } - stride = ALIGN((depth * c_rect->width) >> 3, VPDMA_STRIDE_ALIGN); - dma_addr += (c_rect->left * depth) >> 3; + stride = ALIGN((depth * width) >> 3, VPDMA_STRIDE_ALIGN); + + dma_addr += rect.top * stride + (rect.left * depth >> 3); dtd = list->next; WARN_ON((void *)(dtd + 1) > (list->buf.addr + list->buf.size)); @@ -701,13 +732,14 @@ void vpdma_add_in_dtd(struct vpdma_desc_list *list, int frame_width, !!(flags & VPDMA_DATA_ODD_LINE_SKIP), stride); - dtd->xfer_length_height = dtd_xfer_length_height(c_rect->width, height); + dtd->xfer_length_height = dtd_xfer_length_height(rect.width, + rect.height); dtd->start_addr = (u32) dma_addr; dtd->pkt_ctl = dtd_pkt_ctl(!!(flags & VPDMA_DATA_MODE_TILED), DTD_DIR_IN, channel, priority, next_chan); dtd->frame_width_height = dtd_frame_width_height(frame_width, frame_height); - dtd->start_h_v = dtd_start_h_v(c_rect->left, c_rect->top); + dtd->start_h_v = dtd_start_h_v(start_h, start_v); dtd->client_attr0 = 0; dtd->client_attr1 = 0; @@ -781,7 +813,7 @@ static void vpdma_firmware_cb(const struct firmware *f, void *context) /* already initialized */ if (read_field_reg(vpdma, VPDMA_LIST_ATTR, VPDMA_LIST_RDY_MASK, VPDMA_LIST_RDY_SHFT)) { - vpdma->ready = true; + vpdma->cb(vpdma->pdev); return; } @@ -811,7 +843,7 @@ static void vpdma_firmware_cb(const struct firmware *f, void *context) goto free_buf; } - vpdma->ready = true; + vpdma->cb(vpdma->pdev); free_buf: vpdma_unmap_desc_buf(vpdma, &fw_dma_buf); @@ -839,7 +871,8 @@ static int vpdma_load_firmware(struct vpdma_data *vpdma) return 0; } -struct vpdma_data *vpdma_create(struct platform_device *pdev) +struct vpdma_data *vpdma_create(struct platform_device *pdev, + void (*cb)(struct platform_device *pdev)) { struct resource *res; struct vpdma_data *vpdma; @@ -854,6 +887,7 @@ struct vpdma_data *vpdma_create(struct platform_device *pdev) } vpdma->pdev = pdev; + vpdma->cb = cb; res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "vpdma"); if (res == NULL) { diff --git a/drivers/media/platform/ti-vpe/vpdma.h b/drivers/media/platform/ti-vpe/vpdma.h index cf40f11b3c8f..2bd8fb050381 100644 --- a/drivers/media/platform/ti-vpe/vpdma.h +++ b/drivers/media/platform/ti-vpe/vpdma.h @@ -35,8 +35,8 @@ struct vpdma_data { struct platform_device *pdev; - /* tells whether vpdma firmware is loaded or not */ - bool ready; + /* callback to VPE driver when the firmware is loaded */ + void (*cb)(struct platform_device *pdev); }; enum vpdma_data_format_type { @@ -186,13 +186,15 @@ void vpdma_add_cfd_adb(struct vpdma_desc_list *list, int client, struct vpdma_buf *adb); void vpdma_add_sync_on_channel_ctd(struct vpdma_desc_list *list, enum vpdma_channel chan); -void vpdma_add_out_dtd(struct vpdma_desc_list *list, struct v4l2_rect *c_rect, +void vpdma_add_out_dtd(struct vpdma_desc_list *list, int width, + const struct v4l2_rect *c_rect, const struct vpdma_data_format *fmt, dma_addr_t dma_addr, enum vpdma_channel chan, u32 flags); -void vpdma_add_in_dtd(struct vpdma_desc_list *list, int frame_width, - int frame_height, struct v4l2_rect *c_rect, +void vpdma_add_in_dtd(struct vpdma_desc_list *list, int width, + const struct v4l2_rect *c_rect, const struct vpdma_data_format *fmt, dma_addr_t dma_addr, - enum vpdma_channel chan, int field, u32 flags); + enum vpdma_channel chan, int field, u32 flags, int frame_width, + int frame_height, int start_h, int start_v); /* vpdma list interrupt management */ void vpdma_enable_list_complete_irq(struct vpdma_data *vpdma, int list_num, @@ -208,6 +210,7 @@ void vpdma_set_frame_start_event(struct vpdma_data *vpdma, void vpdma_dump_regs(struct vpdma_data *vpdma); /* initialize vpdma, passed with VPE's platform device pointer */ -struct vpdma_data *vpdma_create(struct platform_device *pdev); +struct vpdma_data *vpdma_create(struct platform_device *pdev, + void (*cb)(struct platform_device *pdev)); #endif diff --git a/drivers/media/platform/ti-vpe/vpe.c b/drivers/media/platform/ti-vpe/vpe.c index 5c421886d97c..972f43f69206 100644 --- a/drivers/media/platform/ti-vpe/vpe.c +++ b/drivers/media/platform/ti-vpe/vpe.c @@ -410,8 +410,10 @@ static struct vpe_q_data *get_q_data(struct vpe_ctx *ctx, { switch (type) { case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: + case V4L2_BUF_TYPE_VIDEO_OUTPUT: return &ctx->q_data[Q_DATA_SRC]; case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: + case V4L2_BUF_TYPE_VIDEO_CAPTURE: return &ctx->q_data[Q_DATA_DST]; default: BUG(); @@ -986,7 +988,6 @@ static void add_out_dtd(struct vpe_ctx *ctx, int port) struct vpe_q_data *q_data = &ctx->q_data[Q_DATA_DST]; const struct vpe_port_data *p_data = &port_data[port]; struct vb2_buffer *vb = ctx->dst_vb; - struct v4l2_rect *c_rect = &q_data->c_rect; struct vpe_fmt *fmt = q_data->fmt; const struct vpdma_data_format *vpdma_fmt; int mv_buf_selector = !ctx->src_mv_buf_selector; @@ -1015,8 +1016,8 @@ static void add_out_dtd(struct vpe_ctx *ctx, int port) if (q_data->flags & Q_DATA_MODE_TILED) flags |= VPDMA_DATA_MODE_TILED; - vpdma_add_out_dtd(&ctx->desc_list, c_rect, vpdma_fmt, dma_addr, - p_data->channel, flags); + vpdma_add_out_dtd(&ctx->desc_list, q_data->width, &q_data->c_rect, + vpdma_fmt, dma_addr, p_data->channel, flags); } static void add_in_dtd(struct vpe_ctx *ctx, int port) @@ -1024,11 +1025,11 @@ static void add_in_dtd(struct vpe_ctx *ctx, int port) struct vpe_q_data *q_data = &ctx->q_data[Q_DATA_SRC]; const struct vpe_port_data *p_data = &port_data[port]; struct vb2_buffer *vb = ctx->src_vbs[p_data->vb_index]; - struct v4l2_rect *c_rect = &q_data->c_rect; struct vpe_fmt *fmt = q_data->fmt; const struct vpdma_data_format *vpdma_fmt; int mv_buf_selector = ctx->src_mv_buf_selector; int field = vb->v4l2_buf.field == V4L2_FIELD_BOTTOM; + int frame_width, frame_height; dma_addr_t dma_addr; u32 flags = 0; @@ -1055,8 +1056,15 @@ static void add_in_dtd(struct vpe_ctx *ctx, int port) if (q_data->flags & Q_DATA_MODE_TILED) flags |= VPDMA_DATA_MODE_TILED; - vpdma_add_in_dtd(&ctx->desc_list, q_data->width, q_data->height, - c_rect, vpdma_fmt, dma_addr, p_data->channel, field, flags); + frame_width = q_data->c_rect.width; + frame_height = q_data->c_rect.height; + + if (p_data->vb_part && fmt->fourcc == V4L2_PIX_FMT_NV12) + frame_height /= 2; + + vpdma_add_in_dtd(&ctx->desc_list, q_data->width, &q_data->c_rect, + vpdma_fmt, dma_addr, p_data->channel, field, flags, frame_width, + frame_height, 0, 0); } /* @@ -1585,6 +1593,151 @@ static int vpe_s_fmt(struct file *file, void *priv, struct v4l2_format *f) return set_srcdst_params(ctx); } +static int __vpe_try_selection(struct vpe_ctx *ctx, struct v4l2_selection *s) +{ + struct vpe_q_data *q_data; + + if ((s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) && + (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)) + return -EINVAL; + + q_data = get_q_data(ctx, s->type); + if (!q_data) + return -EINVAL; + + switch (s->target) { + case V4L2_SEL_TGT_COMPOSE: + /* + * COMPOSE target is only valid for capture buffer type, return + * error for output buffer type + */ + if (s->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) + return -EINVAL; + break; + case V4L2_SEL_TGT_CROP: + /* + * CROP target is only valid for output buffer type, return + * error for capture buffer type + */ + if (s->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + break; + /* + * bound and default crop/compose targets are invalid targets to + * try/set + */ + default: + return -EINVAL; + } + + if (s->r.top < 0 || s->r.left < 0) { + vpe_err(ctx->dev, "negative values for top and left\n"); + s->r.top = s->r.left = 0; + } + + v4l_bound_align_image(&s->r.width, MIN_W, q_data->width, 1, + &s->r.height, MIN_H, q_data->height, H_ALIGN, S_ALIGN); + + /* adjust left/top if cropping rectangle is out of bounds */ + if (s->r.left + s->r.width > q_data->width) + s->r.left = q_data->width - s->r.width; + if (s->r.top + s->r.height > q_data->height) + s->r.top = q_data->height - s->r.height; + + return 0; +} + +static int vpe_g_selection(struct file *file, void *fh, + struct v4l2_selection *s) +{ + struct vpe_ctx *ctx = file2ctx(file); + struct vpe_q_data *q_data; + bool use_c_rect = false; + + if ((s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) && + (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)) + return -EINVAL; + + q_data = get_q_data(ctx, s->type); + if (!q_data) + return -EINVAL; + + switch (s->target) { + case V4L2_SEL_TGT_COMPOSE_DEFAULT: + case V4L2_SEL_TGT_COMPOSE_BOUNDS: + if (s->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) + return -EINVAL; + break; + case V4L2_SEL_TGT_CROP_BOUNDS: + case V4L2_SEL_TGT_CROP_DEFAULT: + if (s->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + break; + case V4L2_SEL_TGT_COMPOSE: + if (s->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) + return -EINVAL; + use_c_rect = true; + break; + case V4L2_SEL_TGT_CROP: + if (s->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + use_c_rect = true; + break; + default: + return -EINVAL; + } + + if (use_c_rect) { + /* + * for CROP/COMPOSE target type, return c_rect params from the + * respective buffer type + */ + s->r = q_data->c_rect; + } else { + /* + * for DEFAULT/BOUNDS target type, return width and height from + * S_FMT of the respective buffer type + */ + s->r.left = 0; + s->r.top = 0; + s->r.width = q_data->width; + s->r.height = q_data->height; + } + + return 0; +} + + +static int vpe_s_selection(struct file *file, void *fh, + struct v4l2_selection *s) +{ + struct vpe_ctx *ctx = file2ctx(file); + struct vpe_q_data *q_data; + struct v4l2_selection sel = *s; + int ret; + + ret = __vpe_try_selection(ctx, &sel); + if (ret) + return ret; + + q_data = get_q_data(ctx, sel.type); + if (!q_data) + return -EINVAL; + + if ((q_data->c_rect.left == sel.r.left) && + (q_data->c_rect.top == sel.r.top) && + (q_data->c_rect.width == sel.r.width) && + (q_data->c_rect.height == sel.r.height)) { + vpe_dbg(ctx->dev, + "requested crop/compose values are already set\n"); + return 0; + } + + q_data->c_rect = sel.r; + + return set_srcdst_params(ctx); +} + static int vpe_reqbufs(struct file *file, void *priv, struct v4l2_requestbuffers *reqbufs) { @@ -1672,6 +1825,9 @@ static const struct v4l2_ioctl_ops vpe_ioctl_ops = { .vidioc_try_fmt_vid_out_mplane = vpe_try_fmt, .vidioc_s_fmt_vid_out_mplane = vpe_s_fmt, + .vidioc_g_selection = vpe_g_selection, + .vidioc_s_selection = vpe_s_selection, + .vidioc_reqbufs = vpe_reqbufs, .vidioc_querybuf = vpe_querybuf, @@ -1784,7 +1940,7 @@ static int queue_init(void *priv, struct vb2_queue *src_vq, memset(src_vq, 0, sizeof(*src_vq)); src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; - src_vq->io_modes = VB2_MMAP; + src_vq->io_modes = VB2_MMAP | VB2_DMABUF; src_vq->drv_priv = ctx; src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); src_vq->ops = &vpe_qops; @@ -1797,7 +1953,7 @@ static int queue_init(void *priv, struct vb2_queue *src_vq, memset(dst_vq, 0, sizeof(*dst_vq)); dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; - dst_vq->io_modes = VB2_MMAP; + dst_vq->io_modes = VB2_MMAP | VB2_DMABUF; dst_vq->drv_priv = ctx; dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); dst_vq->ops = &vpe_qops; @@ -1831,11 +1987,6 @@ static int vpe_open(struct file *file) vpe_dbg(dev, "vpe_open\n"); - if (!dev->vpdma->ready) { - vpe_err(dev, "vpdma firmware not loaded\n"); - return -ENODEV; - } - ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); if (!ctx) return -ENOMEM; @@ -2055,10 +2206,40 @@ static void vpe_runtime_put(struct platform_device *pdev) WARN_ON(r < 0 && r != -ENOSYS); } +static void vpe_fw_cb(struct platform_device *pdev) +{ + struct vpe_dev *dev = platform_get_drvdata(pdev); + struct video_device *vfd; + int ret; + + vfd = &dev->vfd; + *vfd = vpe_videodev; + vfd->lock = &dev->dev_mutex; + vfd->v4l2_dev = &dev->v4l2_dev; + + ret = video_register_device(vfd, VFL_TYPE_GRABBER, 0); + if (ret) { + vpe_err(dev, "Failed to register video device\n"); + + vpe_set_clock_enable(dev, 0); + vpe_runtime_put(pdev); + pm_runtime_disable(&pdev->dev); + v4l2_m2m_release(dev->m2m_dev); + vb2_dma_contig_cleanup_ctx(dev->alloc_ctx); + v4l2_device_unregister(&dev->v4l2_dev); + + return; + } + + video_set_drvdata(vfd, dev); + snprintf(vfd->name, sizeof(vfd->name), "%s", vpe_videodev.name); + dev_info(dev->v4l2_dev.dev, "Device registered as /dev/video%d\n", + vfd->num); +} + static int vpe_probe(struct platform_device *pdev) { struct vpe_dev *dev; - struct video_device *vfd; int ret, irq, func; dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL); @@ -2139,28 +2320,12 @@ static int vpe_probe(struct platform_device *pdev) goto runtime_put; } - dev->vpdma = vpdma_create(pdev); + dev->vpdma = vpdma_create(pdev, vpe_fw_cb); if (IS_ERR(dev->vpdma)) { ret = PTR_ERR(dev->vpdma); goto runtime_put; } - vfd = &dev->vfd; - *vfd = vpe_videodev; - vfd->lock = &dev->dev_mutex; - vfd->v4l2_dev = &dev->v4l2_dev; - - ret = video_register_device(vfd, VFL_TYPE_GRABBER, 0); - if (ret) { - vpe_err(dev, "Failed to register video device\n"); - goto runtime_put; - } - - video_set_drvdata(vfd, dev); - snprintf(vfd->name, sizeof(vfd->name), "%s", vpe_videodev.name); - dev_info(dev->v4l2_dev.dev, "Device registered as /dev/video%d\n", - vfd->num); - return 0; runtime_put: diff --git a/drivers/media/platform/timblogiw.c b/drivers/media/platform/timblogiw.c index ccdadd623a3a..3cb2f3564873 100644 --- a/drivers/media/platform/timblogiw.c +++ b/drivers/media/platform/timblogiw.c @@ -347,7 +347,7 @@ static int timblogiw_s_std(struct file *file, void *priv, v4l2_std_id std) mutex_lock(&lw->lock); if (TIMBLOGIW_HAS_DECODER(lw)) - err = v4l2_subdev_call(lw->sd_enc, core, s_std, std); + err = v4l2_subdev_call(lw->sd_enc, video, s_std, std); if (!err) fh->cur_norm = timblogiw_get_norm(std); @@ -800,7 +800,7 @@ static int timblogiw_probe(struct platform_device *pdev) if (!pdata->encoder.module_name) dev_info(&pdev->dev, "Running without decoder\n"); - lw = kzalloc(sizeof(*lw), GFP_KERNEL); + lw = devm_kzalloc(&pdev->dev, sizeof(*lw), GFP_KERNEL); if (!lw) { err = -ENOMEM; goto err; @@ -820,7 +820,7 @@ static int timblogiw_probe(struct platform_device *pdev) strlcpy(lw->v4l2_dev.name, DRIVER_NAME, sizeof(lw->v4l2_dev.name)); err = v4l2_device_register(NULL, &lw->v4l2_dev); if (err) - goto err_register; + goto err; lw->video_dev.v4l2_dev = &lw->v4l2_dev; @@ -837,8 +837,6 @@ static int timblogiw_probe(struct platform_device *pdev) err_request: v4l2_device_unregister(&lw->v4l2_dev); -err_register: - kfree(lw); err: dev_err(&pdev->dev, "Failed to register: %d\n", err); @@ -853,8 +851,6 @@ static int timblogiw_remove(struct platform_device *pdev) v4l2_device_unregister(&lw->v4l2_dev); - kfree(lw); - return 0; } diff --git a/drivers/media/platform/vino.c b/drivers/media/platform/vino.c index c6af974c5b45..470d35336119 100644 --- a/drivers/media/platform/vino.c +++ b/drivers/media/platform/vino.c @@ -2586,7 +2586,7 @@ static int vino_acquire_input(struct vino_channel_settings *vcs) } if (data_norm == 3) data_norm = VINO_DATA_NORM_PAL; - ret = decoder_call(core, s_std, norm); + ret = decoder_call(video, s_std, norm); } spin_lock_irqsave(&vino_drvdata->input_lock, flags); @@ -2675,7 +2675,7 @@ static int vino_set_input(struct vino_channel_settings *vcs, int input) } if (data_norm == 3) data_norm = VINO_DATA_NORM_PAL; - ret = decoder_call(core, s_std, norm); + ret = decoder_call(video, s_std, norm); } spin_lock_irqsave(&vino_drvdata->input_lock, flags); @@ -2809,7 +2809,7 @@ static int vino_set_data_norm(struct vino_channel_settings *vcs, * as it may take a while... */ norm = vino_data_norms[data_norm].std; - err = decoder_call(core, s_std, norm); + err = decoder_call(video, s_std, norm); spin_lock_irqsave(&vino_drvdata->input_lock, *flags); diff --git a/drivers/media/platform/vivi.c b/drivers/media/platform/vivi.c index 3890f4f42a78..d00bf3df0f8a 100644 --- a/drivers/media/platform/vivi.c +++ b/drivers/media/platform/vivi.c @@ -906,12 +906,11 @@ static int start_streaming(struct vb2_queue *vq, unsigned int count) } /* abort streaming and wait for last buffer */ -static int stop_streaming(struct vb2_queue *vq) +static void stop_streaming(struct vb2_queue *vq) { struct vivi_dev *dev = vb2_get_drv_priv(vq); dprintk(dev, 1, "%s\n", __func__); vivi_stop_generating(dev); - return 0; } static void vivi_lock(struct vb2_queue *vq) diff --git a/drivers/media/platform/vsp1/Makefile b/drivers/media/platform/vsp1/Makefile index 151cecd0ea25..6a93f928dfde 100644 --- a/drivers/media/platform/vsp1/Makefile +++ b/drivers/media/platform/vsp1/Makefile @@ -1,6 +1,6 @@ vsp1-y := vsp1_drv.o vsp1_entity.o vsp1_video.o vsp1-y += vsp1_rpf.o vsp1_rwpf.o vsp1_wpf.o vsp1-y += vsp1_hsit.o vsp1_lif.o vsp1_lut.o -vsp1-y += vsp1_sru.o vsp1_uds.o +vsp1-y += vsp1_bru.o vsp1_sru.o vsp1_uds.o obj-$(CONFIG_VIDEO_RENESAS_VSP1) += vsp1.o diff --git a/drivers/media/platform/vsp1/vsp1.h b/drivers/media/platform/vsp1/vsp1.h index 0313210c6e9e..6ca2cf20d545 100644 --- a/drivers/media/platform/vsp1/vsp1.h +++ b/drivers/media/platform/vsp1/vsp1.h @@ -28,6 +28,7 @@ struct clk; struct device; struct vsp1_platform_data; +struct vsp1_bru; struct vsp1_hsit; struct vsp1_lif; struct vsp1_lut; @@ -45,11 +46,11 @@ struct vsp1_device { void __iomem *mmio; struct clk *clock; - struct clk *rt_clock; struct mutex lock; int ref_count; + struct vsp1_bru *bru; struct vsp1_hsit *hsi; struct vsp1_hsit *hst; struct vsp1_lif *lif; diff --git a/drivers/media/platform/vsp1/vsp1_bru.c b/drivers/media/platform/vsp1/vsp1_bru.c new file mode 100644 index 000000000000..f80695480060 --- /dev/null +++ b/drivers/media/platform/vsp1/vsp1_bru.c @@ -0,0 +1,395 @@ +/* + * vsp1_bru.c -- R-Car VSP1 Blend ROP Unit + * + * Copyright (C) 2013 Renesas Corporation + * + * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include <linux/device.h> +#include <linux/gfp.h> + +#include <media/v4l2-subdev.h> + +#include "vsp1.h" +#include "vsp1_bru.h" + +#define BRU_MIN_SIZE 4U +#define BRU_MAX_SIZE 8190U + +/* ----------------------------------------------------------------------------- + * Device Access + */ + +static inline u32 vsp1_bru_read(struct vsp1_bru *bru, u32 reg) +{ + return vsp1_read(bru->entity.vsp1, reg); +} + +static inline void vsp1_bru_write(struct vsp1_bru *bru, u32 reg, u32 data) +{ + vsp1_write(bru->entity.vsp1, reg, data); +} + +/* ----------------------------------------------------------------------------- + * V4L2 Subdevice Core Operations + */ + +static bool bru_is_input_enabled(struct vsp1_bru *bru, unsigned int input) +{ + return media_entity_remote_pad(&bru->entity.pads[input]) != NULL; +} + +static int bru_s_stream(struct v4l2_subdev *subdev, int enable) +{ + struct vsp1_bru *bru = to_bru(subdev); + struct v4l2_mbus_framefmt *format; + unsigned int i; + + if (!enable) + return 0; + + format = &bru->entity.formats[BRU_PAD_SOURCE]; + + /* The hardware is extremely flexible but we have no userspace API to + * expose all the parameters, nor is it clear whether we would have use + * cases for all the supported modes. Let's just harcode the parameters + * to sane default values for now. + */ + + /* Disable both color data normalization and dithering. */ + vsp1_bru_write(bru, VI6_BRU_INCTRL, 0); + + /* Set the background position to cover the whole output image and + * set its color to opaque black. + */ + vsp1_bru_write(bru, VI6_BRU_VIRRPF_SIZE, + (format->width << VI6_BRU_VIRRPF_SIZE_HSIZE_SHIFT) | + (format->height << VI6_BRU_VIRRPF_SIZE_VSIZE_SHIFT)); + vsp1_bru_write(bru, VI6_BRU_VIRRPF_LOC, 0); + vsp1_bru_write(bru, VI6_BRU_VIRRPF_COL, + 0xff << VI6_BRU_VIRRPF_COL_A_SHIFT); + + /* Route BRU input 1 as SRC input to the ROP unit and configure the ROP + * unit with a NOP operation to make BRU input 1 available as the + * Blend/ROP unit B SRC input. + */ + vsp1_bru_write(bru, VI6_BRU_ROP, VI6_BRU_ROP_DSTSEL_BRUIN(1) | + VI6_BRU_ROP_CROP(VI6_ROP_NOP) | + VI6_BRU_ROP_AROP(VI6_ROP_NOP)); + + for (i = 0; i < 4; ++i) { + u32 ctrl = 0; + + /* Configure all Blend/ROP units corresponding to an enabled BRU + * input for alpha blending. Blend/ROP units corresponding to + * disabled BRU inputs are used in ROP NOP mode to ignore the + * SRC input. + */ + if (bru_is_input_enabled(bru, i)) + ctrl |= VI6_BRU_CTRL_RBC; + else + ctrl |= VI6_BRU_CTRL_CROP(VI6_ROP_NOP) + | VI6_BRU_CTRL_AROP(VI6_ROP_NOP); + + /* Select the virtual RPF as the Blend/ROP unit A DST input to + * serve as a background color. + */ + if (i == 0) + ctrl |= VI6_BRU_CTRL_DSTSEL_VRPF; + + /* Route BRU inputs 0 to 3 as SRC inputs to Blend/ROP units A to + * D in that order. The Blend/ROP unit B SRC is hardwired to the + * ROP unit output, the corresponding register bits must be set + * to 0. + */ + if (i != 1) + ctrl |= VI6_BRU_CTRL_SRCSEL_BRUIN(i); + + vsp1_bru_write(bru, VI6_BRU_CTRL(i), ctrl); + + /* Harcode the blending formula to + * + * DSTc = DSTc * (1 - SRCa) + SRCc * SRCa + * DSTa = DSTa * (1 - SRCa) + SRCa + */ + vsp1_bru_write(bru, VI6_BRU_BLD(i), + VI6_BRU_BLD_CCMDX_255_SRC_A | + VI6_BRU_BLD_CCMDY_SRC_A | + VI6_BRU_BLD_ACMDX_255_SRC_A | + VI6_BRU_BLD_ACMDY_COEFY | + (0xff << VI6_BRU_BLD_COEFY_SHIFT)); + } + + return 0; +} + +/* ----------------------------------------------------------------------------- + * V4L2 Subdevice Pad Operations + */ + +/* + * The BRU can't perform format conversion, all sink and source formats must be + * identical. We pick the format on the first sink pad (pad 0) and propagate it + * to all other pads. + */ + +static int bru_enum_mbus_code(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_mbus_code_enum *code) +{ + static const unsigned int codes[] = { + V4L2_MBUS_FMT_ARGB8888_1X32, + V4L2_MBUS_FMT_AYUV8_1X32, + }; + struct v4l2_mbus_framefmt *format; + + if (code->pad == BRU_PAD_SINK(0)) { + if (code->index >= ARRAY_SIZE(codes)) + return -EINVAL; + + code->code = codes[code->index]; + } else { + if (code->index) + return -EINVAL; + + format = v4l2_subdev_get_try_format(fh, BRU_PAD_SINK(0)); + code->code = format->code; + } + + return 0; +} + +static int bru_enum_frame_size(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_frame_size_enum *fse) +{ + if (fse->index) + return -EINVAL; + + if (fse->code != V4L2_MBUS_FMT_ARGB8888_1X32 && + fse->code != V4L2_MBUS_FMT_AYUV8_1X32) + return -EINVAL; + + fse->min_width = BRU_MIN_SIZE; + fse->max_width = BRU_MAX_SIZE; + fse->min_height = BRU_MIN_SIZE; + fse->max_height = BRU_MAX_SIZE; + + return 0; +} + +static struct v4l2_rect *bru_get_compose(struct vsp1_bru *bru, + struct v4l2_subdev_fh *fh, + unsigned int pad, u32 which) +{ + switch (which) { + case V4L2_SUBDEV_FORMAT_TRY: + return v4l2_subdev_get_try_crop(fh, pad); + case V4L2_SUBDEV_FORMAT_ACTIVE: + return &bru->compose[pad]; + default: + return NULL; + } +} + +static int bru_get_format(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + struct vsp1_bru *bru = to_bru(subdev); + + fmt->format = *vsp1_entity_get_pad_format(&bru->entity, fh, fmt->pad, + fmt->which); + + return 0; +} + +static void bru_try_format(struct vsp1_bru *bru, struct v4l2_subdev_fh *fh, + unsigned int pad, struct v4l2_mbus_framefmt *fmt, + enum v4l2_subdev_format_whence which) +{ + struct v4l2_mbus_framefmt *format; + + switch (pad) { + case BRU_PAD_SINK(0): + /* Default to YUV if the requested format is not supported. */ + if (fmt->code != V4L2_MBUS_FMT_ARGB8888_1X32 && + fmt->code != V4L2_MBUS_FMT_AYUV8_1X32) + fmt->code = V4L2_MBUS_FMT_AYUV8_1X32; + break; + + default: + /* The BRU can't perform format conversion. */ + format = vsp1_entity_get_pad_format(&bru->entity, fh, + BRU_PAD_SINK(0), which); + fmt->code = format->code; + break; + } + + fmt->width = clamp(fmt->width, BRU_MIN_SIZE, BRU_MAX_SIZE); + fmt->height = clamp(fmt->height, BRU_MIN_SIZE, BRU_MAX_SIZE); + fmt->field = V4L2_FIELD_NONE; + fmt->colorspace = V4L2_COLORSPACE_SRGB; +} + +static int bru_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + struct vsp1_bru *bru = to_bru(subdev); + struct v4l2_mbus_framefmt *format; + + bru_try_format(bru, fh, fmt->pad, &fmt->format, fmt->which); + + format = vsp1_entity_get_pad_format(&bru->entity, fh, fmt->pad, + fmt->which); + *format = fmt->format; + + /* Reset the compose rectangle */ + if (fmt->pad != BRU_PAD_SOURCE) { + struct v4l2_rect *compose; + + compose = bru_get_compose(bru, fh, fmt->pad, fmt->which); + compose->left = 0; + compose->top = 0; + compose->width = format->width; + compose->height = format->height; + } + + /* Propagate the format code to all pads */ + if (fmt->pad == BRU_PAD_SINK(0)) { + unsigned int i; + + for (i = 0; i <= BRU_PAD_SOURCE; ++i) { + format = vsp1_entity_get_pad_format(&bru->entity, fh, + i, fmt->which); + format->code = fmt->format.code; + } + } + + return 0; +} + +static int bru_get_selection(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_selection *sel) +{ + struct vsp1_bru *bru = to_bru(subdev); + + if (sel->pad == BRU_PAD_SOURCE) + return -EINVAL; + + switch (sel->target) { + case V4L2_SEL_TGT_COMPOSE_BOUNDS: + sel->r.left = 0; + sel->r.top = 0; + sel->r.width = BRU_MAX_SIZE; + sel->r.height = BRU_MAX_SIZE; + return 0; + + case V4L2_SEL_TGT_COMPOSE: + sel->r = *bru_get_compose(bru, fh, sel->pad, sel->which); + return 0; + + default: + return -EINVAL; + } +} + +static int bru_set_selection(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_selection *sel) +{ + struct vsp1_bru *bru = to_bru(subdev); + struct v4l2_mbus_framefmt *format; + struct v4l2_rect *compose; + + if (sel->pad == BRU_PAD_SOURCE) + return -EINVAL; + + if (sel->target != V4L2_SEL_TGT_COMPOSE) + return -EINVAL; + + /* The compose rectangle top left corner must be inside the output + * frame. + */ + format = vsp1_entity_get_pad_format(&bru->entity, fh, BRU_PAD_SOURCE, + sel->which); + sel->r.left = clamp_t(unsigned int, sel->r.left, 0, format->width - 1); + sel->r.top = clamp_t(unsigned int, sel->r.top, 0, format->height - 1); + + /* Scaling isn't supported, the compose rectangle size must be identical + * to the sink format size. + */ + format = vsp1_entity_get_pad_format(&bru->entity, fh, sel->pad, + sel->which); + sel->r.width = format->width; + sel->r.height = format->height; + + compose = bru_get_compose(bru, fh, sel->pad, sel->which); + *compose = sel->r; + + return 0; +} + +/* ----------------------------------------------------------------------------- + * V4L2 Subdevice Operations + */ + +static struct v4l2_subdev_video_ops bru_video_ops = { + .s_stream = bru_s_stream, +}; + +static struct v4l2_subdev_pad_ops bru_pad_ops = { + .enum_mbus_code = bru_enum_mbus_code, + .enum_frame_size = bru_enum_frame_size, + .get_fmt = bru_get_format, + .set_fmt = bru_set_format, + .get_selection = bru_get_selection, + .set_selection = bru_set_selection, +}; + +static struct v4l2_subdev_ops bru_ops = { + .video = &bru_video_ops, + .pad = &bru_pad_ops, +}; + +/* ----------------------------------------------------------------------------- + * Initialization and Cleanup + */ + +struct vsp1_bru *vsp1_bru_create(struct vsp1_device *vsp1) +{ + struct v4l2_subdev *subdev; + struct vsp1_bru *bru; + int ret; + + bru = devm_kzalloc(vsp1->dev, sizeof(*bru), GFP_KERNEL); + if (bru == NULL) + return ERR_PTR(-ENOMEM); + + bru->entity.type = VSP1_ENTITY_BRU; + + ret = vsp1_entity_init(vsp1, &bru->entity, 5); + if (ret < 0) + return ERR_PTR(ret); + + /* Initialize the V4L2 subdev. */ + subdev = &bru->entity.subdev; + v4l2_subdev_init(subdev, &bru_ops); + + subdev->entity.ops = &vsp1_media_ops; + subdev->internal_ops = &vsp1_subdev_internal_ops; + snprintf(subdev->name, sizeof(subdev->name), "%s bru", + dev_name(vsp1->dev)); + v4l2_set_subdevdata(subdev, bru); + subdev->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + + vsp1_entity_init_formats(subdev, NULL); + + return bru; +} diff --git a/drivers/media/platform/vsp1/vsp1_bru.h b/drivers/media/platform/vsp1/vsp1_bru.h new file mode 100644 index 000000000000..37062704dbf6 --- /dev/null +++ b/drivers/media/platform/vsp1/vsp1_bru.h @@ -0,0 +1,39 @@ +/* + * vsp1_bru.h -- R-Car VSP1 Blend ROP Unit + * + * Copyright (C) 2013 Renesas Corporation + * + * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ +#ifndef __VSP1_BRU_H__ +#define __VSP1_BRU_H__ + +#include <media/media-entity.h> +#include <media/v4l2-subdev.h> + +#include "vsp1_entity.h" + +struct vsp1_device; + +#define BRU_PAD_SINK(n) (n) +#define BRU_PAD_SOURCE 4 + +struct vsp1_bru { + struct vsp1_entity entity; + + struct v4l2_rect compose[4]; +}; + +static inline struct vsp1_bru *to_bru(struct v4l2_subdev *subdev) +{ + return container_of(subdev, struct vsp1_bru, entity.subdev); +} + +struct vsp1_bru *vsp1_bru_create(struct vsp1_device *vsp1); + +#endif /* __VSP1_BRU_H__ */ diff --git a/drivers/media/platform/vsp1/vsp1_drv.c b/drivers/media/platform/vsp1/vsp1_drv.c index 2f74f0e0ddf5..c69ee0657f75 100644 --- a/drivers/media/platform/vsp1/vsp1_drv.c +++ b/drivers/media/platform/vsp1/vsp1_drv.c @@ -16,10 +16,12 @@ #include <linux/device.h> #include <linux/interrupt.h> #include <linux/module.h> +#include <linux/of.h> #include <linux/platform_device.h> #include <linux/videodev2.h> #include "vsp1.h" +#include "vsp1_bru.h" #include "vsp1_hsit.h" #include "vsp1_lif.h" #include "vsp1_lut.h" @@ -155,6 +157,14 @@ static int vsp1_create_entities(struct vsp1_device *vsp1) } /* Instantiate all the entities. */ + vsp1->bru = vsp1_bru_create(vsp1); + if (IS_ERR(vsp1->bru)) { + ret = PTR_ERR(vsp1->bru); + goto done; + } + + list_add_tail(&vsp1->bru->entity.list_dev, &vsp1->entities); + vsp1->hsi = vsp1_hsit_create(vsp1, true); if (IS_ERR(vsp1->hsi)) { ret = PTR_ERR(vsp1->hsi); @@ -329,33 +339,6 @@ static int vsp1_device_init(struct vsp1_device *vsp1) return 0; } -static int vsp1_clocks_enable(struct vsp1_device *vsp1) -{ - int ret; - - ret = clk_prepare_enable(vsp1->clock); - if (ret < 0) - return ret; - - if (IS_ERR(vsp1->rt_clock)) - return 0; - - ret = clk_prepare_enable(vsp1->rt_clock); - if (ret < 0) { - clk_disable_unprepare(vsp1->clock); - return ret; - } - - return 0; -} - -static void vsp1_clocks_disable(struct vsp1_device *vsp1) -{ - if (!IS_ERR(vsp1->rt_clock)) - clk_disable_unprepare(vsp1->rt_clock); - clk_disable_unprepare(vsp1->clock); -} - /* * vsp1_device_get - Acquire the VSP1 device * @@ -373,7 +356,7 @@ struct vsp1_device *vsp1_device_get(struct vsp1_device *vsp1) if (vsp1->ref_count > 0) goto done; - ret = vsp1_clocks_enable(vsp1); + ret = clk_prepare_enable(vsp1->clock); if (ret < 0) { __vsp1 = NULL; goto done; @@ -381,7 +364,7 @@ struct vsp1_device *vsp1_device_get(struct vsp1_device *vsp1) ret = vsp1_device_init(vsp1); if (ret < 0) { - vsp1_clocks_disable(vsp1); + clk_disable_unprepare(vsp1->clock); __vsp1 = NULL; goto done; } @@ -405,7 +388,7 @@ void vsp1_device_put(struct vsp1_device *vsp1) mutex_lock(&vsp1->lock); if (--vsp1->ref_count == 0) - vsp1_clocks_disable(vsp1); + clk_disable_unprepare(vsp1->clock); mutex_unlock(&vsp1->lock); } @@ -424,7 +407,7 @@ static int vsp1_pm_suspend(struct device *dev) if (vsp1->ref_count == 0) return 0; - vsp1_clocks_disable(vsp1); + clk_disable_unprepare(vsp1->clock); return 0; } @@ -437,7 +420,7 @@ static int vsp1_pm_resume(struct device *dev) if (vsp1->ref_count) return 0; - return vsp1_clocks_enable(vsp1); + return clk_prepare_enable(vsp1->clock); } #endif @@ -449,34 +432,59 @@ static const struct dev_pm_ops vsp1_pm_ops = { * Platform Driver */ -static struct vsp1_platform_data * -vsp1_get_platform_data(struct platform_device *pdev) +static int vsp1_validate_platform_data(struct platform_device *pdev, + struct vsp1_platform_data *pdata) { - struct vsp1_platform_data *pdata = pdev->dev.platform_data; - if (pdata == NULL) { dev_err(&pdev->dev, "missing platform data\n"); - return NULL; + return -EINVAL; } if (pdata->rpf_count <= 0 || pdata->rpf_count > VPS1_MAX_RPF) { dev_err(&pdev->dev, "invalid number of RPF (%u)\n", pdata->rpf_count); - return NULL; + return -EINVAL; } if (pdata->uds_count <= 0 || pdata->uds_count > VPS1_MAX_UDS) { dev_err(&pdev->dev, "invalid number of UDS (%u)\n", pdata->uds_count); - return NULL; + return -EINVAL; } if (pdata->wpf_count <= 0 || pdata->wpf_count > VPS1_MAX_WPF) { dev_err(&pdev->dev, "invalid number of WPF (%u)\n", pdata->wpf_count); - return NULL; + return -EINVAL; } + return 0; +} + +static struct vsp1_platform_data * +vsp1_get_platform_data(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct vsp1_platform_data *pdata; + + if (!IS_ENABLED(CONFIG_OF) || np == NULL) + return pdev->dev.platform_data; + + pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); + if (pdata == NULL) + return NULL; + + if (of_property_read_bool(np, "renesas,has-lif")) + pdata->features |= VSP1_HAS_LIF; + if (of_property_read_bool(np, "renesas,has-lut")) + pdata->features |= VSP1_HAS_LUT; + if (of_property_read_bool(np, "renesas,has-sru")) + pdata->features |= VSP1_HAS_SRU; + + of_property_read_u32(np, "renesas,#rpf", &pdata->rpf_count); + of_property_read_u32(np, "renesas,#uds", &pdata->uds_count); + of_property_read_u32(np, "renesas,#wpf", &pdata->wpf_count); + return pdata; } @@ -499,6 +507,10 @@ static int vsp1_probe(struct platform_device *pdev) if (vsp1->pdata == NULL) return -ENODEV; + ret = vsp1_validate_platform_data(pdev, vsp1->pdata); + if (ret < 0) + return ret; + /* I/O, IRQ and clock resources */ io = platform_get_resource(pdev, IORESOURCE_MEM, 0); vsp1->mmio = devm_ioremap_resource(&pdev->dev, io); @@ -511,9 +523,6 @@ static int vsp1_probe(struct platform_device *pdev) return PTR_ERR(vsp1->clock); } - /* The RT clock is optional */ - vsp1->rt_clock = devm_clk_get(&pdev->dev, "rt"); - irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); if (!irq) { dev_err(&pdev->dev, "missing IRQ\n"); @@ -548,6 +557,11 @@ static int vsp1_remove(struct platform_device *pdev) return 0; } +static const struct of_device_id vsp1_of_match[] = { + { .compatible = "renesas,vsp1" }, + { }, +}; + static struct platform_driver vsp1_platform_driver = { .probe = vsp1_probe, .remove = vsp1_remove, @@ -555,6 +569,7 @@ static struct platform_driver vsp1_platform_driver = { .owner = THIS_MODULE, .name = "vsp1", .pm = &vsp1_pm_ops, + .of_match_table = vsp1_of_match, }, }; diff --git a/drivers/media/platform/vsp1/vsp1_entity.c b/drivers/media/platform/vsp1/vsp1_entity.c index 3fc9e4266caf..44167834285d 100644 --- a/drivers/media/platform/vsp1/vsp1_entity.c +++ b/drivers/media/platform/vsp1/vsp1_entity.c @@ -100,8 +100,10 @@ static int vsp1_entity_link_setup(struct media_entity *entity, if (source->sink) return -EBUSY; source->sink = remote->entity; + source->sink_pad = remote->index; } else { source->sink = NULL; + source->sink_pad = 0; } return 0; @@ -116,42 +118,43 @@ const struct media_entity_operations vsp1_media_ops = { * Initialization */ +static const struct vsp1_route vsp1_routes[] = { + { VSP1_ENTITY_BRU, 0, VI6_DPR_BRU_ROUTE, + { VI6_DPR_NODE_BRU_IN(0), VI6_DPR_NODE_BRU_IN(1), + VI6_DPR_NODE_BRU_IN(2), VI6_DPR_NODE_BRU_IN(3), } }, + { VSP1_ENTITY_HSI, 0, VI6_DPR_HSI_ROUTE, { VI6_DPR_NODE_HSI, } }, + { VSP1_ENTITY_HST, 0, VI6_DPR_HST_ROUTE, { VI6_DPR_NODE_HST, } }, + { VSP1_ENTITY_LIF, 0, 0, { VI6_DPR_NODE_LIF, } }, + { VSP1_ENTITY_LUT, 0, VI6_DPR_LUT_ROUTE, { VI6_DPR_NODE_LUT, } }, + { VSP1_ENTITY_RPF, 0, VI6_DPR_RPF_ROUTE(0), { VI6_DPR_NODE_RPF(0), } }, + { VSP1_ENTITY_RPF, 1, VI6_DPR_RPF_ROUTE(1), { VI6_DPR_NODE_RPF(1), } }, + { VSP1_ENTITY_RPF, 2, VI6_DPR_RPF_ROUTE(2), { VI6_DPR_NODE_RPF(2), } }, + { VSP1_ENTITY_RPF, 3, VI6_DPR_RPF_ROUTE(3), { VI6_DPR_NODE_RPF(3), } }, + { VSP1_ENTITY_RPF, 4, VI6_DPR_RPF_ROUTE(4), { VI6_DPR_NODE_RPF(4), } }, + { VSP1_ENTITY_SRU, 0, VI6_DPR_SRU_ROUTE, { VI6_DPR_NODE_SRU, } }, + { VSP1_ENTITY_UDS, 0, VI6_DPR_UDS_ROUTE(0), { VI6_DPR_NODE_UDS(0), } }, + { VSP1_ENTITY_UDS, 1, VI6_DPR_UDS_ROUTE(1), { VI6_DPR_NODE_UDS(1), } }, + { VSP1_ENTITY_UDS, 2, VI6_DPR_UDS_ROUTE(2), { VI6_DPR_NODE_UDS(2), } }, + { VSP1_ENTITY_WPF, 0, 0, { VI6_DPR_NODE_WPF(0), } }, + { VSP1_ENTITY_WPF, 1, 0, { VI6_DPR_NODE_WPF(1), } }, + { VSP1_ENTITY_WPF, 2, 0, { VI6_DPR_NODE_WPF(2), } }, + { VSP1_ENTITY_WPF, 3, 0, { VI6_DPR_NODE_WPF(3), } }, +}; + int vsp1_entity_init(struct vsp1_device *vsp1, struct vsp1_entity *entity, unsigned int num_pads) { - static const struct { - unsigned int id; - unsigned int reg; - } routes[] = { - { VI6_DPR_NODE_HSI, VI6_DPR_HSI_ROUTE }, - { VI6_DPR_NODE_HST, VI6_DPR_HST_ROUTE }, - { VI6_DPR_NODE_LIF, 0 }, - { VI6_DPR_NODE_LUT, VI6_DPR_LUT_ROUTE }, - { VI6_DPR_NODE_RPF(0), VI6_DPR_RPF_ROUTE(0) }, - { VI6_DPR_NODE_RPF(1), VI6_DPR_RPF_ROUTE(1) }, - { VI6_DPR_NODE_RPF(2), VI6_DPR_RPF_ROUTE(2) }, - { VI6_DPR_NODE_RPF(3), VI6_DPR_RPF_ROUTE(3) }, - { VI6_DPR_NODE_RPF(4), VI6_DPR_RPF_ROUTE(4) }, - { VI6_DPR_NODE_SRU, VI6_DPR_SRU_ROUTE }, - { VI6_DPR_NODE_UDS(0), VI6_DPR_UDS_ROUTE(0) }, - { VI6_DPR_NODE_UDS(1), VI6_DPR_UDS_ROUTE(1) }, - { VI6_DPR_NODE_UDS(2), VI6_DPR_UDS_ROUTE(2) }, - { VI6_DPR_NODE_WPF(0), 0 }, - { VI6_DPR_NODE_WPF(1), 0 }, - { VI6_DPR_NODE_WPF(2), 0 }, - { VI6_DPR_NODE_WPF(3), 0 }, - }; - unsigned int i; - for (i = 0; i < ARRAY_SIZE(routes); ++i) { - if (routes[i].id == entity->id) { - entity->route = routes[i].reg; + for (i = 0; i < ARRAY_SIZE(vsp1_routes); ++i) { + if (vsp1_routes[i].type == entity->type && + vsp1_routes[i].index == entity->index) { + entity->route = &vsp1_routes[i]; break; } } - if (i == ARRAY_SIZE(routes)) + if (i == ARRAY_SIZE(vsp1_routes)) return -EINVAL; entity->vsp1 = vsp1; diff --git a/drivers/media/platform/vsp1/vsp1_entity.h b/drivers/media/platform/vsp1/vsp1_entity.h index f6fd6988aeb0..7afbd8a7ba66 100644 --- a/drivers/media/platform/vsp1/vsp1_entity.h +++ b/drivers/media/platform/vsp1/vsp1_entity.h @@ -20,6 +20,7 @@ struct vsp1_device; enum vsp1_entity_type { + VSP1_ENTITY_BRU, VSP1_ENTITY_HSI, VSP1_ENTITY_HST, VSP1_ENTITY_LIF, @@ -30,13 +31,31 @@ enum vsp1_entity_type { VSP1_ENTITY_WPF, }; +/* + * struct vsp1_route - Entity routing configuration + * @type: Entity type this routing entry is associated with + * @index: Entity index this routing entry is associated with + * @reg: Output routing configuration register + * @inputs: Target node value for each input + * + * Each $vsp1_route entry describes routing configuration for the entity + * specified by the entry's @type and @index. @reg indicates the register that + * holds output routing configuration for the entity, and the @inputs array + * store the target node value for each input of the entity. + */ +struct vsp1_route { + enum vsp1_entity_type type; + unsigned int index; + unsigned int reg; + unsigned int inputs[4]; +}; + struct vsp1_entity { struct vsp1_device *vsp1; enum vsp1_entity_type type; unsigned int index; - unsigned int id; - unsigned int route; + const struct vsp1_route *route; struct list_head list_dev; struct list_head list_pipe; @@ -45,6 +64,7 @@ struct vsp1_entity { unsigned int source_pad; struct media_entity *sink; + unsigned int sink_pad; struct v4l2_subdev subdev; struct v4l2_mbus_framefmt *formats; diff --git a/drivers/media/platform/vsp1/vsp1_hsit.c b/drivers/media/platform/vsp1/vsp1_hsit.c index 285485350d82..db2950a73c60 100644 --- a/drivers/media/platform/vsp1/vsp1_hsit.c +++ b/drivers/media/platform/vsp1/vsp1_hsit.c @@ -193,13 +193,10 @@ struct vsp1_hsit *vsp1_hsit_create(struct vsp1_device *vsp1, bool inverse) hsit->inverse = inverse; - if (inverse) { + if (inverse) hsit->entity.type = VSP1_ENTITY_HSI; - hsit->entity.id = VI6_DPR_NODE_HSI; - } else { + else hsit->entity.type = VSP1_ENTITY_HST; - hsit->entity.id = VI6_DPR_NODE_HST; - } ret = vsp1_entity_init(vsp1, &hsit->entity, 2); if (ret < 0) diff --git a/drivers/media/platform/vsp1/vsp1_lif.c b/drivers/media/platform/vsp1/vsp1_lif.c index 135a78957014..d4fb23e9c4a8 100644 --- a/drivers/media/platform/vsp1/vsp1_lif.c +++ b/drivers/media/platform/vsp1/vsp1_lif.c @@ -215,7 +215,6 @@ struct vsp1_lif *vsp1_lif_create(struct vsp1_device *vsp1) return ERR_PTR(-ENOMEM); lif->entity.type = VSP1_ENTITY_LIF; - lif->entity.id = VI6_DPR_NODE_LIF; ret = vsp1_entity_init(vsp1, &lif->entity, 2); if (ret < 0) diff --git a/drivers/media/platform/vsp1/vsp1_lut.c b/drivers/media/platform/vsp1/vsp1_lut.c index 4e9dc7c86ef8..fea36ebe2565 100644 --- a/drivers/media/platform/vsp1/vsp1_lut.c +++ b/drivers/media/platform/vsp1/vsp1_lut.c @@ -229,7 +229,6 @@ struct vsp1_lut *vsp1_lut_create(struct vsp1_device *vsp1) return ERR_PTR(-ENOMEM); lut->entity.type = VSP1_ENTITY_LUT; - lut->entity.id = VI6_DPR_NODE_LUT; ret = vsp1_entity_init(vsp1, &lut->entity, 2); if (ret < 0) diff --git a/drivers/media/platform/vsp1/vsp1_regs.h b/drivers/media/platform/vsp1/vsp1_regs.h index 28650806c20f..3e74b44286f6 100644 --- a/drivers/media/platform/vsp1/vsp1_regs.h +++ b/drivers/media/platform/vsp1/vsp1_regs.h @@ -451,13 +451,111 @@ * BRU Control Registers */ +#define VI6_ROP_NOP 0 +#define VI6_ROP_AND 1 +#define VI6_ROP_AND_REV 2 +#define VI6_ROP_COPY 3 +#define VI6_ROP_AND_INV 4 +#define VI6_ROP_CLEAR 5 +#define VI6_ROP_XOR 6 +#define VI6_ROP_OR 7 +#define VI6_ROP_NOR 8 +#define VI6_ROP_EQUIV 9 +#define VI6_ROP_INVERT 10 +#define VI6_ROP_OR_REV 11 +#define VI6_ROP_COPY_INV 12 +#define VI6_ROP_OR_INV 13 +#define VI6_ROP_NAND 14 +#define VI6_ROP_SET 15 + #define VI6_BRU_INCTRL 0x2c00 +#define VI6_BRU_INCTRL_NRM (1 << 28) +#define VI6_BRU_INCTRL_DnON (1 << (16 + (n))) +#define VI6_BRU_INCTRL_DITHn_OFF (0 << ((n) * 4)) +#define VI6_BRU_INCTRL_DITHn_18BPP (1 << ((n) * 4)) +#define VI6_BRU_INCTRL_DITHn_16BPP (2 << ((n) * 4)) +#define VI6_BRU_INCTRL_DITHn_15BPP (3 << ((n) * 4)) +#define VI6_BRU_INCTRL_DITHn_12BPP (4 << ((n) * 4)) +#define VI6_BRU_INCTRL_DITHn_8BPP (5 << ((n) * 4)) +#define VI6_BRU_INCTRL_DITHn_MASK (7 << ((n) * 4)) +#define VI6_BRU_INCTRL_DITHn_SHIFT ((n) * 4) + #define VI6_BRU_VIRRPF_SIZE 0x2c04 +#define VI6_BRU_VIRRPF_SIZE_HSIZE_MASK (0x1fff << 16) +#define VI6_BRU_VIRRPF_SIZE_HSIZE_SHIFT 16 +#define VI6_BRU_VIRRPF_SIZE_VSIZE_MASK (0x1fff << 0) +#define VI6_BRU_VIRRPF_SIZE_VSIZE_SHIFT 0 + #define VI6_BRU_VIRRPF_LOC 0x2c08 +#define VI6_BRU_VIRRPF_LOC_HCOORD_MASK (0x1fff << 16) +#define VI6_BRU_VIRRPF_LOC_HCOORD_SHIFT 16 +#define VI6_BRU_VIRRPF_LOC_VCOORD_MASK (0x1fff << 0) +#define VI6_BRU_VIRRPF_LOC_VCOORD_SHIFT 0 + #define VI6_BRU_VIRRPF_COL 0x2c0c +#define VI6_BRU_VIRRPF_COL_A_MASK (0xff << 24) +#define VI6_BRU_VIRRPF_COL_A_SHIFT 24 +#define VI6_BRU_VIRRPF_COL_RCR_MASK (0xff << 16) +#define VI6_BRU_VIRRPF_COL_RCR_SHIFT 16 +#define VI6_BRU_VIRRPF_COL_GY_MASK (0xff << 8) +#define VI6_BRU_VIRRPF_COL_GY_SHIFT 8 +#define VI6_BRU_VIRRPF_COL_BCB_MASK (0xff << 0) +#define VI6_BRU_VIRRPF_COL_BCB_SHIFT 0 + #define VI6_BRU_CTRL(n) (0x2c10 + (n) * 8) +#define VI6_BRU_CTRL_RBC (1 << 31) +#define VI6_BRU_CTRL_DSTSEL_BRUIN(n) ((n) << 20) +#define VI6_BRU_CTRL_DSTSEL_VRPF (4 << 20) +#define VI6_BRU_CTRL_DSTSEL_MASK (7 << 20) +#define VI6_BRU_CTRL_SRCSEL_BRUIN(n) ((n) << 16) +#define VI6_BRU_CTRL_SRCSEL_VRPF (4 << 16) +#define VI6_BRU_CTRL_SRCSEL_MASK (7 << 16) +#define VI6_BRU_CTRL_CROP(rop) ((rop) << 4) +#define VI6_BRU_CTRL_CROP_MASK (0xf << 4) +#define VI6_BRU_CTRL_AROP(rop) ((rop) << 0) +#define VI6_BRU_CTRL_AROP_MASK (0xf << 0) + #define VI6_BRU_BLD(n) (0x2c14 + (n) * 8) +#define VI6_BRU_BLD_CBES (1 << 31) +#define VI6_BRU_BLD_CCMDX_DST_A (0 << 28) +#define VI6_BRU_BLD_CCMDX_255_DST_A (1 << 28) +#define VI6_BRU_BLD_CCMDX_SRC_A (2 << 28) +#define VI6_BRU_BLD_CCMDX_255_SRC_A (3 << 28) +#define VI6_BRU_BLD_CCMDX_COEFX (4 << 28) +#define VI6_BRU_BLD_CCMDX_MASK (7 << 28) +#define VI6_BRU_BLD_CCMDY_DST_A (0 << 24) +#define VI6_BRU_BLD_CCMDY_255_DST_A (1 << 24) +#define VI6_BRU_BLD_CCMDY_SRC_A (2 << 24) +#define VI6_BRU_BLD_CCMDY_255_SRC_A (3 << 24) +#define VI6_BRU_BLD_CCMDY_COEFY (4 << 24) +#define VI6_BRU_BLD_CCMDY_MASK (7 << 24) +#define VI6_BRU_BLD_CCMDY_SHIFT 24 +#define VI6_BRU_BLD_ABES (1 << 23) +#define VI6_BRU_BLD_ACMDX_DST_A (0 << 20) +#define VI6_BRU_BLD_ACMDX_255_DST_A (1 << 20) +#define VI6_BRU_BLD_ACMDX_SRC_A (2 << 20) +#define VI6_BRU_BLD_ACMDX_255_SRC_A (3 << 20) +#define VI6_BRU_BLD_ACMDX_COEFX (4 << 20) +#define VI6_BRU_BLD_ACMDX_MASK (7 << 20) +#define VI6_BRU_BLD_ACMDY_DST_A (0 << 16) +#define VI6_BRU_BLD_ACMDY_255_DST_A (1 << 16) +#define VI6_BRU_BLD_ACMDY_SRC_A (2 << 16) +#define VI6_BRU_BLD_ACMDY_255_SRC_A (3 << 16) +#define VI6_BRU_BLD_ACMDY_COEFY (4 << 16) +#define VI6_BRU_BLD_ACMDY_MASK (7 << 16) +#define VI6_BRU_BLD_COEFX_MASK (0xff << 8) +#define VI6_BRU_BLD_COEFX_SHIFT 8 +#define VI6_BRU_BLD_COEFY_MASK (0xff << 0) +#define VI6_BRU_BLD_COEFY_SHIFT 0 + #define VI6_BRU_ROP 0x2c30 +#define VI6_BRU_ROP_DSTSEL_BRUIN(n) ((n) << 20) +#define VI6_BRU_ROP_DSTSEL_VRPF (4 << 20) +#define VI6_BRU_ROP_DSTSEL_MASK (7 << 20) +#define VI6_BRU_ROP_CROP(rop) ((rop) << 4) +#define VI6_BRU_ROP_CROP_MASK (0xf << 4) +#define VI6_BRU_ROP_AROP(rop) ((rop) << 0) +#define VI6_BRU_ROP_AROP_MASK (0xf << 0) /* ----------------------------------------------------------------------------- * HGO Control Registers diff --git a/drivers/media/platform/vsp1/vsp1_rpf.c b/drivers/media/platform/vsp1/vsp1_rpf.c index 2b04d0f95c62..c3d98642a4aa 100644 --- a/drivers/media/platform/vsp1/vsp1_rpf.c +++ b/drivers/media/platform/vsp1/vsp1_rpf.c @@ -96,8 +96,10 @@ static int rpf_s_stream(struct v4l2_subdev *subdev, int enable) vsp1_rpf_write(rpf, VI6_RPF_INFMT, infmt); vsp1_rpf_write(rpf, VI6_RPF_DSWAP, fmtinfo->swap); - /* Output location. Composing isn't supported yet. */ - vsp1_rpf_write(rpf, VI6_RPF_LOC, 0); + /* Output location */ + vsp1_rpf_write(rpf, VI6_RPF_LOC, + (rpf->location.left << VI6_RPF_LOC_HCOORD_SHIFT) | + (rpf->location.top << VI6_RPF_LOC_VCOORD_SHIFT)); /* Disable alpha, mask and color key. Set the alpha channel to a fixed * value of 255. @@ -176,7 +178,6 @@ struct vsp1_rwpf *vsp1_rpf_create(struct vsp1_device *vsp1, unsigned int index) rpf->entity.type = VSP1_ENTITY_RPF; rpf->entity.index = index; - rpf->entity.id = VI6_DPR_NODE_RPF(index); ret = vsp1_entity_init(vsp1, &rpf->entity, 2); if (ret < 0) diff --git a/drivers/media/platform/vsp1/vsp1_rwpf.h b/drivers/media/platform/vsp1/vsp1_rwpf.h index 5c5ee81bbeae..b4fb65e58770 100644 --- a/drivers/media/platform/vsp1/vsp1_rwpf.h +++ b/drivers/media/platform/vsp1/vsp1_rwpf.h @@ -30,6 +30,10 @@ struct vsp1_rwpf { unsigned int max_width; unsigned int max_height; + struct { + unsigned int left; + unsigned int top; + } location; struct v4l2_rect crop; unsigned int offsets[2]; diff --git a/drivers/media/platform/vsp1/vsp1_sru.c b/drivers/media/platform/vsp1/vsp1_sru.c index 7ab1a0b2d656..aa0e04c56f3f 100644 --- a/drivers/media/platform/vsp1/vsp1_sru.c +++ b/drivers/media/platform/vsp1/vsp1_sru.c @@ -327,7 +327,6 @@ struct vsp1_sru *vsp1_sru_create(struct vsp1_device *vsp1) return ERR_PTR(-ENOMEM); sru->entity.type = VSP1_ENTITY_SRU; - sru->entity.id = VI6_DPR_NODE_SRU; ret = vsp1_entity_init(vsp1, &sru->entity, 2); if (ret < 0) diff --git a/drivers/media/platform/vsp1/vsp1_uds.c b/drivers/media/platform/vsp1/vsp1_uds.c index 622342ac7770..0293bdbb4401 100644 --- a/drivers/media/platform/vsp1/vsp1_uds.c +++ b/drivers/media/platform/vsp1/vsp1_uds.c @@ -131,7 +131,7 @@ static int uds_s_stream(struct v4l2_subdev *subdev, int enable) return 0; /* Enable multi-tap scaling. */ - vsp1_uds_write(uds, VI6_UDS_CTRL, VI6_UDS_CTRL_BC); + vsp1_uds_write(uds, VI6_UDS_CTRL, VI6_UDS_CTRL_AON | VI6_UDS_CTRL_BC); vsp1_uds_write(uds, VI6_UDS_PASS_BWIDTH, (uds_passband_width(uds->hscale) @@ -139,7 +139,6 @@ static int uds_s_stream(struct v4l2_subdev *subdev, int enable) (uds_passband_width(uds->vscale) << VI6_UDS_PASS_BWIDTH_V_SHIFT)); - /* Set the scaling ratios and the output size. */ format = &uds->entity.formats[UDS_PAD_SOURCE]; @@ -323,7 +322,6 @@ struct vsp1_uds *vsp1_uds_create(struct vsp1_device *vsp1, unsigned int index) uds->entity.type = VSP1_ENTITY_UDS; uds->entity.index = index; - uds->entity.id = VI6_DPR_NODE_UDS(index); ret = vsp1_entity_init(vsp1, &uds->entity, 2); if (ret < 0) diff --git a/drivers/media/platform/vsp1/vsp1_video.c b/drivers/media/platform/vsp1/vsp1_video.c index b48f135ffc01..8a1253e51f04 100644 --- a/drivers/media/platform/vsp1/vsp1_video.c +++ b/drivers/media/platform/vsp1/vsp1_video.c @@ -28,6 +28,7 @@ #include <media/videobuf2-dma-contig.h> #include "vsp1.h" +#include "vsp1_bru.h" #include "vsp1_entity.h" #include "vsp1_rwpf.h" #include "vsp1_video.h" @@ -280,6 +281,9 @@ static int vsp1_pipeline_validate_branch(struct vsp1_rwpf *input, struct media_pad *pad; bool uds_found = false; + input->location.left = 0; + input->location.top = 0; + pad = media_entity_remote_pad(&input->entity.pads[RWPF_PAD_SOURCE]); while (1) { @@ -292,6 +296,17 @@ static int vsp1_pipeline_validate_branch(struct vsp1_rwpf *input, entity = to_vsp1_entity(media_entity_to_v4l2_subdev(pad->entity)); + /* A BRU is present in the pipeline, store the compose rectangle + * location in the input RPF for use when configuring the RPF. + */ + if (entity->type == VSP1_ENTITY_BRU) { + struct vsp1_bru *bru = to_bru(&entity->subdev); + struct v4l2_rect *rect = &bru->compose[pad->index]; + + input->location.left = rect->left; + input->location.top = rect->top; + } + /* We've reached the WPF, we're done. */ if (entity->type == VSP1_ENTITY_WPF) break; @@ -363,6 +378,8 @@ static int vsp1_pipeline_validate(struct vsp1_pipeline *pipe, rwpf->video.pipe_index = 0; } else if (e->type == VSP1_ENTITY_LIF) { pipe->lif = e; + } else if (e->type == VSP1_ENTITY_BRU) { + pipe->bru = e; } } @@ -392,6 +409,7 @@ error: pipe->num_video = 0; pipe->num_inputs = 0; pipe->output = NULL; + pipe->bru = NULL; pipe->lif = NULL; return ret; } @@ -430,6 +448,7 @@ static void vsp1_pipeline_cleanup(struct vsp1_pipeline *pipe) pipe->num_video = 0; pipe->num_inputs = 0; pipe->output = NULL; + pipe->bru = NULL; pipe->lif = NULL; } @@ -461,7 +480,7 @@ static int vsp1_pipeline_stop(struct vsp1_pipeline *pipe) list_for_each_entry(entity, &pipe->entities, list_pipe) { if (entity->route) - vsp1_write(entity->vsp1, entity->route, + vsp1_write(entity->vsp1, entity->route->reg, VI6_DPR_NODE_UNUSED); v4l2_subdev_call(&entity->subdev, video, s_stream, 0); @@ -680,11 +699,12 @@ static void vsp1_entity_route_setup(struct vsp1_entity *source) { struct vsp1_entity *sink; - if (source->route == 0) + if (source->route->reg == 0) return; sink = container_of(source->sink, struct vsp1_entity, subdev.entity); - vsp1_write(source->vsp1, source->route, sink->id); + vsp1_write(source->vsp1, source->route->reg, + sink->route->inputs[source->sink_pad]); } static int vsp1_video_start_streaming(struct vb2_queue *vq, unsigned int count) @@ -720,7 +740,7 @@ static int vsp1_video_start_streaming(struct vb2_queue *vq, unsigned int count) return 0; } -static int vsp1_video_stop_streaming(struct vb2_queue *vq) +static void vsp1_video_stop_streaming(struct vb2_queue *vq) { struct vsp1_video *video = vb2_get_drv_priv(vq); struct vsp1_pipeline *pipe = to_vsp1_pipeline(&video->video.entity); @@ -743,8 +763,6 @@ static int vsp1_video_stop_streaming(struct vb2_queue *vq) spin_lock_irqsave(&video->irqlock, flags); INIT_LIST_HEAD(&video->irqqueue); spin_unlock_irqrestore(&video->irqlock, flags); - - return 0; } static struct vb2_ops vsp1_video_queue_qops = { diff --git a/drivers/media/platform/vsp1/vsp1_video.h b/drivers/media/platform/vsp1/vsp1_video.h index 53e4b3745940..c04d48fa2999 100644 --- a/drivers/media/platform/vsp1/vsp1_video.h +++ b/drivers/media/platform/vsp1/vsp1_video.h @@ -75,6 +75,7 @@ struct vsp1_pipeline { unsigned int num_inputs; struct vsp1_rwpf *inputs[VPS1_MAX_RPF]; struct vsp1_rwpf *output; + struct vsp1_entity *bru; struct vsp1_entity *lif; struct list_head entities; diff --git a/drivers/media/platform/vsp1/vsp1_wpf.c b/drivers/media/platform/vsp1/vsp1_wpf.c index 11a61c601da0..1294340dcb36 100644 --- a/drivers/media/platform/vsp1/vsp1_wpf.c +++ b/drivers/media/platform/vsp1/vsp1_wpf.c @@ -58,13 +58,21 @@ static int wpf_s_stream(struct v4l2_subdev *subdev, int enable) return 0; } - /* Sources */ + /* Sources. If the pipeline has a single input configure it as the + * master layer. Otherwise configure all inputs as sub-layers and + * select the virtual RPF as the master layer. + */ for (i = 0; i < pipe->num_inputs; ++i) { struct vsp1_rwpf *input = pipe->inputs[i]; - srcrpf |= VI6_WPF_SRCRPF_RPF_ACT_MST(input->entity.index); + srcrpf |= pipe->num_inputs == 1 + ? VI6_WPF_SRCRPF_RPF_ACT_MST(input->entity.index) + : VI6_WPF_SRCRPF_RPF_ACT_SUB(input->entity.index); } + if (pipe->num_inputs > 1) + srcrpf |= VI6_WPF_SRCRPF_VIRACT_MST; + vsp1_wpf_write(wpf, VI6_WPF_SRCRPF, srcrpf); /* Destination stride. */ @@ -181,7 +189,6 @@ struct vsp1_rwpf *vsp1_wpf_create(struct vsp1_device *vsp1, unsigned int index) wpf->entity.type = VSP1_ENTITY_WPF; wpf->entity.index = index; - wpf->entity.id = VI6_DPR_NODE_WPF(index); ret = vsp1_entity_init(vsp1, &wpf->entity, 2); if (ret < 0) diff --git a/drivers/media/rc/mceusb.c b/drivers/media/rc/mceusb.c index 5d8f3d40d820..d5c1df3c9db1 100644 --- a/drivers/media/rc/mceusb.c +++ b/drivers/media/rc/mceusb.c @@ -747,11 +747,19 @@ static void mce_request_packet(struct mceusb_dev *ir, unsigned char *data, } /* outbound data */ - pipe = usb_sndintpipe(ir->usbdev, - ir->usb_ep_out->bEndpointAddress); - usb_fill_int_urb(async_urb, ir->usbdev, pipe, - async_buf, size, mce_async_callback, - ir, ir->usb_ep_out->bInterval); + if (usb_endpoint_xfer_int(ir->usb_ep_out)) { + pipe = usb_sndintpipe(ir->usbdev, + ir->usb_ep_out->bEndpointAddress); + usb_fill_int_urb(async_urb, ir->usbdev, pipe, async_buf, + size, mce_async_callback, ir, + ir->usb_ep_out->bInterval); + } else { + pipe = usb_sndbulkpipe(ir->usbdev, + ir->usb_ep_out->bEndpointAddress); + usb_fill_bulk_urb(async_urb, ir->usbdev, pipe, + async_buf, size, mce_async_callback, + ir); + } memcpy(async_buf, data, size); } else if (urb_type == MCEUSB_RX) { @@ -1269,32 +1277,26 @@ static int mceusb_dev_probe(struct usb_interface *intf, for (i = 0; i < idesc->desc.bNumEndpoints; ++i) { ep = &idesc->endpoint[i].desc; - if ((ep_in == NULL) - && ((ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK) - == USB_DIR_IN) - && (((ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) - == USB_ENDPOINT_XFER_BULK) - || ((ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) - == USB_ENDPOINT_XFER_INT))) { - - ep_in = ep; - ep_in->bmAttributes = USB_ENDPOINT_XFER_INT; - ep_in->bInterval = 1; - dev_dbg(&intf->dev, "acceptable inbound endpoint found"); + if (ep_in == NULL) { + if (usb_endpoint_is_bulk_in(ep)) { + ep_in = ep; + dev_dbg(&intf->dev, "acceptable bulk inbound endpoint found\n"); + } else if (usb_endpoint_is_int_in(ep)) { + ep_in = ep; + ep_in->bInterval = 1; + dev_dbg(&intf->dev, "acceptable interrupt inbound endpoint found\n"); + } } - if ((ep_out == NULL) - && ((ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK) - == USB_DIR_OUT) - && (((ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) - == USB_ENDPOINT_XFER_BULK) - || ((ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) - == USB_ENDPOINT_XFER_INT))) { - - ep_out = ep; - ep_out->bmAttributes = USB_ENDPOINT_XFER_INT; - ep_out->bInterval = 1; - dev_dbg(&intf->dev, "acceptable outbound endpoint found"); + if (ep_out == NULL) { + if (usb_endpoint_is_bulk_out(ep)) { + ep_out = ep; + dev_dbg(&intf->dev, "acceptable bulk outbound endpoint found\n"); + } else if (usb_endpoint_is_int_out(ep)) { + ep_out = ep; + ep_out->bInterval = 1; + dev_dbg(&intf->dev, "acceptable interrupt outbound endpoint found\n"); + } } } if (ep_in == NULL) { @@ -1302,7 +1304,10 @@ static int mceusb_dev_probe(struct usb_interface *intf, return -ENODEV; } - pipe = usb_rcvintpipe(dev, ep_in->bEndpointAddress); + if (usb_endpoint_xfer_int(ep_in)) + pipe = usb_rcvintpipe(dev, ep_in->bEndpointAddress); + else + pipe = usb_rcvbulkpipe(dev, ep_in->bEndpointAddress); maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe)); ir = kzalloc(sizeof(struct mceusb_dev), GFP_KERNEL); diff --git a/drivers/media/rc/redrat3.c b/drivers/media/rc/redrat3.c index 47cd373e2295..79abbc8d9600 100644 --- a/drivers/media/rc/redrat3.c +++ b/drivers/media/rc/redrat3.c @@ -60,28 +60,6 @@ #define DRIVER_DESC "RedRat3 USB IR Transceiver Driver" #define DRIVER_NAME "redrat3" -/* module parameters */ -#ifdef CONFIG_USB_DEBUG -static int debug = 1; -#else -static int debug; -#endif - -#define RR3_DEBUG_STANDARD 0x1 -#define RR3_DEBUG_FUNCTION_TRACE 0x2 - -#define rr3_dbg(dev, fmt, ...) \ - do { \ - if (debug & RR3_DEBUG_STANDARD) \ - dev_info(dev, fmt, ## __VA_ARGS__); \ - } while (0) - -#define rr3_ftr(dev, fmt, ...) \ - do { \ - if (debug & RR3_DEBUG_FUNCTION_TRACE) \ - dev_info(dev, fmt, ## __VA_ARGS__); \ - } while (0) - /* bulk data transfer types */ #define RR3_ERROR 0x01 #define RR3_MOD_SIGNAL_IN 0x20 @@ -237,13 +215,11 @@ static void redrat3_issue_async(struct redrat3_dev *rr3) { int res; - rr3_ftr(rr3->dev, "Entering %s\n", __func__); - res = usb_submit_urb(rr3->read_urb, GFP_ATOMIC); if (res) - rr3_dbg(rr3->dev, "%s: receive request FAILED! " - "(res %d, len %d)\n", __func__, res, - rr3->read_urb->transfer_buffer_length); + dev_dbg(rr3->dev, + "%s: receive request FAILED! (res %d, len %d)\n", + __func__, res, rr3->read_urb->transfer_buffer_length); } static void redrat3_dump_fw_error(struct redrat3_dev *rr3, int code) @@ -359,7 +335,7 @@ static void redrat3_rx_timeout(unsigned long data) { struct redrat3_dev *rr3 = (struct redrat3_dev *)data; - rr3_dbg(rr3->dev, "calling ir_raw_event_reset\n"); + dev_dbg(rr3->dev, "calling ir_raw_event_reset\n"); ir_raw_event_reset(rr3->rc); } @@ -377,8 +353,6 @@ static void redrat3_process_ir_data(struct redrat3_dev *rr3) return; } - rr3_ftr(rr3->dev, "Entered %s\n", __func__); - dev = rr3->dev; /* Make sure we reset the IR kfifo after a bit of inactivity */ @@ -386,7 +360,7 @@ static void redrat3_process_ir_data(struct redrat3_dev *rr3) mod_timer(&rr3->rx_timeout, jiffies + delay); mod_freq = redrat3_val_to_mod_freq(&rr3->irdata); - rr3_dbg(dev, "Got mod_freq of %u\n", mod_freq); + dev_dbg(dev, "Got mod_freq of %u\n", mod_freq); /* process each rr3 encoded byte into an int */ sig_size = be16_to_cpu(rr3->irdata.sig_size); @@ -408,7 +382,7 @@ static void redrat3_process_ir_data(struct redrat3_dev *rr3) /* cap the value to IR_MAX_DURATION */ rawir.duration &= IR_MAX_DURATION; - rr3_dbg(dev, "storing %s with duration %d (i: %d)\n", + dev_dbg(dev, "storing %s with duration %d (i: %d)\n", rawir.pulse ? "pulse" : "space", rawir.duration, i); ir_raw_event_store_with_filter(rr3->rc, &rawir); } @@ -421,12 +395,12 @@ static void redrat3_process_ir_data(struct redrat3_dev *rr3) rawir.duration = US_TO_NS(2800); else rawir.duration = trailer; - rr3_dbg(dev, "storing trailing space with duration %d\n", + dev_dbg(dev, "storing trailing space with duration %d\n", rawir.duration); ir_raw_event_store_with_filter(rr3->rc, &rawir); } - rr3_dbg(dev, "calling ir_raw_event_handle\n"); + dev_dbg(dev, "calling ir_raw_event_handle\n"); ir_raw_event_handle(rr3->rc); } @@ -464,8 +438,6 @@ static int redrat3_enable_detector(struct redrat3_dev *rr3) struct device *dev = rr3->dev; u8 ret; - rr3_ftr(dev, "Entering %s\n", __func__); - ret = redrat3_send_cmd(RR3_RC_DET_ENABLE, rr3); if (ret != 0) dev_dbg(dev, "%s: unexpected ret of %d\n", @@ -486,7 +458,6 @@ static int redrat3_enable_detector(struct redrat3_dev *rr3) static inline void redrat3_delete(struct redrat3_dev *rr3, struct usb_device *udev) { - rr3_ftr(rr3->dev, "%s cleaning up\n", __func__); usb_kill_urb(rr3->read_urb); usb_kill_urb(rr3->flash_urb); usb_free_urb(rr3->read_urb); @@ -519,7 +490,7 @@ static u32 redrat3_get_timeout(struct redrat3_dev *rr3) else { timeout = redrat3_len_to_us(be32_to_cpup(tmp)); - rr3_dbg(rr3->dev, "Got timeout of %d ms\n", timeout / 1000); + dev_dbg(rr3->dev, "Got timeout of %d ms\n", timeout / 1000); } kfree(tmp); @@ -535,8 +506,6 @@ static void redrat3_reset(struct redrat3_dev *rr3) u8 *val; int len = sizeof(u8); - rr3_ftr(dev, "Entering %s\n", __func__); - rxpipe = usb_rcvctrlpipe(udev, 0); txpipe = usb_sndctrlpipe(udev, 0); @@ -550,19 +519,19 @@ static void redrat3_reset(struct redrat3_dev *rr3) rc = usb_control_msg(udev, rxpipe, RR3_RESET, USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, RR3_CPUCS_REG_ADDR, 0, val, len, HZ * 25); - rr3_dbg(dev, "reset returned 0x%02x\n", rc); + dev_dbg(dev, "reset returned 0x%02x\n", rc); *val = 5; rc = usb_control_msg(udev, txpipe, RR3_SET_IR_PARAM, USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT, RR3_IR_IO_LENGTH_FUZZ, 0, val, len, HZ * 25); - rr3_dbg(dev, "set ir parm len fuzz %d rc 0x%02x\n", *val, rc); + dev_dbg(dev, "set ir parm len fuzz %d rc 0x%02x\n", *val, rc); *val = RR3_DRIVER_MAXLENS; rc = usb_control_msg(udev, txpipe, RR3_SET_IR_PARAM, USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT, RR3_IR_IO_MAX_LENGTHS, 0, val, len, HZ * 25); - rr3_dbg(dev, "set ir parm max lens %d rc 0x%02x\n", *val, rc); + dev_dbg(dev, "set ir parm max lens %d rc 0x%02x\n", *val, rc); kfree(val); } @@ -572,8 +541,6 @@ static void redrat3_get_firmware_rev(struct redrat3_dev *rr3) int rc = 0; char *buffer; - rr3_ftr(rr3->dev, "Entering %s\n", __func__); - buffer = kzalloc(sizeof(char) * (RR3_FW_VERSION_LEN + 1), GFP_KERNEL); if (!buffer) { dev_err(rr3->dev, "Memory allocation failure\n"); @@ -591,7 +558,6 @@ static void redrat3_get_firmware_rev(struct redrat3_dev *rr3) dev_err(rr3->dev, "Problem fetching firmware ID\n"); kfree(buffer); - rr3_ftr(rr3->dev, "Exiting %s\n", __func__); } static void redrat3_read_packet_start(struct redrat3_dev *rr3, unsigned len) @@ -599,8 +565,6 @@ static void redrat3_read_packet_start(struct redrat3_dev *rr3, unsigned len) struct redrat3_header *header = rr3->bulk_in_buf; unsigned pktlen, pkttype; - rr3_ftr(rr3->dev, "Entering %s\n", __func__); - /* grab the Length and type of transfer */ pktlen = be16_to_cpu(header->length); pkttype = be16_to_cpu(header->transfer_type); @@ -622,12 +586,12 @@ static void redrat3_read_packet_start(struct redrat3_dev *rr3, unsigned len) case RR3_MOD_SIGNAL_IN: memcpy(&rr3->irdata, rr3->bulk_in_buf, len); rr3->bytes_read = len; - rr3_dbg(rr3->dev, "bytes_read %d, pktlen %d\n", + dev_dbg(rr3->dev, "bytes_read %d, pktlen %d\n", rr3->bytes_read, pktlen); break; default: - rr3_dbg(rr3->dev, "ignoring packet with type 0x%02x, len of %d, 0x%02x\n", + dev_dbg(rr3->dev, "ignoring packet with type 0x%02x, len of %d, 0x%02x\n", pkttype, len, pktlen); break; } @@ -637,8 +601,6 @@ static void redrat3_read_packet_continue(struct redrat3_dev *rr3, unsigned len) { void *irdata = &rr3->irdata; - rr3_ftr(rr3->dev, "Entering %s\n", __func__); - if (len + rr3->bytes_read > sizeof(rr3->irdata)) { dev_warn(rr3->dev, "too much data for packet\n"); rr3->bytes_read = 0; @@ -648,7 +610,7 @@ static void redrat3_read_packet_continue(struct redrat3_dev *rr3, unsigned len) memcpy(irdata + rr3->bytes_read, rr3->bulk_in_buf, len); rr3->bytes_read += len; - rr3_dbg(rr3->dev, "bytes_read %d, pktlen %d\n", rr3->bytes_read, + dev_dbg(rr3->dev, "bytes_read %d, pktlen %d\n", rr3->bytes_read, be16_to_cpu(rr3->irdata.header.length)); } @@ -659,8 +621,6 @@ static int redrat3_get_ir_data(struct redrat3_dev *rr3, unsigned len) unsigned pkttype; int ret = 0; - rr3_ftr(dev, "Entering %s\n", __func__); - if (rr3->bytes_read == 0 && len >= sizeof(struct redrat3_header)) { redrat3_read_packet_start(rr3, len); } else if (rr3->bytes_read != 0) { @@ -681,7 +641,7 @@ static int redrat3_get_ir_data(struct redrat3_dev *rr3, unsigned len) if (pkttype == RR3_MOD_SIGNAL_IN) redrat3_process_ir_data(rr3); else - rr3_dbg(dev, "discarding non-signal data packet (type 0x%02x)\n", + dev_dbg(dev, "discarding non-signal data packet (type 0x%02x)\n", pkttype); out: @@ -705,8 +665,6 @@ static void redrat3_handle_async(struct urb *urb) return; } - rr3_ftr(rr3->dev, "Entering %s\n", __func__); - switch (urb->status) { case 0: ret = redrat3_get_ir_data(rr3, urb->actual_length); @@ -743,7 +701,7 @@ static int redrat3_set_tx_carrier(struct rc_dev *rcdev, u32 carrier) struct redrat3_dev *rr3 = rcdev->priv; struct device *dev = rr3->dev; - rr3_dbg(dev, "Setting modulation frequency to %u", carrier); + dev_dbg(dev, "Setting modulation frequency to %u", carrier); if (carrier == 0) return -EINVAL; @@ -764,8 +722,6 @@ static int redrat3_transmit_ir(struct rc_dev *rcdev, unsigned *txbuf, u8 curlencheck = 0; unsigned i, sendbuf_len; - rr3_ftr(dev, "Entering %s\n", __func__); - if (rr3->transmitting) { dev_warn(dev, "%s: transmitter already in use\n", __func__); return -EAGAIN; @@ -801,7 +757,7 @@ static int redrat3_transmit_ir(struct rc_dev *rcdev, unsigned *txbuf, break; } if (lencheck == curlencheck) { - rr3_dbg(dev, "txbuf[%d]=%u, pos %d, enc %u\n", + dev_dbg(dev, "txbuf[%d]=%u, pos %d, enc %u\n", i, txbuf[i], curlencheck, cur_sample_len); if (curlencheck < RR3_DRIVER_MAXLENS) { /* now convert the value to a proper @@ -835,7 +791,7 @@ static int redrat3_transmit_ir(struct rc_dev *rcdev, unsigned *txbuf, pipe = usb_sndbulkpipe(rr3->udev, rr3->ep_out->bEndpointAddress); ret = usb_bulk_msg(rr3->udev, pipe, irdata, sendbuf_len, &ret_len, 10 * HZ); - rr3_dbg(dev, "sent %d bytes, (ret %d)\n", ret_len, ret); + dev_dbg(dev, "sent %d bytes, (ret %d)\n", ret_len, ret); /* now tell the hardware to transmit what we sent it */ pipe = usb_rcvctrlpipe(rr3->udev, 0); @@ -957,8 +913,6 @@ static int redrat3_dev_probe(struct usb_interface *intf, int pipe, i; int retval = -ENOMEM; - rr3_ftr(dev, "%s called\n", __func__); - uhi = intf->cur_altsetting; /* find our bulk-in and bulk-out endpoints */ @@ -971,7 +925,7 @@ static int redrat3_dev_probe(struct usb_interface *intf, ((addr & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN) && ((attrs & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_BULK)) { - rr3_dbg(dev, "found bulk-in endpoint at 0x%02x\n", + dev_dbg(dev, "found bulk-in endpoint at 0x%02x\n", ep->bEndpointAddress); /* data comes in on 0x82, 0x81 is for other data... */ if (ep->bEndpointAddress == RR3_BULK_IN_EP_ADDR) @@ -982,7 +936,7 @@ static int redrat3_dev_probe(struct usb_interface *intf, ((addr & USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT) && ((attrs & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_BULK)) { - rr3_dbg(dev, "found bulk-out endpoint at 0x%02x\n", + dev_dbg(dev, "found bulk-out endpoint at 0x%02x\n", ep->bEndpointAddress); ep_out = ep; } @@ -1074,7 +1028,6 @@ static int redrat3_dev_probe(struct usb_interface *intf, /* we can register the device now, as it is ready */ usb_set_intfdata(intf, rr3); - rr3_ftr(dev, "Exiting %s\n", __func__); return 0; led_free_error: @@ -1093,8 +1046,6 @@ static void redrat3_dev_disconnect(struct usb_interface *intf) struct usb_device *udev = interface_to_usbdev(intf); struct redrat3_dev *rr3 = usb_get_intfdata(intf); - rr3_ftr(&intf->dev, "Entering %s\n", __func__); - if (!rr3) return; @@ -1103,14 +1054,12 @@ static void redrat3_dev_disconnect(struct usb_interface *intf) led_classdev_unregister(&rr3->led); del_timer_sync(&rr3->rx_timeout); redrat3_delete(rr3, udev); - - rr3_ftr(&intf->dev, "RedRat3 IR Transceiver now disconnected\n"); } static int redrat3_dev_suspend(struct usb_interface *intf, pm_message_t message) { struct redrat3_dev *rr3 = usb_get_intfdata(intf); - rr3_ftr(rr3->dev, "suspend\n"); + led_classdev_suspend(&rr3->led); usb_kill_urb(rr3->read_urb); usb_kill_urb(rr3->flash_urb); @@ -1120,7 +1069,7 @@ static int redrat3_dev_suspend(struct usb_interface *intf, pm_message_t message) static int redrat3_dev_resume(struct usb_interface *intf) { struct redrat3_dev *rr3 = usb_get_intfdata(intf); - rr3_ftr(rr3->dev, "resume\n"); + if (usb_submit_urb(rr3->read_urb, GFP_ATOMIC)) return -EIO; led_classdev_resume(&rr3->led); @@ -1144,8 +1093,3 @@ MODULE_AUTHOR(DRIVER_AUTHOR); MODULE_AUTHOR(DRIVER_AUTHOR2); MODULE_LICENSE("GPL"); MODULE_DEVICE_TABLE(usb, redrat3_dev_table); - -module_param(debug, int, S_IRUGO | S_IWUSR); -MODULE_PARM_DESC(debug, "Enable module debug spew. 0 = no debugging (default) " - "0x1 = standard debug messages, 0x2 = function tracing debug. " - "Flag bits are addative (i.e., 0x3 for both debug types)."); diff --git a/drivers/media/rc/streamzap.c b/drivers/media/rc/streamzap.c index f4e0bc3d382c..bd5e4ff9e0ba 100644 --- a/drivers/media/rc/streamzap.c +++ b/drivers/media/rc/streamzap.c @@ -42,12 +42,6 @@ #define DRIVER_NAME "streamzap" #define DRIVER_DESC "Streamzap Remote Control driver" -#ifdef CONFIG_USB_DEBUG -static bool debug = 1; -#else -static bool debug; -#endif - #define USB_STREAMZAP_VENDOR_ID 0x0e9c #define USB_STREAMZAP_PRODUCT_ID 0x0000 @@ -528,6 +522,3 @@ module_usb_driver(streamzap_driver); MODULE_AUTHOR("Jarod Wilson <jarod@wilsonet.com>"); MODULE_DESCRIPTION(DRIVER_DESC); MODULE_LICENSE("GPL"); - -module_param(debug, bool, S_IRUGO | S_IWUSR); -MODULE_PARM_DESC(debug, "Enable debugging messages"); diff --git a/drivers/media/tuners/Kconfig b/drivers/media/tuners/Kconfig index a1284889cd15..22b6b8bb1d93 100644 --- a/drivers/media/tuners/Kconfig +++ b/drivers/media/tuners/Kconfig @@ -230,6 +230,13 @@ config MEDIA_TUNER_TUA9001 help Infineon TUA 9001 silicon tuner driver. +config MEDIA_TUNER_SI2157 + tristate "Silicon Labs Si2157 silicon tuner" + depends on MEDIA_SUPPORT && I2C + default m if !MEDIA_SUBDRV_AUTOSELECT + help + Silicon Labs Si2157 silicon tuner driver. + config MEDIA_TUNER_IT913X tristate "ITE Tech IT913x silicon tuner" depends on MEDIA_SUPPORT && I2C diff --git a/drivers/media/tuners/Makefile b/drivers/media/tuners/Makefile index efe82a904b12..a6ff0c628dfa 100644 --- a/drivers/media/tuners/Makefile +++ b/drivers/media/tuners/Makefile @@ -31,6 +31,7 @@ obj-$(CONFIG_MEDIA_TUNER_TDA18212) += tda18212.o obj-$(CONFIG_MEDIA_TUNER_E4000) += e4000.o obj-$(CONFIG_MEDIA_TUNER_FC2580) += fc2580.o obj-$(CONFIG_MEDIA_TUNER_TUA9001) += tua9001.o +obj-$(CONFIG_MEDIA_TUNER_SI2157) += si2157.o obj-$(CONFIG_MEDIA_TUNER_M88TS2022) += m88ts2022.o obj-$(CONFIG_MEDIA_TUNER_FC0011) += fc0011.o obj-$(CONFIG_MEDIA_TUNER_FC0012) += fc0012.o diff --git a/drivers/media/tuners/si2157.c b/drivers/media/tuners/si2157.c new file mode 100644 index 000000000000..271a752cee54 --- /dev/null +++ b/drivers/media/tuners/si2157.c @@ -0,0 +1,260 @@ +/* + * Silicon Labs Si2157 silicon tuner 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 "si2157_priv.h" + +/* execute firmware command */ +static int si2157_cmd_execute(struct si2157 *s, struct si2157_cmd *cmd) +{ + int ret; + u8 buf[1]; + unsigned long timeout; + + mutex_lock(&s->i2c_mutex); + + if (cmd->len) { + /* write cmd and args for firmware */ + ret = i2c_master_send(s->client, cmd->args, cmd->len); + if (ret < 0) { + goto err_mutex_unlock; + } else if (ret != cmd->len) { + ret = -EREMOTEIO; + goto err_mutex_unlock; + } + } + + /* wait cmd execution terminate */ + #define TIMEOUT 80 + timeout = jiffies + msecs_to_jiffies(TIMEOUT); + while (!time_after(jiffies, timeout)) { + ret = i2c_master_recv(s->client, buf, 1); + if (ret < 0) { + goto err_mutex_unlock; + } else if (ret != 1) { + ret = -EREMOTEIO; + goto err_mutex_unlock; + } + + /* firmware ready? */ + if ((buf[0] >> 7) & 0x01) + break; + } + + dev_dbg(&s->client->dev, "%s: cmd execution took %d ms\n", __func__, + jiffies_to_msecs(jiffies) - + (jiffies_to_msecs(timeout) - TIMEOUT)); + + if (!(buf[0] >> 7) & 0x01) { + ret = -ETIMEDOUT; + goto err_mutex_unlock; + } else { + ret = 0; + } + +err_mutex_unlock: + mutex_unlock(&s->i2c_mutex); + if (ret) + goto err; + + return 0; +err: + dev_dbg(&s->client->dev, "%s: failed=%d\n", __func__, ret); + return ret; +} + +static int si2157_init(struct dvb_frontend *fe) +{ + struct si2157 *s = fe->tuner_priv; + + dev_dbg(&s->client->dev, "%s:\n", __func__); + + s->active = true; + + return 0; +} + +static int si2157_sleep(struct dvb_frontend *fe) +{ + struct si2157 *s = fe->tuner_priv; + + dev_dbg(&s->client->dev, "%s:\n", __func__); + + s->active = false; + + return 0; +} + +static int si2157_set_params(struct dvb_frontend *fe) +{ + struct si2157 *s = fe->tuner_priv; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + int ret; + struct si2157_cmd cmd; + + dev_dbg(&s->client->dev, + "%s: delivery_system=%d frequency=%u bandwidth_hz=%u\n", + __func__, c->delivery_system, c->frequency, + c->bandwidth_hz); + + if (!s->active) { + ret = -EAGAIN; + goto err; + } + + /* configure? */ + cmd.args[0] = 0xc0; + cmd.args[1] = 0x00; + cmd.args[2] = 0x0c; + cmd.args[3] = 0x00; + cmd.args[4] = 0x00; + cmd.args[5] = 0x01; + cmd.args[6] = 0x01; + cmd.args[7] = 0x01; + cmd.args[8] = 0x01; + cmd.args[9] = 0x01; + cmd.args[10] = 0x01; + cmd.args[11] = 0x02; + cmd.args[12] = 0x00; + cmd.args[13] = 0x00; + cmd.args[14] = 0x01; + cmd.len = 15; + ret = si2157_cmd_execute(s, &cmd); + if (ret) + goto err; + + cmd.args[0] = 0x02; + cmd.len = 1; + ret = si2157_cmd_execute(s, &cmd); + if (ret) + goto err; + + cmd.args[0] = 0x01; + cmd.args[1] = 0x01; + cmd.len = 2; + ret = si2157_cmd_execute(s, &cmd); + if (ret) + goto err; + + /* set frequency */ + cmd.args[0] = 0x41; + cmd.args[1] = 0x00; + cmd.args[2] = 0x00; + cmd.args[3] = 0x00; + cmd.args[4] = (c->frequency >> 0) & 0xff; + cmd.args[5] = (c->frequency >> 8) & 0xff; + cmd.args[6] = (c->frequency >> 16) & 0xff; + cmd.args[7] = (c->frequency >> 24) & 0xff; + cmd.len = 8; + ret = si2157_cmd_execute(s, &cmd); + if (ret) + goto err; + + return 0; +err: + dev_dbg(&s->client->dev, "%s: failed=%d\n", __func__, ret); + return ret; +} + +static const struct dvb_tuner_ops si2157_tuner_ops = { + .info = { + .name = "Silicon Labs Si2157", + .frequency_min = 110000000, + .frequency_max = 862000000, + }, + + .init = si2157_init, + .sleep = si2157_sleep, + .set_params = si2157_set_params, +}; + +static int si2157_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct si2157_config *cfg = client->dev.platform_data; + struct dvb_frontend *fe = cfg->fe; + struct si2157 *s; + struct si2157_cmd cmd; + int ret; + + s = kzalloc(sizeof(struct si2157), GFP_KERNEL); + if (!s) { + ret = -ENOMEM; + dev_err(&client->dev, "%s: kzalloc() failed\n", KBUILD_MODNAME); + goto err; + } + + s->client = client; + s->fe = cfg->fe; + mutex_init(&s->i2c_mutex); + + /* check if the tuner is there */ + cmd.len = 0; + ret = si2157_cmd_execute(s, &cmd); + if (ret) + goto err; + + fe->tuner_priv = s; + memcpy(&fe->ops.tuner_ops, &si2157_tuner_ops, + sizeof(struct dvb_tuner_ops)); + + i2c_set_clientdata(client, s); + + dev_info(&s->client->dev, + "%s: Silicon Labs Si2157 successfully attached\n", + KBUILD_MODNAME); + return 0; +err: + dev_dbg(&client->dev, "%s: failed=%d\n", __func__, ret); + kfree(s); + + return ret; +} + +static int si2157_remove(struct i2c_client *client) +{ + struct si2157 *s = i2c_get_clientdata(client); + struct dvb_frontend *fe = s->fe; + + dev_dbg(&client->dev, "%s:\n", __func__); + + memset(&fe->ops.tuner_ops, 0, sizeof(struct dvb_tuner_ops)); + fe->tuner_priv = NULL; + kfree(s); + + return 0; +} + +static const struct i2c_device_id si2157_id[] = { + {"si2157", 0}, + {} +}; +MODULE_DEVICE_TABLE(i2c, si2157_id); + +static struct i2c_driver si2157_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "si2157", + }, + .probe = si2157_probe, + .remove = si2157_remove, + .id_table = si2157_id, +}; + +module_i2c_driver(si2157_driver); + +MODULE_DESCRIPTION("Silicon Labs Si2157 silicon tuner driver"); +MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/tuners/si2157.h b/drivers/media/tuners/si2157.h new file mode 100644 index 000000000000..f469a092b66b --- /dev/null +++ b/drivers/media/tuners/si2157.h @@ -0,0 +1,34 @@ +/* + * Silicon Labs Si2157 silicon tuner 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 SI2157_H +#define SI2157_H + +#include <linux/kconfig.h> +#include "dvb_frontend.h" + +/* + * I2C address + * 0x60 + */ +struct si2157_config { + /* + * frontend + */ + struct dvb_frontend *fe; +}; + +#endif diff --git a/drivers/media/tuners/si2157_priv.h b/drivers/media/tuners/si2157_priv.h new file mode 100644 index 000000000000..6cc6c6fdab7a --- /dev/null +++ b/drivers/media/tuners/si2157_priv.h @@ -0,0 +1,37 @@ +/* + * Silicon Labs Si2157 silicon tuner 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 SI2157_PRIV_H +#define SI2157_PRIV_H + +#include "si2157.h" + +/* state struct */ +struct si2157 { + struct mutex i2c_mutex; + struct i2c_client *client; + struct dvb_frontend *fe; + bool active; +}; + +/* firmare command struct */ +#define SI2157_ARGLEN 30 +struct si2157_cmd { + u8 args[SI2157_ARGLEN]; + unsigned len; +}; + +#endif diff --git a/drivers/media/tuners/xc5000.c b/drivers/media/tuners/xc5000.c index 5cd09a681b6a..2b3d514be672 100644 --- a/drivers/media/tuners/xc5000.c +++ b/drivers/media/tuners/xc5000.c @@ -25,6 +25,7 @@ #include <linux/moduleparam.h> #include <linux/videodev2.h> #include <linux/delay.h> +#include <linux/workqueue.h> #include <linux/dvb/frontend.h> #include <linux/i2c.h> @@ -65,26 +66,25 @@ struct xc5000_priv { u16 pll_register_no; u8 init_status_supported; u8 fw_checksum_supported; + + struct dvb_frontend *fe; + struct delayed_work timer_sleep; }; /* Misc Defines */ #define MAX_TV_STANDARD 24 #define XC_MAX_I2C_WRITE_LENGTH 64 +/* Time to suspend after the .sleep callback is called */ +#define XC5000_SLEEP_TIME 5000 /* ms */ + /* Signal Types */ #define XC_RF_MODE_AIR 0 #define XC_RF_MODE_CABLE 1 -/* Result codes */ -#define XC_RESULT_SUCCESS 0 -#define XC_RESULT_RESET_FAILURE 1 -#define XC_RESULT_I2C_WRITE_FAILURE 2 -#define XC_RESULT_I2C_READ_FAILURE 3 -#define XC_RESULT_OUT_OF_RANGE 5 - /* Product id */ #define XC_PRODUCT_ID_FW_NOT_LOADED 0x2000 -#define XC_PRODUCT_ID_FW_LOADED 0x1388 +#define XC_PRODUCT_ID_FW_LOADED 0x1388 /* Registers */ #define XREG_INIT 0x00 @@ -152,16 +152,16 @@ struct xc5000_priv { */ struct XC_TV_STANDARD { - char *Name; - u16 AudioMode; - u16 VideoMode; + char *name; + u16 audio_mode; + u16 video_mode; }; /* Tuner standards */ #define MN_NTSC_PAL_BTSC 0 #define MN_NTSC_PAL_A2 1 #define MN_NTSC_PAL_EIAJ 2 -#define MN_NTSC_PAL_Mono 3 +#define MN_NTSC_PAL_MONO 3 #define BG_PAL_A2 4 #define BG_PAL_NICAM 5 #define BG_PAL_MONO 6 @@ -171,19 +171,19 @@ struct XC_TV_STANDARD { #define DK_PAL_NICAM 10 #define DK_PAL_MONO 11 #define DK_SECAM_A2DK1 12 -#define DK_SECAM_A2LDK3 13 -#define DK_SECAM_A2MONO 14 +#define DK_SECAM_A2LDK3 13 +#define DK_SECAM_A2MONO 14 #define L_SECAM_NICAM 15 #define LC_SECAM_NICAM 16 #define DTV6 17 #define DTV8 18 #define DTV7_8 19 #define DTV7 20 -#define FM_Radio_INPUT2 21 -#define FM_Radio_INPUT1 22 -#define FM_Radio_INPUT1_MONO 23 +#define FM_RADIO_INPUT2 21 +#define FM_RADIO_INPUT1 22 +#define FM_RADIO_INPUT1_MONO 23 -static struct XC_TV_STANDARD XC5000_Standard[MAX_TV_STANDARD] = { +static struct XC_TV_STANDARD xc5000_standard[MAX_TV_STANDARD] = { {"M/N-NTSC/PAL-BTSC", 0x0400, 0x8020}, {"M/N-NTSC/PAL-A2", 0x0600, 0x8020}, {"M/N-NTSC/PAL-EIAJ", 0x0440, 0x8020}, @@ -249,7 +249,7 @@ static inline const struct xc5000_fw_cfg *xc5000_assign_firmware(int chip_id) static int xc_load_fw_and_init_tuner(struct dvb_frontend *fe, int force); static int xc5000_is_firmware_loaded(struct dvb_frontend *fe); static int xc5000_readreg(struct xc5000_priv *priv, u16 reg, u16 *val); -static int xc5000_TunerReset(struct dvb_frontend *fe); +static int xc5000_tuner_reset(struct dvb_frontend *fe); static int xc_send_i2c_data(struct xc5000_priv *priv, u8 *buf, int len) { @@ -258,9 +258,9 @@ static int xc_send_i2c_data(struct xc5000_priv *priv, u8 *buf, int len) if (i2c_transfer(priv->i2c_props.adap, &msg, 1) != 1) { printk(KERN_ERR "xc5000: I2C write failed (len=%i)\n", len); - return XC_RESULT_I2C_WRITE_FAILURE; + return -EREMOTEIO; } - return XC_RESULT_SUCCESS; + return 0; } #if 0 @@ -297,15 +297,10 @@ static int xc5000_readreg(struct xc5000_priv *priv, u16 reg, u16 *val) } *val = (bval[0] << 8) | bval[1]; - return XC_RESULT_SUCCESS; -} - -static void xc_wait(int wait_ms) -{ - msleep(wait_ms); + return 0; } -static int xc5000_TunerReset(struct dvb_frontend *fe) +static int xc5000_tuner_reset(struct dvb_frontend *fe) { struct xc5000_priv *priv = fe->tuner_priv; int ret; @@ -320,43 +315,43 @@ static int xc5000_TunerReset(struct dvb_frontend *fe) XC5000_TUNER_RESET, 0); if (ret) { printk(KERN_ERR "xc5000: reset failed\n"); - return XC_RESULT_RESET_FAILURE; + return ret; } } else { printk(KERN_ERR "xc5000: no tuner reset callback function, fatal\n"); - return XC_RESULT_RESET_FAILURE; + return -EINVAL; } - return XC_RESULT_SUCCESS; + return 0; } -static int xc_write_reg(struct xc5000_priv *priv, u16 regAddr, u16 i2cData) +static int xc_write_reg(struct xc5000_priv *priv, u16 reg_addr, u16 i2c_data) { u8 buf[4]; - int WatchDogTimer = 100; + int watch_dog_timer = 100; int result; - buf[0] = (regAddr >> 8) & 0xFF; - buf[1] = regAddr & 0xFF; - buf[2] = (i2cData >> 8) & 0xFF; - buf[3] = i2cData & 0xFF; + buf[0] = (reg_addr >> 8) & 0xFF; + buf[1] = reg_addr & 0xFF; + buf[2] = (i2c_data >> 8) & 0xFF; + buf[3] = i2c_data & 0xFF; result = xc_send_i2c_data(priv, buf, 4); - if (result == XC_RESULT_SUCCESS) { + if (result == 0) { /* wait for busy flag to clear */ - while ((WatchDogTimer > 0) && (result == XC_RESULT_SUCCESS)) { + while ((watch_dog_timer > 0) && (result == 0)) { result = xc5000_readreg(priv, XREG_BUSY, (u16 *)buf); - if (result == XC_RESULT_SUCCESS) { + if (result == 0) { if ((buf[0] == 0) && (buf[1] == 0)) { /* busy flag cleared */ break; } else { - xc_wait(5); /* wait 5 ms */ - WatchDogTimer--; + msleep(5); /* wait 5 ms */ + watch_dog_timer--; } } } } - if (WatchDogTimer <= 0) - result = XC_RESULT_I2C_WRITE_FAILURE; + if (watch_dog_timer <= 0) + result = -EREMOTEIO; return result; } @@ -375,13 +370,13 @@ static int xc_load_i2c_sequence(struct dvb_frontend *fe, const u8 *i2c_sequence) len = i2c_sequence[index] * 256 + i2c_sequence[index+1]; if (len == 0x0000) { /* RESET command */ - result = xc5000_TunerReset(fe); + result = xc5000_tuner_reset(fe); index += 2; - if (result != XC_RESULT_SUCCESS) + if (result != 0) return result; } else if (len & 0x8000) { /* WAIT command */ - xc_wait(len & 0x7FFF); + msleep(len & 0x7FFF); index += 2; } else { /* Send i2c data whilst ensuring individual transactions @@ -404,7 +399,7 @@ static int xc_load_i2c_sequence(struct dvb_frontend *fe, const u8 *i2c_sequence) result = xc_send_i2c_data(priv, buf, nbytes_to_send); - if (result != XC_RESULT_SUCCESS) + if (result != 0) return result; pos += nbytes_to_send - 2; @@ -412,7 +407,7 @@ static int xc_load_i2c_sequence(struct dvb_frontend *fe, const u8 *i2c_sequence) index += len; } } - return XC_RESULT_SUCCESS; + return 0; } static int xc_initialize(struct xc5000_priv *priv) @@ -421,29 +416,29 @@ static int xc_initialize(struct xc5000_priv *priv) return xc_write_reg(priv, XREG_INIT, 0); } -static int xc_SetTVStandard(struct xc5000_priv *priv, - u16 VideoMode, u16 AudioMode, u8 RadioMode) +static int xc_set_tv_standard(struct xc5000_priv *priv, + u16 video_mode, u16 audio_mode, u8 radio_mode) { int ret; - dprintk(1, "%s(0x%04x,0x%04x)\n", __func__, VideoMode, AudioMode); - if (RadioMode) { + dprintk(1, "%s(0x%04x,0x%04x)\n", __func__, video_mode, audio_mode); + if (radio_mode) { dprintk(1, "%s() Standard = %s\n", __func__, - XC5000_Standard[RadioMode].Name); + xc5000_standard[radio_mode].name); } else { dprintk(1, "%s() Standard = %s\n", __func__, - XC5000_Standard[priv->video_standard].Name); + xc5000_standard[priv->video_standard].name); } - ret = xc_write_reg(priv, XREG_VIDEO_MODE, VideoMode); - if (ret == XC_RESULT_SUCCESS) - ret = xc_write_reg(priv, XREG_AUDIO_MODE, AudioMode); + ret = xc_write_reg(priv, XREG_VIDEO_MODE, video_mode); + if (ret == 0) + ret = xc_write_reg(priv, XREG_AUDIO_MODE, audio_mode); return ret; } -static int xc_SetSignalSource(struct xc5000_priv *priv, u16 rf_mode) +static int xc_set_signal_source(struct xc5000_priv *priv, u16 rf_mode) { dprintk(1, "%s(%d) Source = %s\n", __func__, rf_mode, rf_mode == XC_RF_MODE_AIR ? "ANTENNA" : "CABLE"); @@ -459,7 +454,7 @@ static int xc_SetSignalSource(struct xc5000_priv *priv, u16 rf_mode) static const struct dvb_tuner_ops xc5000_tuner_ops; -static int xc_set_RF_frequency(struct xc5000_priv *priv, u32 freq_hz) +static int xc_set_rf_frequency(struct xc5000_priv *priv, u32 freq_hz) { u16 freq_code; @@ -467,7 +462,7 @@ static int xc_set_RF_frequency(struct xc5000_priv *priv, u32 freq_hz) if ((freq_hz > xc5000_tuner_ops.info.frequency_max) || (freq_hz < xc5000_tuner_ops.info.frequency_min)) - return XC_RESULT_OUT_OF_RANGE; + return -EINVAL; freq_code = (u16)(freq_hz / 15625); @@ -488,7 +483,7 @@ static int xc_set_IF_frequency(struct xc5000_priv *priv, u32 freq_khz) } -static int xc_get_ADC_Envelope(struct xc5000_priv *priv, u16 *adc_envelope) +static int xc_get_adc_envelope(struct xc5000_priv *priv, u16 *adc_envelope) { return xc5000_readreg(priv, XREG_ADC_ENV, adc_envelope); } @@ -496,14 +491,14 @@ static int xc_get_ADC_Envelope(struct xc5000_priv *priv, u16 *adc_envelope) static int xc_get_frequency_error(struct xc5000_priv *priv, u32 *freq_error_hz) { int result; - u16 regData; + u16 reg_data; u32 tmp; - result = xc5000_readreg(priv, XREG_FREQ_ERROR, ®Data); - if (result != XC_RESULT_SUCCESS) + result = xc5000_readreg(priv, XREG_FREQ_ERROR, ®_data); + if (result != 0) return result; - tmp = (u32)regData; + tmp = (u32)reg_data; (*freq_error_hz) = (tmp * 15625) / 1000; return result; } @@ -521,7 +516,7 @@ static int xc_get_version(struct xc5000_priv *priv, int result; result = xc5000_readreg(priv, XREG_VERSION, &data); - if (result != XC_RESULT_SUCCESS) + if (result != 0) return result; (*hw_majorversion) = (data >> 12) & 0x0F; @@ -539,14 +534,14 @@ static int xc_get_buildversion(struct xc5000_priv *priv, u16 *buildrev) static int xc_get_hsync_freq(struct xc5000_priv *priv, u32 *hsync_freq_hz) { - u16 regData; + u16 reg_data; int result; - result = xc5000_readreg(priv, XREG_HSYNC_FREQ, ®Data); - if (result != XC_RESULT_SUCCESS) + result = xc5000_readreg(priv, XREG_HSYNC_FREQ, ®_data); + if (result != 0) return result; - (*hsync_freq_hz) = ((regData & 0x0fff) * 763)/100; + (*hsync_freq_hz) = ((reg_data & 0x0fff) * 763)/100; return result; } @@ -570,19 +565,19 @@ static int xc_get_totalgain(struct xc5000_priv *priv, u16 *totalgain) return xc5000_readreg(priv, XREG_TOTALGAIN, totalgain); } -static u16 WaitForLock(struct xc5000_priv *priv) +static u16 wait_for_lock(struct xc5000_priv *priv) { - u16 lockState = 0; - int watchDogCount = 40; - - while ((lockState == 0) && (watchDogCount > 0)) { - xc_get_lock_status(priv, &lockState); - if (lockState != 1) { - xc_wait(5); - watchDogCount--; + u16 lock_state = 0; + int watch_dog_count = 40; + + while ((lock_state == 0) && (watch_dog_count > 0)) { + xc_get_lock_status(priv, &lock_state); + if (lock_state != 1) { + msleep(5); + watch_dog_count--; } } - return lockState; + return lock_state; } #define XC_TUNE_ANALOG 0 @@ -593,11 +588,11 @@ static int xc_tune_channel(struct xc5000_priv *priv, u32 freq_hz, int mode) dprintk(1, "%s(%u)\n", __func__, freq_hz); - if (xc_set_RF_frequency(priv, freq_hz) != XC_RESULT_SUCCESS) + if (xc_set_rf_frequency(priv, freq_hz) != 0) return 0; if (mode == XC_TUNE_ANALOG) { - if (WaitForLock(priv) == 1) + if (wait_for_lock(priv) == 1) found = 1; } @@ -607,7 +602,7 @@ static int xc_tune_channel(struct xc5000_priv *priv, u32 freq_hz, int mode) static int xc_set_xtal(struct dvb_frontend *fe) { struct xc5000_priv *priv = fe->tuner_priv; - int ret = XC_RESULT_SUCCESS; + int ret = 0; switch (priv->chip_id) { default: @@ -649,23 +644,22 @@ static int xc5000_fwupload(struct dvb_frontend *fe) priv->i2c_props.adap->dev.parent); if (ret) { printk(KERN_ERR "xc5000: Upload failed. (file not found?)\n"); - ret = XC_RESULT_RESET_FAILURE; goto out; } else { printk(KERN_DEBUG "xc5000: firmware read %Zu bytes.\n", fw->size); - ret = XC_RESULT_SUCCESS; + ret = 0; } if (fw->size != desired_fw->size) { printk(KERN_ERR "xc5000: firmware incorrect size\n"); - ret = XC_RESULT_RESET_FAILURE; + ret = -EINVAL; } else { printk(KERN_INFO "xc5000: firmware uploading...\n"); ret = xc_load_i2c_sequence(fe, fw->data); - if (XC_RESULT_SUCCESS == ret) + if (0 == ret) ret = xc_set_xtal(fe); - if (XC_RESULT_SUCCESS == ret) + if (0 == ret) printk(KERN_INFO "xc5000: firmware upload complete...\n"); else printk(KERN_ERR "xc5000: firmware upload failed...\n"); @@ -695,9 +689,9 @@ static void xc_debug_dump(struct xc5000_priv *priv) * Frame Lines needs two frame times after initial lock * before it is valid. */ - xc_wait(100); + msleep(100); - xc_get_ADC_Envelope(priv, &adc_envelope); + xc_get_adc_envelope(priv, &adc_envelope); dprintk(1, "*** ADC envelope (0-1023) = %d\n", adc_envelope); xc_get_frequency_error(priv, &freq_error_hz); @@ -744,7 +738,7 @@ static int xc5000_set_params(struct dvb_frontend *fe) u32 freq = fe->dtv_property_cache.frequency; u32 delsys = fe->dtv_property_cache.delivery_system; - if (xc_load_fw_and_init_tuner(fe, 0) != XC_RESULT_SUCCESS) { + if (xc_load_fw_and_init_tuner(fe, 0) != 0) { dprintk(1, "Unable to load firmware and init tuner\n"); return -EINVAL; } @@ -820,24 +814,24 @@ static int xc5000_set_params(struct dvb_frontend *fe) dprintk(1, "%s() frequency=%d (compensated to %d)\n", __func__, freq, priv->freq_hz); - ret = xc_SetSignalSource(priv, priv->rf_mode); - if (ret != XC_RESULT_SUCCESS) { + ret = xc_set_signal_source(priv, priv->rf_mode); + if (ret != 0) { printk(KERN_ERR - "xc5000: xc_SetSignalSource(%d) failed\n", + "xc5000: xc_set_signal_source(%d) failed\n", priv->rf_mode); return -EREMOTEIO; } - ret = xc_SetTVStandard(priv, - XC5000_Standard[priv->video_standard].VideoMode, - XC5000_Standard[priv->video_standard].AudioMode, 0); - if (ret != XC_RESULT_SUCCESS) { - printk(KERN_ERR "xc5000: xc_SetTVStandard failed\n"); + ret = xc_set_tv_standard(priv, + xc5000_standard[priv->video_standard].video_mode, + xc5000_standard[priv->video_standard].audio_mode, 0); + if (ret != 0) { + printk(KERN_ERR "xc5000: xc_set_tv_standard failed\n"); return -EREMOTEIO; } ret = xc_set_IF_frequency(priv, priv->if_khz); - if (ret != XC_RESULT_SUCCESS) { + if (ret != 0) { printk(KERN_ERR "xc5000: xc_Set_IF_frequency(%d) failed\n", priv->if_khz); return -EIO; @@ -862,15 +856,15 @@ static int xc5000_is_firmware_loaded(struct dvb_frontend *fe) u16 id; ret = xc5000_readreg(priv, XREG_PRODUCT_ID, &id); - if (ret == XC_RESULT_SUCCESS) { + if (ret == 0) { if (id == XC_PRODUCT_ID_FW_NOT_LOADED) - ret = XC_RESULT_RESET_FAILURE; + ret = -ENOENT; else - ret = XC_RESULT_SUCCESS; + ret = 0; } dprintk(1, "%s() returns %s id = 0x%x\n", __func__, - ret == XC_RESULT_SUCCESS ? "True" : "False", id); + ret == 0 ? "True" : "False", id); return ret; } @@ -936,19 +930,19 @@ static int xc5000_set_tv_freq(struct dvb_frontend *fe, } tune_channel: - ret = xc_SetSignalSource(priv, priv->rf_mode); - if (ret != XC_RESULT_SUCCESS) { + ret = xc_set_signal_source(priv, priv->rf_mode); + if (ret != 0) { printk(KERN_ERR - "xc5000: xc_SetSignalSource(%d) failed\n", + "xc5000: xc_set_signal_source(%d) failed\n", priv->rf_mode); return -EREMOTEIO; } - ret = xc_SetTVStandard(priv, - XC5000_Standard[priv->video_standard].VideoMode, - XC5000_Standard[priv->video_standard].AudioMode, 0); - if (ret != XC_RESULT_SUCCESS) { - printk(KERN_ERR "xc5000: xc_SetTVStandard failed\n"); + ret = xc_set_tv_standard(priv, + xc5000_standard[priv->video_standard].video_mode, + xc5000_standard[priv->video_standard].audio_mode, 0); + if (ret != 0) { + printk(KERN_ERR "xc5000: xc_set_tv_standard failed\n"); return -EREMOTEIO; } @@ -966,7 +960,7 @@ tune_channel: /* PLL is unlocked, force reload of the firmware */ dprintk(1, "xc5000: PLL not locked (0x%x). Reloading...\n", pll_lock_status); - if (xc_load_fw_and_init_tuner(fe, 1) != XC_RESULT_SUCCESS) { + if (xc_load_fw_and_init_tuner(fe, 1) != 0) { printk(KERN_ERR "xc5000: Unable to reload fw\n"); return -EREMOTEIO; } @@ -993,11 +987,11 @@ static int xc5000_set_radio_freq(struct dvb_frontend *fe, } if (priv->radio_input == XC5000_RADIO_FM1) - radio_input = FM_Radio_INPUT1; + radio_input = FM_RADIO_INPUT1; else if (priv->radio_input == XC5000_RADIO_FM2) - radio_input = FM_Radio_INPUT2; + radio_input = FM_RADIO_INPUT2; else if (priv->radio_input == XC5000_RADIO_FM1_MONO) - radio_input = FM_Radio_INPUT1_MONO; + radio_input = FM_RADIO_INPUT1_MONO; else { dprintk(1, "%s() unknown radio input %d\n", __func__, priv->radio_input); @@ -1008,18 +1002,18 @@ static int xc5000_set_radio_freq(struct dvb_frontend *fe, priv->rf_mode = XC_RF_MODE_AIR; - ret = xc_SetTVStandard(priv, XC5000_Standard[radio_input].VideoMode, - XC5000_Standard[radio_input].AudioMode, radio_input); + ret = xc_set_tv_standard(priv, xc5000_standard[radio_input].video_mode, + xc5000_standard[radio_input].audio_mode, radio_input); - if (ret != XC_RESULT_SUCCESS) { - printk(KERN_ERR "xc5000: xc_SetTVStandard failed\n"); + if (ret != 0) { + printk(KERN_ERR "xc5000: xc_set_tv_standard failed\n"); return -EREMOTEIO; } - ret = xc_SetSignalSource(priv, priv->rf_mode); - if (ret != XC_RESULT_SUCCESS) { + ret = xc_set_signal_source(priv, priv->rf_mode); + if (ret != 0) { printk(KERN_ERR - "xc5000: xc_SetSignalSource(%d) failed\n", + "xc5000: xc_set_signal_source(%d) failed\n", priv->rf_mode); return -EREMOTEIO; } @@ -1044,7 +1038,7 @@ static int xc5000_set_analog_params(struct dvb_frontend *fe, if (priv->i2c_props.adap == NULL) return -EINVAL; - if (xc_load_fw_and_init_tuner(fe, 0) != XC_RESULT_SUCCESS) { + if (xc_load_fw_and_init_tuner(fe, 0) != 0) { dprintk(1, "Unable to load firmware and init tuner\n"); return -EINVAL; } @@ -1105,23 +1099,25 @@ static int xc5000_get_status(struct dvb_frontend *fe, u32 *status) static int xc_load_fw_and_init_tuner(struct dvb_frontend *fe, int force) { struct xc5000_priv *priv = fe->tuner_priv; - int ret = XC_RESULT_SUCCESS; + int ret = 0; u16 pll_lock_status; u16 fw_ck; - if (force || xc5000_is_firmware_loaded(fe) != XC_RESULT_SUCCESS) { + cancel_delayed_work(&priv->timer_sleep); + + if (force || xc5000_is_firmware_loaded(fe) != 0) { fw_retry: ret = xc5000_fwupload(fe); - if (ret != XC_RESULT_SUCCESS) + if (ret != 0) return ret; msleep(20); if (priv->fw_checksum_supported) { if (xc5000_readreg(priv, XREG_FW_CHECKSUM, &fw_ck) - != XC_RESULT_SUCCESS) { + != 0) { dprintk(1, "%s() FW checksum reading failed.\n", __func__); goto fw_retry; @@ -1137,7 +1133,7 @@ fw_retry: /* Start the tuner self-calibration process */ ret |= xc_initialize(priv); - if (ret != XC_RESULT_SUCCESS) + if (ret != 0) goto fw_retry; /* Wait for calibration to complete. @@ -1145,10 +1141,10 @@ fw_retry: * I2C transactions until calibration is complete. This way we * don't have to rely on clock stretching working. */ - xc_wait(100); + msleep(100); if (priv->init_status_supported) { - if (xc5000_readreg(priv, XREG_INIT_STATUS, &fw_ck) != XC_RESULT_SUCCESS) { + if (xc5000_readreg(priv, XREG_INIT_STATUS, &fw_ck) != 0) { dprintk(1, "%s() FW failed reading init status.\n", __func__); goto fw_retry; @@ -1177,27 +1173,39 @@ fw_retry: return ret; } -static int xc5000_sleep(struct dvb_frontend *fe) +static void xc5000_do_timer_sleep(struct work_struct *timer_sleep) { + struct xc5000_priv *priv =container_of(timer_sleep, struct xc5000_priv, + timer_sleep.work); + struct dvb_frontend *fe = priv->fe; int ret; dprintk(1, "%s()\n", __func__); - /* Avoid firmware reload on slow devices */ - if (no_poweroff) - return 0; - /* According to Xceive technical support, the "powerdown" register was removed in newer versions of the firmware. The "supported" way to sleep the tuner is to pull the reset pin low for 10ms */ - ret = xc5000_TunerReset(fe); - if (ret != XC_RESULT_SUCCESS) { + ret = xc5000_tuner_reset(fe); + if (ret != 0) printk(KERN_ERR "xc5000: %s() unable to shutdown tuner\n", __func__); - return -EREMOTEIO; - } else - return XC_RESULT_SUCCESS; +} + +static int xc5000_sleep(struct dvb_frontend *fe) +{ + struct xc5000_priv *priv = fe->tuner_priv; + + dprintk(1, "%s()\n", __func__); + + /* Avoid firmware reload on slow devices */ + if (no_poweroff) + return 0; + + schedule_delayed_work(&priv->timer_sleep, + msecs_to_jiffies(XC5000_SLEEP_TIME)); + + return 0; } static int xc5000_init(struct dvb_frontend *fe) @@ -1205,7 +1213,7 @@ static int xc5000_init(struct dvb_frontend *fe) struct xc5000_priv *priv = fe->tuner_priv; dprintk(1, "%s()\n", __func__); - if (xc_load_fw_and_init_tuner(fe, 0) != XC_RESULT_SUCCESS) { + if (xc_load_fw_and_init_tuner(fe, 0) != 0) { printk(KERN_ERR "xc5000: Unable to initialise tuner\n"); return -EREMOTEIO; } @@ -1224,8 +1232,10 @@ static int xc5000_release(struct dvb_frontend *fe) mutex_lock(&xc5000_list_mutex); - if (priv) + if (priv) { + cancel_delayed_work(&priv->timer_sleep); hybrid_tuner_release_state(priv); + } mutex_unlock(&xc5000_list_mutex); @@ -1297,6 +1307,8 @@ struct dvb_frontend *xc5000_attach(struct dvb_frontend *fe, /* new tuner instance */ priv->bandwidth = 6000000; fe->tuner_priv = priv; + priv->fe = fe; + INIT_DELAYED_WORK(&priv->timer_sleep, xc5000_do_timer_sleep); break; default: /* existing tuner instance */ @@ -1327,7 +1339,7 @@ struct dvb_frontend *xc5000_attach(struct dvb_frontend *fe, /* Check if firmware has been loaded. It is possible that another instance of the driver has loaded the firmware. */ - if (xc5000_readreg(priv, XREG_PRODUCT_ID, &id) != XC_RESULT_SUCCESS) + if (xc5000_readreg(priv, XREG_PRODUCT_ID, &id) != 0) goto fail; switch (id) { diff --git a/drivers/media/usb/au0828/au0828-dvb.c b/drivers/media/usb/au0828/au0828-dvb.c index 4ae8b1074649..d8b5d9480279 100644 --- a/drivers/media/usb/au0828/au0828-dvb.c +++ b/drivers/media/usb/au0828/au0828-dvb.c @@ -114,16 +114,20 @@ static void urb_completion(struct urb *purb) int ptype = usb_pipetype(purb->pipe); unsigned char *ptr; - dprintk(2, "%s()\n", __func__); + dprintk(2, "%s: %d\n", __func__, purb->actual_length); - if (!dev) + if (!dev) { + dprintk(2, "%s: no dev!\n", __func__); return; + } - if (dev->urb_streaming == 0) + if (dev->urb_streaming == 0) { + dprintk(2, "%s: not streaming!\n", __func__); return; + } if (ptype != PIPE_BULK) { - printk(KERN_ERR "%s() Unsupported URB type %d\n", + printk(KERN_ERR "%s: Unsupported URB type %d\n", __func__, ptype); return; } @@ -252,8 +256,6 @@ static void au0828_stop_transport(struct au0828_dev *dev, int full_stop) au0828_write(dev, 0x60b, 0x00); } - - static int au0828_dvb_start_feed(struct dvb_demux_feed *feed) { struct dvb_demux *demux = feed->demux; @@ -296,6 +298,8 @@ static int au0828_dvb_stop_feed(struct dvb_demux_feed *feed) dprintk(1, "%s()\n", __func__); if (dvb) { + cancel_work_sync(&dev->restart_streaming); + mutex_lock(&dvb->lock); dvb->stop_count++; dprintk(1, "%s(), start_count: %d, stop_count: %d\n", __func__, @@ -338,6 +342,41 @@ static void au0828_restart_dvb_streaming(struct work_struct *work) mutex_unlock(&dvb->lock); } +static int au0828_set_frontend(struct dvb_frontend *fe) +{ + struct au0828_dev *dev = fe->dvb->priv; + struct au0828_dvb *dvb = &dev->dvb; + int ret, was_streaming; + + mutex_lock(&dvb->lock); + was_streaming = dev->urb_streaming; + if (was_streaming) { + au0828_stop_transport(dev, 1); + + /* + * We can't hold a mutex here, as the restart_streaming + * kthread may also hold it. + */ + mutex_unlock(&dvb->lock); + cancel_work_sync(&dev->restart_streaming); + mutex_lock(&dvb->lock); + + stop_urb_transfer(dev); + } + mutex_unlock(&dvb->lock); + + ret = dvb->set_frontend(fe); + + if (was_streaming) { + mutex_lock(&dvb->lock); + au0828_start_transport(dev); + start_urb_transfer(dev); + mutex_unlock(&dvb->lock); + } + + return ret; +} + static int dvb_register(struct au0828_dev *dev) { struct au0828_dvb *dvb = &dev->dvb; @@ -382,6 +421,10 @@ static int dvb_register(struct au0828_dev *dev) goto fail_frontend; } + /* Hook dvb frontend */ + dvb->set_frontend = dvb->frontend->ops.set_frontend; + dvb->frontend->ops.set_frontend = au0828_set_frontend; + /* register demux stuff */ dvb->demux.dmx.capabilities = DMX_TS_FILTERING | DMX_SECTION_FILTERING | @@ -471,6 +514,8 @@ void au0828_dvb_unregister(struct au0828_dev *dev) if (dvb->frontend == NULL) return; + cancel_work_sync(&dev->restart_streaming); + dvb_net_release(&dvb->net); dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_mem); dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_hw); diff --git a/drivers/media/usb/au0828/au0828-video.c b/drivers/media/usb/au0828/au0828-video.c index f6154546b5c0..9038194513c5 100644 --- a/drivers/media/usb/au0828/au0828-video.c +++ b/drivers/media/usb/au0828/au0828-video.c @@ -1109,7 +1109,7 @@ static void au0828_init_tuner(struct au0828_dev *dev) /* If we've never sent the standard in tuner core, do so now. We don't do this at device probe because we don't want to incur the cost of a firmware load */ - v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_std, dev->std); + v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_std, dev->std); v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_frequency, &f); i2c_gate_ctrl(dev, 0); } @@ -1368,7 +1368,7 @@ static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id norm) have to make the au0828 bridge adjust the size of its capture buffer, which is currently hardcoded at 720x480 */ - v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_std, norm); + v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_std, norm); i2c_gate_ctrl(dev, 0); diff --git a/drivers/media/usb/au0828/au0828.h b/drivers/media/usb/au0828/au0828.h index 5439772c1551..7112b9d956fa 100644 --- a/drivers/media/usb/au0828/au0828.h +++ b/drivers/media/usb/au0828/au0828.h @@ -104,6 +104,8 @@ struct au0828_dvb { int feeding; int start_count; int stop_count; + + int (*set_frontend)(struct dvb_frontend *fe); }; enum au0828_stream_state { diff --git a/drivers/media/usb/cx231xx/cx231xx-417.c b/drivers/media/usb/cx231xx/cx231xx-417.c index 2f63029e7a36..30a0c69fb42f 100644 --- a/drivers/media/usb/cx231xx/cx231xx-417.c +++ b/drivers/media/usb/cx231xx/cx231xx-417.c @@ -1516,7 +1516,7 @@ static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id id) dev->ts1.height = 576; cx2341x_handler_set_50hz(&dev->mpeg_ctrl_handler, true); } - call_all(dev, core, s_std, dev->norm); + call_all(dev, video, s_std, dev->norm); /* do mode control overrides */ cx231xx_do_mode_ctrl_overrides(dev); diff --git a/drivers/media/usb/cx231xx/cx231xx-video.c b/drivers/media/usb/cx231xx/cx231xx-video.c index 990626101718..1f8751379e24 100644 --- a/drivers/media/usb/cx231xx/cx231xx-video.c +++ b/drivers/media/usb/cx231xx/cx231xx-video.c @@ -1009,7 +1009,7 @@ static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id norm) dev->width = 720; dev->height = (dev->norm & V4L2_STD_625_50) ? 576 : 480; - call_all(dev, core, s_std, dev->norm); + call_all(dev, video, s_std, dev->norm); /* We need to reset basic properties in the decoder related to resolution (since a standard change effects things like the number @@ -1108,7 +1108,7 @@ int cx231xx_s_input(struct file *file, void *priv, unsigned int i) /* There's a tuner, so reset the standard and put it on the last known frequency (since it was probably powered down until now */ - call_all(dev, core, s_std, dev->norm); + call_all(dev, video, s_std, dev->norm); } return 0; @@ -2099,7 +2099,7 @@ int cx231xx_register_analog_devices(struct cx231xx *dev) /* Set the initial input */ video_mux(dev, dev->video_input); - call_all(dev, core, s_std, dev->norm); + call_all(dev, video, s_std, dev->norm); v4l2_ctrl_handler_init(&dev->ctrl_handler, 10); v4l2_ctrl_handler_init(&dev->radio_ctrl_handler, 5); 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 de02db802ace..e35580618936 100644 --- a/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c +++ b/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c @@ -399,7 +399,7 @@ static int dvb_usb_stop_feed(struct dvb_demux_feed *dvbdmxfeed) /* clear 'streaming' status bit */ clear_bit(ADAP_STREAMING, &adap->state_bits); - smp_mb__after_clear_bit(); + smp_mb__after_atomic(); wake_up_bit(&adap->state_bits, ADAP_STREAMING); skip_feed_stop: @@ -550,7 +550,7 @@ static int dvb_usb_fe_init(struct dvb_frontend *fe) err: if (!adap->suspend_resume_active) { clear_bit(ADAP_INIT, &adap->state_bits); - smp_mb__after_clear_bit(); + smp_mb__after_atomic(); wake_up_bit(&adap->state_bits, ADAP_INIT); } @@ -591,7 +591,7 @@ err: if (!adap->suspend_resume_active) { adap->active_fe = -1; clear_bit(ADAP_SLEEP, &adap->state_bits); - smp_mb__after_clear_bit(); + smp_mb__after_atomic(); wake_up_bit(&adap->state_bits, ADAP_SLEEP); } diff --git a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c index dcbd392e6efc..a676e4452847 100644 --- a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c +++ b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c @@ -1537,6 +1537,12 @@ static const struct usb_device_id rtl28xxu_id_table[] = { &rtl2832u_props, "Crypto ReDi PC 50 A", NULL) }, { DVB_USB_DEVICE(USB_VID_KYE, 0x707f, &rtl2832u_props, "Genius TVGo DVB-T03", NULL) }, + { DVB_USB_DEVICE(USB_VID_KWORLD_2, 0xd395, + &rtl2832u_props, "Peak DVB-T USB", NULL) }, + { DVB_USB_DEVICE(USB_VID_KWORLD_2, USB_PID_SVEON_STV20_RTL2832U, + &rtl2832u_props, "Sveon STV20", NULL) }, + { DVB_USB_DEVICE(USB_VID_KWORLD_2, USB_PID_SVEON_STV27, + &rtl2832u_props, "Sveon STV27", NULL) }, /* RTL2832P devices: */ { DVB_USB_DEVICE(USB_VID_HANFTEK, 0x0131, diff --git a/drivers/media/usb/dvb-usb/az6027.c b/drivers/media/usb/dvb-usb/az6027.c index c11138ebf6fb..0df52ab32a7b 100644 --- a/drivers/media/usb/dvb-usb/az6027.c +++ b/drivers/media/usb/dvb-usb/az6027.c @@ -1088,6 +1088,7 @@ static struct usb_device_id az6027_usb_table[] = { { USB_DEVICE(USB_VID_TECHNISAT, USB_PID_TECHNISAT_USB2_HDCI_V1) }, { USB_DEVICE(USB_VID_TECHNISAT, USB_PID_TECHNISAT_USB2_HDCI_V2) }, { USB_DEVICE(USB_VID_ELGATO, USB_PID_ELGATO_EYETV_SAT) }, + { USB_DEVICE(USB_VID_ELGATO, USB_PID_ELGATO_EYETV_SAT_V2) }, { }, }; @@ -1136,7 +1137,7 @@ static struct dvb_usb_device_properties az6027_properties = { .i2c_algo = &az6027_i2c_algo, - .num_device_descs = 6, + .num_device_descs = 7, .devices = { { .name = "AZUREWAVE DVB-S/S2 USB2.0 (AZ6027)", @@ -1162,6 +1163,10 @@ static struct dvb_usb_device_properties az6027_properties = { .name = "Elgato EyeTV Sat", .cold_ids = { &az6027_usb_table[5], NULL }, .warm_ids = { NULL }, + }, { + .name = "Elgato EyeTV Sat", + .cold_ids = { &az6027_usb_table[6], NULL }, + .warm_ids = { NULL }, }, { NULL }, } diff --git a/drivers/media/usb/dvb-usb/dib0700.h b/drivers/media/usb/dvb-usb/dib0700.h index 637b6123f391..927617d95616 100644 --- a/drivers/media/usb/dvb-usb/dib0700.h +++ b/drivers/media/usb/dvb-usb/dib0700.h @@ -59,7 +59,7 @@ extern int dib0700_set_gpio(struct dvb_usb_device *, enum dib07x0_gpios gpio, u8 extern int dib0700_ctrl_clock(struct dvb_usb_device *d, u32 clk_MHz, u8 clock_out_gp3); extern int dib0700_ctrl_rd(struct dvb_usb_device *d, u8 *tx, u8 txlen, u8 *rx, u8 rxlen); extern int dib0700_download_firmware(struct usb_device *udev, const struct firmware *fw); -extern int dib0700_rc_setup(struct dvb_usb_device *d); +extern int dib0700_rc_setup(struct dvb_usb_device *d, struct usb_interface *intf); extern int dib0700_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff); extern struct i2c_algorithm dib0700_i2c_algo; extern int dib0700_identify_state(struct usb_device *udev, struct dvb_usb_device_properties *props, diff --git a/drivers/media/usb/dvb-usb/dib0700_core.c b/drivers/media/usb/dvb-usb/dib0700_core.c index bf2a908d74cf..c14285fa8271 100644 --- a/drivers/media/usb/dvb-usb/dib0700_core.c +++ b/drivers/media/usb/dvb-usb/dib0700_core.c @@ -754,17 +754,20 @@ resubmit: usb_submit_urb(purb, GFP_ATOMIC); } -int dib0700_rc_setup(struct dvb_usb_device *d) +int dib0700_rc_setup(struct dvb_usb_device *d, struct usb_interface *intf) { struct dib0700_state *st = d->priv; struct urb *purb; - int ret; + const struct usb_endpoint_descriptor *e; + int ret, rc_ep = 1; + unsigned int pipe = 0; /* Poll-based. Don't initialize bulk mode */ - if (st->fw_version < 0x10200) + if (st->fw_version < 0x10200 || !intf) return 0; /* Starting in firmware 1.20, the RC info is provided on a bulk pipe */ + purb = usb_alloc_urb(0, GFP_KERNEL); if (purb == NULL) { err("rc usb alloc urb failed"); @@ -779,9 +782,35 @@ int dib0700_rc_setup(struct dvb_usb_device *d) } purb->status = -EINPROGRESS; - usb_fill_bulk_urb(purb, d->udev, usb_rcvbulkpipe(d->udev, 1), - purb->transfer_buffer, RC_MSG_SIZE_V1_20, - dib0700_rc_urb_completion, d); + + /* + * Some devices like the Hauppauge NovaTD model 52009 use an interrupt + * endpoint, while others use a bulk one. + */ + e = &intf->altsetting[0].endpoint[rc_ep].desc; + if (usb_endpoint_dir_in(e)) { + if (usb_endpoint_xfer_bulk(e)) { + pipe = usb_rcvbulkpipe(d->udev, rc_ep); + usb_fill_bulk_urb(purb, d->udev, pipe, + purb->transfer_buffer, + RC_MSG_SIZE_V1_20, + dib0700_rc_urb_completion, d); + + } else if (usb_endpoint_xfer_int(e)) { + pipe = usb_rcvintpipe(d->udev, rc_ep); + usb_fill_int_urb(purb, d->udev, pipe, + purb->transfer_buffer, + RC_MSG_SIZE_V1_20, + dib0700_rc_urb_completion, d, 1); + } + } + + if (!pipe) { + err("There's no endpoint for remote controller"); + kfree(purb->transfer_buffer); + usb_free_urb(purb); + return 0; + } ret = usb_submit_urb(purb, GFP_ATOMIC); if (ret) { @@ -820,7 +849,7 @@ static int dib0700_probe(struct usb_interface *intf, else dev->props.rc.core.bulk_mode = false; - dib0700_rc_setup(dev); + dib0700_rc_setup(dev, intf); return 0; } diff --git a/drivers/media/usb/dvb-usb/dib0700_devices.c b/drivers/media/usb/dvb-usb/dib0700_devices.c index 829323e42ca0..10e0db8d1850 100644 --- a/drivers/media/usb/dvb-usb/dib0700_devices.c +++ b/drivers/media/usb/dvb-usb/dib0700_devices.c @@ -514,7 +514,7 @@ static int dib0700_rc_query_old_firmware(struct dvb_usb_device *d) /* info("%d: %2X %2X %2X %2X",dvb_usb_dib0700_ir_proto,(int)key[3-2],(int)key[3-3],(int)key[3-1],(int)key[3]); */ - dib0700_rc_setup(d); /* reset ir sensor data to prevent false events */ + dib0700_rc_setup(d, NULL); /* reset ir sensor data to prevent false events */ d->last_event = 0; switch (d->props.rc.core.protocol) { diff --git a/drivers/media/usb/dvb-usb/technisat-usb2.c b/drivers/media/usb/dvb-usb/technisat-usb2.c index 98d24aefb640..d947e0379008 100644 --- a/drivers/media/usb/dvb-usb/technisat-usb2.c +++ b/drivers/media/usb/dvb-usb/technisat-usb2.c @@ -214,10 +214,10 @@ static void technisat_usb2_frontend_reset(struct usb_device *udev) /* LED control */ enum technisat_usb2_led_state { - LED_OFF, - LED_BLINK, - LED_ON, - LED_UNDEFINED + TECH_LED_OFF, + TECH_LED_BLINK, + TECH_LED_ON, + TECH_LED_UNDEFINED }; static int technisat_usb2_set_led(struct dvb_usb_device *d, int red, enum technisat_usb2_led_state state) @@ -229,14 +229,14 @@ static int technisat_usb2_set_led(struct dvb_usb_device *d, int red, enum techni 0 }; - if (disable_led_control && state != LED_OFF) + if (disable_led_control && state != TECH_LED_OFF) return 0; switch (state) { - case LED_ON: + case TECH_LED_ON: led[1] = 0x82; break; - case LED_BLINK: + case TECH_LED_BLINK: led[1] = 0x82; if (red) { led[2] = 0x02; @@ -251,7 +251,7 @@ static int technisat_usb2_set_led(struct dvb_usb_device *d, int red, enum techni break; default: - case LED_OFF: + case TECH_LED_OFF: led[1] = 0x80; break; } @@ -310,11 +310,11 @@ static void technisat_usb2_green_led_control(struct work_struct *work) goto schedule; if (ber > 1000) - technisat_usb2_set_led(state->dev, 0, LED_BLINK); + technisat_usb2_set_led(state->dev, 0, TECH_LED_BLINK); else - technisat_usb2_set_led(state->dev, 0, LED_ON); + technisat_usb2_set_led(state->dev, 0, TECH_LED_ON); } else - technisat_usb2_set_led(state->dev, 0, LED_OFF); + technisat_usb2_set_led(state->dev, 0, TECH_LED_OFF); } schedule: @@ -365,9 +365,9 @@ static int technisat_usb2_power_ctrl(struct dvb_usb_device *d, int level) return 0; /* green led is turned off in any case - will be turned on when tuning */ - technisat_usb2_set_led(d, 0, LED_OFF); + technisat_usb2_set_led(d, 0, TECH_LED_OFF); /* red led is turned on all the time */ - technisat_usb2_set_led(d, 1, LED_ON); + technisat_usb2_set_led(d, 1, TECH_LED_ON); return 0; } @@ -667,7 +667,7 @@ static int technisat_usb2_rc_query(struct dvb_usb_device *d) return 0; if (!disable_led_control) - technisat_usb2_set_led(d, 1, LED_BLINK); + technisat_usb2_set_led(d, 1, TECH_LED_BLINK); return 0; } diff --git a/drivers/media/usb/em28xx/Kconfig b/drivers/media/usb/em28xx/Kconfig index d23a912096f7..f5d7198753c7 100644 --- a/drivers/media/usb/em28xx/Kconfig +++ b/drivers/media/usb/em28xx/Kconfig @@ -57,6 +57,8 @@ config VIDEO_EM28XX_DVB select DVB_M88DS3103 if MEDIA_SUBDRV_AUTOSELECT select MEDIA_TUNER_M88TS2022 if MEDIA_SUBDRV_AUTOSELECT select DVB_DRX39XYJ if MEDIA_SUBDRV_AUTOSELECT + select DVB_SI2168 if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_SI2157 if MEDIA_SUBDRV_AUTOSELECT ---help--- This adds support for DVB cards based on the Empiatech em28xx chips. diff --git a/drivers/media/usb/em28xx/em28xx-audio.c b/drivers/media/usb/em28xx/em28xx-audio.c index 342490f44ed2..e881ef7b6445 100644 --- a/drivers/media/usb/em28xx/em28xx-audio.c +++ b/drivers/media/usb/em28xx/em28xx-audio.c @@ -92,7 +92,7 @@ static void em28xx_audio_isocirq(struct urb *urb) if (dev->disconnected) { dprintk("device disconnected while streaming. URB status=%d.\n", urb->status); - atomic_set(&dev->stream_started, 0); + atomic_set(&dev->adev.stream_started, 0); return; } @@ -109,7 +109,7 @@ static void em28xx_audio_isocirq(struct urb *urb) break; } - if (atomic_read(&dev->stream_started) == 0) + if (atomic_read(&dev->adev.stream_started) == 0) return; if (dev->adev.capture_pcm_substream) { @@ -185,7 +185,7 @@ static int em28xx_init_audio_isoc(struct em28xx *dev) em28xx_errdev("submit of audio urb failed (error=%i)\n", errCode); em28xx_deinit_isoc_audio(dev); - atomic_set(&dev->stream_started, 0); + atomic_set(&dev->adev.stream_started, 0); return errCode; } @@ -332,9 +332,9 @@ static int snd_em28xx_pcm_close(struct snd_pcm_substream *substream) dev->mute = 1; mutex_lock(&dev->lock); dev->adev.users--; - if (atomic_read(&dev->stream_started) > 0) { - atomic_set(&dev->stream_started, 0); - schedule_work(&dev->wq_trigger); + if (atomic_read(&dev->adev.stream_started) > 0) { + atomic_set(&dev->adev.stream_started, 0); + schedule_work(&dev->adev.wq_trigger); } em28xx_audio_analog_set(dev); @@ -381,12 +381,13 @@ static int snd_em28xx_hw_capture_params(struct snd_pcm_substream *substream, static int snd_em28xx_hw_capture_free(struct snd_pcm_substream *substream) { struct em28xx *dev = snd_pcm_substream_chip(substream); + struct em28xx_audio *adev = &dev->adev; dprintk("Stop capture, if needed\n"); - if (atomic_read(&dev->stream_started) > 0) { - atomic_set(&dev->stream_started, 0); - schedule_work(&dev->wq_trigger); + if (atomic_read(&adev->stream_started) > 0) { + atomic_set(&adev->stream_started, 0); + schedule_work(&adev->wq_trigger); } return 0; @@ -407,9 +408,11 @@ static int snd_em28xx_prepare(struct snd_pcm_substream *substream) static void audio_trigger(struct work_struct *work) { - struct em28xx *dev = container_of(work, struct em28xx, wq_trigger); + struct em28xx_audio *adev = + container_of(work, struct em28xx_audio, wq_trigger); + struct em28xx *dev = container_of(adev, struct em28xx, adev); - if (atomic_read(&dev->stream_started)) { + if (atomic_read(&adev->stream_started)) { dprintk("starting capture"); em28xx_init_audio_isoc(dev); } else { @@ -431,17 +434,17 @@ static int snd_em28xx_capture_trigger(struct snd_pcm_substream *substream, case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: /* fall through */ case SNDRV_PCM_TRIGGER_RESUME: /* fall through */ case SNDRV_PCM_TRIGGER_START: - atomic_set(&dev->stream_started, 1); + atomic_set(&dev->adev.stream_started, 1); break; case SNDRV_PCM_TRIGGER_PAUSE_PUSH: /* fall through */ case SNDRV_PCM_TRIGGER_SUSPEND: /* fall through */ case SNDRV_PCM_TRIGGER_STOP: - atomic_set(&dev->stream_started, 0); + atomic_set(&dev->adev.stream_started, 0); break; default: retval = -EINVAL; } - schedule_work(&dev->wq_trigger); + schedule_work(&dev->adev.wq_trigger); return retval; } @@ -928,7 +931,7 @@ static int em28xx_audio_init(struct em28xx *dev) strcpy(card->shortname, "Em28xx Audio"); strcpy(card->longname, "Empia Em28xx Audio"); - INIT_WORK(&dev->wq_trigger, audio_trigger); + INIT_WORK(&adev->wq_trigger, audio_trigger); if (dev->audio_mode.ac97 != EM28XX_NO_AC97) { em28xx_cvol_new(card, dev, "Video", AC97_VIDEO); @@ -983,7 +986,7 @@ static int em28xx_audio_fini(struct em28xx *dev) if (dev->adev.sndcard) { snd_card_disconnect(dev->adev.sndcard); - flush_work(&dev->wq_trigger); + flush_work(&dev->adev.wq_trigger); em28xx_audio_free_urb(dev); @@ -1005,7 +1008,7 @@ static int em28xx_audio_suspend(struct em28xx *dev) em28xx_info("Suspending audio extension"); em28xx_deinit_isoc_audio(dev); - atomic_set(&dev->stream_started, 0); + atomic_set(&dev->adev.stream_started, 0); return 0; } @@ -1019,7 +1022,7 @@ static int em28xx_audio_resume(struct em28xx *dev) em28xx_info("Resuming audio extension"); /* Nothing to do other than schedule_work() ?? */ - schedule_work(&dev->wq_trigger); + schedule_work(&dev->adev.wq_trigger); return 0; } diff --git a/drivers/media/usb/em28xx/em28xx-camera.c b/drivers/media/usb/em28xx/em28xx-camera.c index 505e0505be04..12d4c0326e31 100644 --- a/drivers/media/usb/em28xx/em28xx-camera.c +++ b/drivers/media/usb/em28xx/em28xx-camera.c @@ -330,13 +330,14 @@ int em28xx_init_camera(struct em28xx *dev) char clk_name[V4L2_SUBDEV_NAME_SIZE]; struct i2c_client *client = &dev->i2c_client[dev->def_i2c_bus]; struct i2c_adapter *adap = &dev->i2c_adap[dev->def_i2c_bus]; + struct em28xx_v4l2 *v4l2 = dev->v4l2; int ret = 0; v4l2_clk_name_i2c(clk_name, sizeof(clk_name), i2c_adapter_id(adap), client->addr); - dev->clk = v4l2_clk_register_fixed(clk_name, "mclk", -EINVAL); - if (IS_ERR(dev->clk)) - return PTR_ERR(dev->clk); + v4l2->clk = v4l2_clk_register_fixed(clk_name, "mclk", -EINVAL); + if (IS_ERR(v4l2->clk)) + return PTR_ERR(v4l2->clk); switch (dev->em28xx_sensor) { case EM28XX_MT9V011: @@ -348,8 +349,8 @@ int em28xx_init_camera(struct em28xx *dev) .platform_data = &pdata, }; - dev->sensor_xres = 640; - dev->sensor_yres = 480; + v4l2->sensor_xres = 640; + v4l2->sensor_yres = 480; /* * FIXME: mt9v011 uses I2S speed as xtal clk - at least with @@ -362,41 +363,41 @@ int em28xx_init_camera(struct em28xx *dev) */ dev->board.xclk = EM28XX_XCLK_FREQUENCY_4_3MHZ; em28xx_write_reg(dev, EM28XX_R0F_XCLK, dev->board.xclk); - dev->sensor_xtal = 4300000; - pdata.xtal = dev->sensor_xtal; + v4l2->sensor_xtal = 4300000; + pdata.xtal = v4l2->sensor_xtal; if (NULL == - v4l2_i2c_new_subdev_board(&dev->v4l2_dev, adap, + v4l2_i2c_new_subdev_board(&dev->v4l2->v4l2_dev, adap, &mt9v011_info, NULL)) { ret = -ENODEV; break; } /* probably means GRGB 16 bit bayer */ - dev->vinmode = 0x0d; - dev->vinctl = 0x00; + v4l2->vinmode = 0x0d; + v4l2->vinctl = 0x00; break; } case EM28XX_MT9M001: - dev->sensor_xres = 1280; - dev->sensor_yres = 1024; + v4l2->sensor_xres = 1280; + v4l2->sensor_yres = 1024; em28xx_initialize_mt9m001(dev); /* probably means BGGR 16 bit bayer */ - dev->vinmode = 0x0c; - dev->vinctl = 0x00; + v4l2->vinmode = 0x0c; + v4l2->vinctl = 0x00; break; case EM28XX_MT9M111: - dev->sensor_xres = 640; - dev->sensor_yres = 512; + v4l2->sensor_xres = 640; + v4l2->sensor_yres = 512; dev->board.xclk = EM28XX_XCLK_FREQUENCY_48MHZ; em28xx_write_reg(dev, EM28XX_R0F_XCLK, dev->board.xclk); em28xx_initialize_mt9m111(dev); - dev->vinmode = 0x0a; - dev->vinctl = 0x00; + v4l2->vinmode = 0x0a; + v4l2->vinctl = 0x00; break; case EM28XX_OV2640: @@ -418,11 +419,11 @@ int em28xx_init_camera(struct em28xx *dev) * - adjust bridge xclk * - disable 16 bit (12 bit) output formats on high resolutions */ - dev->sensor_xres = 640; - dev->sensor_yres = 480; + v4l2->sensor_xres = 640; + v4l2->sensor_yres = 480; subdev = - v4l2_i2c_new_subdev_board(&dev->v4l2_dev, adap, + v4l2_i2c_new_subdev_board(&dev->v4l2->v4l2_dev, adap, &ov2640_info, NULL); if (NULL == subdev) { ret = -ENODEV; @@ -437,8 +438,8 @@ int em28xx_init_camera(struct em28xx *dev) /* NOTE: for UXGA=1600x1200 switch to 12MHz */ dev->board.xclk = EM28XX_XCLK_FREQUENCY_24MHZ; em28xx_write_reg(dev, EM28XX_R0F_XCLK, dev->board.xclk); - dev->vinmode = 0x08; - dev->vinctl = 0x00; + v4l2->vinmode = 0x08; + v4l2->vinctl = 0x00; break; } @@ -448,8 +449,8 @@ int em28xx_init_camera(struct em28xx *dev) } if (ret < 0) { - v4l2_clk_unregister_fixed(dev->clk); - dev->clk = NULL; + v4l2_clk_unregister_fixed(v4l2->clk); + v4l2->clk = NULL; } return ret; diff --git a/drivers/media/usb/em28xx/em28xx-cards.c b/drivers/media/usb/em28xx/em28xx-cards.c index 50aa5a5317f2..15ad47045553 100644 --- a/drivers/media/usb/em28xx/em28xx-cards.c +++ b/drivers/media/usb/em28xx/em28xx-cards.c @@ -467,6 +467,18 @@ static struct em28xx_reg_seq speedlink_vad_laplace_reg_seq[] = { { -1, -1, -1, -1}, }; +static struct em28xx_reg_seq pctv_292e[] = { + {EM2874_R80_GPIO_P0_CTRL, 0xff, 0xff, 0}, + {0x0d, 0xff, 0xff, 950}, + {EM2874_R80_GPIO_P0_CTRL, 0xbd, 0xff, 100}, + {EM2874_R80_GPIO_P0_CTRL, 0xfd, 0xff, 410}, + {EM2874_R80_GPIO_P0_CTRL, 0x7d, 0xff, 300}, + {EM2874_R80_GPIO_P0_CTRL, 0x7c, 0xff, 60}, + {0x0d, 0x42, 0xff, 50}, + {EM2874_R5F_TS_ENABLE, 0x85, 0xff, 0}, + {-1, -1, -1, -1}, +}; + /* * Button definitions */ @@ -2220,6 +2232,17 @@ struct em28xx_board em28xx_boards[] = { .has_dvb = 1, .ir_codes = RC_MAP_PINNACLE_PCTV_HD, }, + /* 2013:025f PCTV tripleStick (292e). + * Empia EM28178, Silicon Labs Si2168, Silicon Labs Si2157 */ + [EM28178_BOARD_PCTV_292E] = { + .name = "PCTV tripleStick (292e)", + .def_i2c_bus = 1, + .i2c_speed = EM28XX_I2C_CLK_WAIT_ENABLE | EM28XX_I2C_FREQ_400_KHZ, + .tuner_type = TUNER_ABSENT, + .tuner_gpio = pctv_292e, + .has_dvb = 1, + .ir_codes = RC_MAP_PINNACLE_PCTV_HD, + }, }; EXPORT_SYMBOL_GPL(em28xx_boards); @@ -2397,6 +2420,8 @@ struct usb_device_id em28xx_id_table[] = { .driver_info = EM2765_BOARD_SPEEDLINK_VAD_LAPLACE }, { USB_DEVICE(0x2013, 0x0258), .driver_info = EM28178_BOARD_PCTV_461E }, + { USB_DEVICE(0x2013, 0x025f), + .driver_info = EM28178_BOARD_PCTV_292E }, { }, }; MODULE_DEVICE_TABLE(usb, em28xx_id_table); @@ -2682,8 +2707,6 @@ static void em28xx_card_setup(struct em28xx *dev) if (dev->board.is_webcam) { if (em28xx_detect_sensor(dev) < 0) dev->board.is_webcam = 0; - else - dev->progressive = 1; } switch (dev->model) { @@ -2718,11 +2741,6 @@ static void em28xx_card_setup(struct em28xx *dev) dev->board.name, dev->model); dev->tuner_type = em28xx_boards[dev->model].tuner_type; - if (em28xx_boards[dev->model].tuner_addr) - dev->tuner_addr = em28xx_boards[dev->model].tuner_addr; - - if (em28xx_boards[dev->model].tda9887_conf) - dev->tda9887_conf = em28xx_boards[dev->model].tda9887_conf; /* request some modules */ switch (dev->model) { @@ -2991,8 +3009,6 @@ static int em28xx_init_dev(struct em28xx *dev, struct usb_device *udev, const char *chip_name = default_chip_name; dev->udev = udev; - mutex_init(&dev->vb_queue_lock); - mutex_init(&dev->vb_vbi_queue_lock); mutex_init(&dev->ctrl_urb_lock); spin_lock_init(&dev->slock); @@ -3416,15 +3432,14 @@ static int em28xx_usb_probe(struct usb_interface *interface, /* Select USB transfer types to use */ if (has_video) { - if (!dev->analog_ep_isoc || (try_bulk && dev->analog_ep_bulk)) - dev->analog_xfer_bulk = 1; - em28xx_info("analog set to %s mode.\n", - dev->analog_xfer_bulk ? "bulk" : "isoc"); + if (!dev->analog_ep_isoc || (try_bulk && dev->analog_ep_bulk)) + dev->analog_xfer_bulk = 1; + em28xx_info("analog set to %s mode.\n", + dev->analog_xfer_bulk ? "bulk" : "isoc"); } if (has_dvb) { - if (!dev->dvb_ep_isoc || (try_bulk && dev->dvb_ep_bulk)) - dev->dvb_xfer_bulk = 1; - + if (!dev->dvb_ep_isoc || (try_bulk && dev->dvb_ep_bulk)) + dev->dvb_xfer_bulk = 1; em28xx_info("dvb set to %s mode.\n", dev->dvb_xfer_bulk ? "bulk" : "isoc"); } diff --git a/drivers/media/usb/em28xx/em28xx-dvb.c b/drivers/media/usb/em28xx/em28xx-dvb.c index f599b18ef7ca..a121ed9561fd 100644 --- a/drivers/media/usb/em28xx/em28xx-dvb.c +++ b/drivers/media/usb/em28xx/em28xx-dvb.c @@ -55,6 +55,8 @@ #include "mb86a20s.h" #include "m88ds3103.h" #include "m88ts2022.h" +#include "si2168.h" +#include "si2157.h" MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@infradead.org>"); MODULE_LICENSE("GPL"); @@ -93,6 +95,7 @@ struct em28xx_dvb { struct semaphore pll_mutex; bool dont_attach_fe1; int lna_gpio; + struct i2c_client *i2c_client_demod; struct i2c_client *i2c_client_tuner; }; @@ -743,6 +746,21 @@ static int em28xx_pctv_290e_set_lna(struct dvb_frontend *fe) #endif } +static int em28xx_pctv_292e_set_lna(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + struct em28xx_i2c_bus *i2c_bus = fe->dvb->priv; + struct em28xx *dev = i2c_bus->dev; + u8 lna; + + if (c->lna == 1) + lna = 0x01; + else + lna = 0x00; + + return em28xx_write_reg_bits(dev, EM2874_R80_GPIO_P0_CTRL, lna, 0x01); +} + static int em28xx_mt352_terratec_xs_init(struct dvb_frontend *fe) { /* Values extracted from a USB trace of the Terratec Windows driver */ @@ -1496,6 +1514,63 @@ static int em28xx_dvb_init(struct em28xx *dev) dvb->i2c_client_tuner = client; } break; + case EM28178_BOARD_PCTV_292E: + { + struct i2c_adapter *adapter; + struct i2c_client *client; + struct i2c_board_info info; + struct si2168_config si2168_config; + struct si2157_config si2157_config; + + /* attach demod */ + si2168_config.i2c_adapter = &adapter; + si2168_config.fe = &dvb->fe[0]; + memset(&info, 0, sizeof(struct i2c_board_info)); + strlcpy(info.type, "si2168", I2C_NAME_SIZE); + info.addr = 0x64; + info.platform_data = &si2168_config; + request_module(info.type); + client = i2c_new_device(&dev->i2c_adap[dev->def_i2c_bus], &info); + if (client == NULL || client->dev.driver == NULL) { + result = -ENODEV; + goto out_free; + } + + if (!try_module_get(client->dev.driver->owner)) { + i2c_unregister_device(client); + result = -ENODEV; + goto out_free; + } + + dvb->i2c_client_demod = client; + + /* attach tuner */ + si2157_config.fe = dvb->fe[0]; + memset(&info, 0, sizeof(struct i2c_board_info)); + strlcpy(info.type, "si2157", I2C_NAME_SIZE); + info.addr = 0x60; + info.platform_data = &si2157_config; + request_module(info.type); + client = i2c_new_device(adapter, &info); + if (client == NULL || client->dev.driver == NULL) { + module_put(dvb->i2c_client_demod->dev.driver->owner); + i2c_unregister_device(dvb->i2c_client_demod); + result = -ENODEV; + goto out_free; + } + + if (!try_module_get(client->dev.driver->owner)) { + i2c_unregister_device(client); + module_put(dvb->i2c_client_demod->dev.driver->owner); + i2c_unregister_device(dvb->i2c_client_demod); + result = -ENODEV; + goto out_free; + } + + dvb->i2c_client_tuner = client; + dvb->fe[0]->ops.set_lna = em28xx_pctv_292e_set_lna; + } + break; default: em28xx_errdev("/2: The frontend of your DVB/ATSC card" " isn't supported yet\n"); @@ -1582,6 +1657,13 @@ static int em28xx_dvb_fini(struct em28xx *dev) i2c_unregister_device(client); } + /* remove I2C demod */ + client = dvb->i2c_client_demod; + if (client) { + module_put(client->dev.driver->owner); + i2c_unregister_device(client); + } + em28xx_unregister_dvb(dvb); kfree(dvb); dev->dvb = NULL; @@ -1647,6 +1729,13 @@ static int em28xx_dvb_resume(struct em28xx *dev) i2c_unregister_device(client); } + /* remove I2C demod */ + client = dvb->i2c_client_demod; + if (client) { + module_put(client->dev.driver->owner); + i2c_unregister_device(client); + } + em28xx_unregister_dvb(dvb); kfree(dvb); dev->dvb = NULL; diff --git a/drivers/media/usb/em28xx/em28xx-i2c.c b/drivers/media/usb/em28xx/em28xx-i2c.c index ba6433c3a643..b58d4ebf6419 100644 --- a/drivers/media/usb/em28xx/em28xx-i2c.c +++ b/drivers/media/usb/em28xx/em28xx-i2c.c @@ -939,7 +939,6 @@ int em28xx_i2c_register(struct em28xx *dev, unsigned bus, dev->i2c_bus[bus].algo_type = algo_type; dev->i2c_bus[bus].dev = dev; dev->i2c_adap[bus].algo_data = &dev->i2c_bus[bus]; - i2c_set_adapdata(&dev->i2c_adap[bus], &dev->v4l2_dev); retval = i2c_add_adapter(&dev->i2c_adap[bus]); if (retval < 0) { diff --git a/drivers/media/usb/em28xx/em28xx-v4l.h b/drivers/media/usb/em28xx/em28xx-v4l.h index bce438691e0e..432862c20bbf 100644 --- a/drivers/media/usb/em28xx/em28xx-v4l.h +++ b/drivers/media/usb/em28xx/em28xx-v4l.h @@ -16,5 +16,5 @@ int em28xx_start_analog_streaming(struct vb2_queue *vq, unsigned int count); -int em28xx_stop_vbi_streaming(struct vb2_queue *vq); +void em28xx_stop_vbi_streaming(struct vb2_queue *vq); extern struct vb2_ops em28xx_vbi_qops; diff --git a/drivers/media/usb/em28xx/em28xx-vbi.c b/drivers/media/usb/em28xx/em28xx-vbi.c index db3d655600df..6d7f657f6f55 100644 --- a/drivers/media/usb/em28xx/em28xx-vbi.c +++ b/drivers/media/usb/em28xx/em28xx-vbi.c @@ -47,12 +47,13 @@ static int vbi_queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt, unsigned int sizes[], void *alloc_ctxs[]) { struct em28xx *dev = vb2_get_drv_priv(vq); + struct em28xx_v4l2 *v4l2 = dev->v4l2; unsigned long size; if (fmt) size = fmt->fmt.pix.sizeimage; else - size = dev->vbi_width * dev->vbi_height * 2; + size = v4l2->vbi_width * v4l2->vbi_height * 2; if (0 == *nbuffers) *nbuffers = 32; @@ -69,11 +70,12 @@ static int vbi_queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt, static int vbi_buffer_prepare(struct vb2_buffer *vb) { - struct em28xx *dev = vb2_get_drv_priv(vb->vb2_queue); - struct em28xx_buffer *buf = container_of(vb, struct em28xx_buffer, vb); + struct em28xx *dev = vb2_get_drv_priv(vb->vb2_queue); + struct em28xx_v4l2 *v4l2 = dev->v4l2; + struct em28xx_buffer *buf = container_of(vb, struct em28xx_buffer, vb); unsigned long size; - size = dev->vbi_width * dev->vbi_height * 2; + size = v4l2->vbi_width * v4l2->vbi_height * 2; if (vb2_plane_size(vb, 0) < size) { printk(KERN_INFO "%s data will not fit into plane (%lu < %lu)\n", diff --git a/drivers/media/usb/em28xx/em28xx-video.c b/drivers/media/usb/em28xx/em28xx-video.c index 0856e5d367b6..f6b49c98e2c9 100644 --- a/drivers/media/usb/em28xx/em28xx-video.c +++ b/drivers/media/usb/em28xx/em28xx-video.c @@ -141,6 +141,33 @@ static struct em28xx_fmt format[] = { }, }; +/*FIXME: maxw should be dependent of alt mode */ +static inline unsigned int norm_maxw(struct em28xx *dev) +{ + struct em28xx_v4l2 *v4l2 = dev->v4l2; + + if (dev->board.is_webcam) + return v4l2->sensor_xres; + + if (dev->board.max_range_640_480) + return 640; + + return 720; +} + +static inline unsigned int norm_maxh(struct em28xx *dev) +{ + struct em28xx_v4l2 *v4l2 = dev->v4l2; + + if (dev->board.is_webcam) + return v4l2->sensor_yres; + + if (dev->board.max_range_640_480) + return 480; + + return (v4l2->norm & V4L2_STD_625_50) ? 576 : 480; +} + static int em28xx_vbi_supported(struct em28xx *dev) { /* Modprobe option to manually disable */ @@ -166,10 +193,11 @@ static int em28xx_vbi_supported(struct em28xx *dev) */ static void em28xx_wake_i2c(struct em28xx *dev) { - v4l2_device_call_all(&dev->v4l2_dev, 0, core, reset, 0); - v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_routing, + struct v4l2_device *v4l2_dev = &dev->v4l2->v4l2_dev; + v4l2_device_call_all(v4l2_dev, 0, core, reset, 0); + v4l2_device_call_all(v4l2_dev, 0, video, s_routing, INPUT(dev->ctl_input)->vmux, 0, 0); - v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_stream, 0); + v4l2_device_call_all(v4l2_dev, 0, video, s_stream, 0); } static int em28xx_colorlevels_set_default(struct em28xx *dev) @@ -194,8 +222,9 @@ static int em28xx_set_outfmt(struct em28xx *dev) { int ret; u8 fmt, vinctrl; + struct em28xx_v4l2 *v4l2 = dev->v4l2; - fmt = dev->format->reg; + fmt = v4l2->format->reg; if (!dev->is_em25xx) fmt |= 0x20; /* @@ -211,20 +240,20 @@ static int em28xx_set_outfmt(struct em28xx *dev) if (ret < 0) return ret; - ret = em28xx_write_reg(dev, EM28XX_R10_VINMODE, dev->vinmode); + ret = em28xx_write_reg(dev, EM28XX_R10_VINMODE, v4l2->vinmode); if (ret < 0) return ret; - vinctrl = dev->vinctl; + vinctrl = v4l2->vinctl; if (em28xx_vbi_supported(dev) == 1) { vinctrl |= EM28XX_VINCTRL_VBI_RAW; em28xx_write_reg(dev, EM28XX_R34_VBI_START_H, 0x00); - em28xx_write_reg(dev, EM28XX_R36_VBI_WIDTH, dev->vbi_width/4); - em28xx_write_reg(dev, EM28XX_R37_VBI_HEIGHT, dev->vbi_height); - if (dev->norm & V4L2_STD_525_60) { + em28xx_write_reg(dev, EM28XX_R36_VBI_WIDTH, v4l2->vbi_width/4); + em28xx_write_reg(dev, EM28XX_R37_VBI_HEIGHT, v4l2->vbi_height); + if (v4l2->norm & V4L2_STD_525_60) { /* NTSC */ em28xx_write_reg(dev, EM28XX_R35_VBI_START_V, 0x09); - } else if (dev->norm & V4L2_STD_625_50) { + } else if (v4l2->norm & V4L2_STD_625_50) { /* PAL */ em28xx_write_reg(dev, EM28XX_R35_VBI_START_V, 0x07); } @@ -274,7 +303,7 @@ static void em28xx_capture_area_set(struct em28xx *dev, u8 hstart, u8 vstart, static int em28xx_scaler_set(struct em28xx *dev, u16 h, u16 v) { - u8 mode; + u8 mode = 0x00; /* the em2800 scaler only supports scaling down to 50% */ if (dev->board.is_em2800) { @@ -293,22 +322,22 @@ static int em28xx_scaler_set(struct em28xx *dev, u16 h, u16 v) to work correctly */ mode = (h || v) ? 0x30 : 0x00; } - return em28xx_write_reg_bits(dev, EM28XX_R26_COMPR, mode, 0x30); + return em28xx_write_reg(dev, EM28XX_R26_COMPR, mode); } /* FIXME: this only function read values from dev */ static int em28xx_resolution_set(struct em28xx *dev) { - int width, height; - width = norm_maxw(dev); - height = norm_maxh(dev); + struct em28xx_v4l2 *v4l2 = dev->v4l2; + int width = norm_maxw(dev); + int height = norm_maxh(dev); /* Properly setup VBI */ - dev->vbi_width = 720; - if (dev->norm & V4L2_STD_525_60) - dev->vbi_height = 12; + v4l2->vbi_width = 720; + if (v4l2->norm & V4L2_STD_525_60) + v4l2->vbi_height = 12; else - dev->vbi_height = 18; + v4l2->vbi_height = 18; em28xx_set_outfmt(dev); @@ -326,15 +355,16 @@ static int em28xx_resolution_set(struct em28xx *dev) else em28xx_capture_area_set(dev, 0, 0, width, height); - return em28xx_scaler_set(dev, dev->hscale, dev->vscale); + return em28xx_scaler_set(dev, v4l2->hscale, v4l2->vscale); } /* Set USB alternate setting for analog video */ static int em28xx_set_alternate(struct em28xx *dev) { + struct em28xx_v4l2 *v4l2 = dev->v4l2; int errCode; int i; - unsigned int min_pkt_size = dev->width * 2 + 4; + unsigned int min_pkt_size = v4l2->width * 2 + 4; /* NOTE: for isoc transfers, only alt settings > 0 are allowed bulk transfers seem to work only with alt=0 ! */ @@ -351,7 +381,7 @@ static int em28xx_set_alternate(struct em28xx *dev) the frame size should be increased, otherwise, only green screen will be received. */ - if (dev->width * 2 * dev->height > 720 * 240 * 2) + if (v4l2->width * 2 * v4l2->height > 720 * 240 * 2) min_pkt_size *= 2; for (i = 0; i < dev->num_alt; i++) { @@ -404,7 +434,7 @@ static inline void finish_buffer(struct em28xx *dev, { em28xx_isocdbg("[%p/%d] wakeup\n", buf, buf->top_field); - buf->vb.v4l2_buf.sequence = dev->field_count++; + buf->vb.v4l2_buf.sequence = dev->v4l2->field_count++; buf->vb.v4l2_buf.field = V4L2_FIELD_INTERLACED; v4l2_get_timestamp(&buf->vb.v4l2_buf.timestamp); @@ -419,9 +449,10 @@ static void em28xx_copy_video(struct em28xx *dev, unsigned char *usb_buf, unsigned long len) { + struct em28xx_v4l2 *v4l2 = dev->v4l2; void *fieldstart, *startwrite, *startread; int linesdone, currlinedone, offset, lencopy, remain; - int bytesperline = dev->width << 1; + int bytesperline = v4l2->width << 1; if (buf->pos + len > buf->length) len = buf->length - buf->pos; @@ -429,7 +460,7 @@ static void em28xx_copy_video(struct em28xx *dev, startread = usb_buf; remain = len; - if (dev->progressive || buf->top_field) + if (v4l2->progressive || buf->top_field) fieldstart = buf->vb_buf; else /* interlaced mode, even nr. of lines */ fieldstart = buf->vb_buf + bytesperline; @@ -437,7 +468,7 @@ static void em28xx_copy_video(struct em28xx *dev, linesdone = buf->pos / bytesperline; currlinedone = buf->pos % bytesperline; - if (dev->progressive) + if (v4l2->progressive) offset = linesdone * bytesperline + currlinedone; else offset = linesdone * bytesperline * 2 + currlinedone; @@ -461,7 +492,7 @@ static void em28xx_copy_video(struct em28xx *dev, remain -= lencopy; while (remain > 0) { - if (dev->progressive) + if (v4l2->progressive) startwrite += lencopy; else startwrite += lencopy + bytesperline; @@ -507,7 +538,7 @@ static void em28xx_copy_vbi(struct em28xx *dev, offset = buf->pos; /* Make sure the bottom field populates the second half of the frame */ if (buf->top_field == 0) - offset += dev->vbi_width * dev->vbi_height; + offset += dev->v4l2->vbi_width * dev->v4l2->vbi_height; memcpy(buf->vb_buf + offset, usb_buf, len); buf->pos += len; @@ -583,13 +614,15 @@ finish_field_prepare_next(struct em28xx *dev, struct em28xx_buffer *buf, struct em28xx_dmaqueue *dma_q) { - if (dev->progressive || dev->top_field) { /* Brand new frame */ + struct em28xx_v4l2 *v4l2 = dev->v4l2; + + if (v4l2->progressive || v4l2->top_field) { /* Brand new frame */ if (buf != NULL) finish_buffer(dev, buf); buf = get_next_buf(dev, dma_q); } if (buf != NULL) { - buf->top_field = dev->top_field; + buf->top_field = v4l2->top_field; buf->pos = 0; } @@ -603,6 +636,7 @@ static inline void process_frame_data_em28xx(struct em28xx *dev, unsigned char *data_pkt, unsigned int data_len) { + struct em28xx_v4l2 *v4l2 = dev->v4l2; struct em28xx_buffer *buf = dev->usb_ctl.vid_buf; struct em28xx_buffer *vbi_buf = dev->usb_ctl.vbi_buf; struct em28xx_dmaqueue *dma_q = &dev->vidq; @@ -622,17 +656,17 @@ static inline void process_frame_data_em28xx(struct em28xx *dev, data_len -= 4; } else if (data_pkt[0] == 0x33 && data_pkt[1] == 0x95) { /* Field start (VBI mode) */ - dev->capture_type = 0; - dev->vbi_read = 0; + v4l2->capture_type = 0; + v4l2->vbi_read = 0; em28xx_isocdbg("VBI START HEADER !!!\n"); - dev->top_field = !(data_pkt[2] & 1); + v4l2->top_field = !(data_pkt[2] & 1); data_pkt += 4; data_len -= 4; } else if (data_pkt[0] == 0x22 && data_pkt[1] == 0x5a) { /* Field start (VBI disabled) */ - dev->capture_type = 2; + v4l2->capture_type = 2; em28xx_isocdbg("VIDEO START HEADER !!!\n"); - dev->top_field = !(data_pkt[2] & 1); + v4l2->top_field = !(data_pkt[2] & 1); data_pkt += 4; data_len -= 4; } @@ -640,37 +674,37 @@ static inline void process_frame_data_em28xx(struct em28xx *dev, /* NOTE: With bulk transfers, intermediate data packets * have no continuation header */ - if (dev->capture_type == 0) { + if (v4l2->capture_type == 0) { vbi_buf = finish_field_prepare_next(dev, vbi_buf, vbi_dma_q); dev->usb_ctl.vbi_buf = vbi_buf; - dev->capture_type = 1; + v4l2->capture_type = 1; } - if (dev->capture_type == 1) { - int vbi_size = dev->vbi_width * dev->vbi_height; - int vbi_data_len = ((dev->vbi_read + data_len) > vbi_size) ? - (vbi_size - dev->vbi_read) : data_len; + if (v4l2->capture_type == 1) { + int vbi_size = v4l2->vbi_width * v4l2->vbi_height; + int vbi_data_len = ((v4l2->vbi_read + data_len) > vbi_size) ? + (vbi_size - v4l2->vbi_read) : data_len; /* Copy VBI data */ if (vbi_buf != NULL) em28xx_copy_vbi(dev, vbi_buf, data_pkt, vbi_data_len); - dev->vbi_read += vbi_data_len; + v4l2->vbi_read += vbi_data_len; if (vbi_data_len < data_len) { /* Continue with copying video data */ - dev->capture_type = 2; + v4l2->capture_type = 2; data_pkt += vbi_data_len; data_len -= vbi_data_len; } } - if (dev->capture_type == 2) { + if (v4l2->capture_type == 2) { buf = finish_field_prepare_next(dev, buf, dma_q); dev->usb_ctl.vid_buf = buf; - dev->capture_type = 3; + v4l2->capture_type = 3; } - if (dev->capture_type == 3 && buf != NULL && data_len > 0) + if (v4l2->capture_type == 3 && buf != NULL && data_len > 0) em28xx_copy_video(dev, buf, data_pkt, data_len); } @@ -683,6 +717,7 @@ static inline void process_frame_data_em25xx(struct em28xx *dev, { struct em28xx_buffer *buf = dev->usb_ctl.vid_buf; struct em28xx_dmaqueue *dmaq = &dev->vidq; + struct em28xx_v4l2 *v4l2 = dev->v4l2; bool frame_end = 0; /* Check for header */ @@ -691,7 +726,7 @@ static inline void process_frame_data_em25xx(struct em28xx *dev, if (data_len >= 2) { /* em25xx header is only 2 bytes long */ if ((data_pkt[0] == EM25XX_FRMDATAHDR_BYTE1) && ((data_pkt[1] & ~EM25XX_FRMDATAHDR_BYTE2_MASK) == 0x00)) { - dev->top_field = !(data_pkt[1] & + v4l2->top_field = !(data_pkt[1] & EM25XX_FRMDATAHDR_BYTE2_FRAME_ID); frame_end = data_pkt[1] & EM25XX_FRMDATAHDR_BYTE2_FRAME_END; @@ -841,12 +876,14 @@ static int queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt, unsigned int sizes[], void *alloc_ctxs[]) { struct em28xx *dev = vb2_get_drv_priv(vq); + struct em28xx_v4l2 *v4l2 = dev->v4l2; unsigned long size; if (fmt) size = fmt->fmt.pix.sizeimage; else - size = (dev->width * dev->height * dev->format->depth + 7) >> 3; + size = + (v4l2->width * v4l2->height * v4l2->format->depth + 7) >> 3; if (size == 0) return -EINVAL; @@ -864,12 +901,13 @@ static int buffer_prepare(struct vb2_buffer *vb) { struct em28xx *dev = vb2_get_drv_priv(vb->vb2_queue); + struct em28xx_v4l2 *v4l2 = dev->v4l2; struct em28xx_buffer *buf = container_of(vb, struct em28xx_buffer, vb); unsigned long size; em28xx_videodbg("%s, field=%d\n", __func__, vb->v4l2_buf.field); - size = (dev->width * dev->height * dev->format->depth + 7) >> 3; + size = (v4l2->width * v4l2->height * v4l2->format->depth + 7) >> 3; if (vb2_plane_size(vb, 0) < size) { em28xx_videodbg("%s data will not fit into plane (%lu < %lu)\n", @@ -884,6 +922,7 @@ buffer_prepare(struct vb2_buffer *vb) int em28xx_start_analog_streaming(struct vb2_queue *vq, unsigned int count) { struct em28xx *dev = vb2_get_drv_priv(vq); + struct em28xx_v4l2 *v4l2 = dev->v4l2; struct v4l2_frequency f; int rc = 0; @@ -895,7 +934,7 @@ int em28xx_start_analog_streaming(struct vb2_queue *vq, unsigned int count) if (rc) return rc; - if (dev->streaming_users == 0) { + if (v4l2->streaming_users == 0) { /* First active streaming user, so allocate all the URBs */ /* Allocate the USB bandwidth */ @@ -906,7 +945,7 @@ int em28xx_start_analog_streaming(struct vb2_queue *vq, unsigned int count) */ em28xx_wake_i2c(dev); - dev->capture_type = -1; + v4l2->capture_type = -1; rc = em28xx_init_usb_xfer(dev, EM28XX_ANALOG_MODE, dev->analog_xfer_bulk, EM28XX_NUM_BUFS, @@ -924,22 +963,24 @@ int em28xx_start_analog_streaming(struct vb2_queue *vq, unsigned int count) /* Ask tuner to go to analog or radio mode */ memset(&f, 0, sizeof(f)); - f.frequency = dev->ctl_freq; + f.frequency = v4l2->frequency; if (vq->owner && vq->owner->vdev->vfl_type == VFL_TYPE_RADIO) f.type = V4L2_TUNER_RADIO; else f.type = V4L2_TUNER_ANALOG_TV; - v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_frequency, &f); + v4l2_device_call_all(&v4l2->v4l2_dev, + 0, tuner, s_frequency, &f); } - dev->streaming_users++; + v4l2->streaming_users++; return rc; } -static int em28xx_stop_streaming(struct vb2_queue *vq) +static void em28xx_stop_streaming(struct vb2_queue *vq) { struct em28xx *dev = vb2_get_drv_priv(vq); + struct em28xx_v4l2 *v4l2 = dev->v4l2; struct em28xx_dmaqueue *vidq = &dev->vidq; unsigned long flags = 0; @@ -947,7 +988,7 @@ static int em28xx_stop_streaming(struct vb2_queue *vq) res_free(dev, vq->type); - if (dev->streaming_users-- == 1) { + if (v4l2->streaming_users-- == 1) { /* Last active user, so shutdown all the URBS */ em28xx_uninit_usb_xfer(dev, EM28XX_ANALOG_MODE); } @@ -961,13 +1002,12 @@ static int em28xx_stop_streaming(struct vb2_queue *vq) } dev->usb_ctl.vid_buf = NULL; spin_unlock_irqrestore(&dev->slock, flags); - - return 0; } -int em28xx_stop_vbi_streaming(struct vb2_queue *vq) +void em28xx_stop_vbi_streaming(struct vb2_queue *vq) { struct em28xx *dev = vb2_get_drv_priv(vq); + struct em28xx_v4l2 *v4l2 = dev->v4l2; struct em28xx_dmaqueue *vbiq = &dev->vbiq; unsigned long flags = 0; @@ -975,7 +1015,7 @@ int em28xx_stop_vbi_streaming(struct vb2_queue *vq) res_free(dev, vq->type); - if (dev->streaming_users-- == 1) { + if (v4l2->streaming_users-- == 1) { /* Last active user, so shutdown all the URBS */ em28xx_uninit_usb_xfer(dev, EM28XX_ANALOG_MODE); } @@ -989,8 +1029,6 @@ int em28xx_stop_vbi_streaming(struct vb2_queue *vq) } dev->usb_ctl.vbi_buf = NULL; spin_unlock_irqrestore(&dev->slock, flags); - - return 0; } static void @@ -1024,9 +1062,10 @@ static int em28xx_vb2_setup(struct em28xx *dev) { int rc; struct vb2_queue *q; + struct em28xx_v4l2 *v4l2 = dev->v4l2; /* Setup Videobuf2 for Video capture */ - q = &dev->vb_vidq; + q = &v4l2->vb_vidq; q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; q->io_modes = VB2_READ | VB2_MMAP | VB2_USERPTR | VB2_DMABUF; q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; @@ -1040,7 +1079,7 @@ static int em28xx_vb2_setup(struct em28xx *dev) return rc; /* Setup Videobuf2 for VBI capture */ - q = &dev->vb_vbiq; + q = &v4l2->vb_vbiq; q->type = V4L2_BUF_TYPE_VBI_CAPTURE; q->io_modes = VB2_READ | VB2_MMAP | VB2_USERPTR; q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; @@ -1060,6 +1099,7 @@ static int em28xx_vb2_setup(struct em28xx *dev) static void video_mux(struct em28xx *dev, int index) { + struct v4l2_device *v4l2_dev = &dev->v4l2->v4l2_dev; dev->ctl_input = index; dev->ctl_ainput = INPUT(index)->amux; dev->ctl_aoutput = INPUT(index)->aout; @@ -1067,21 +1107,21 @@ static void video_mux(struct em28xx *dev, int index) if (!dev->ctl_aoutput) dev->ctl_aoutput = EM28XX_AOUT_MASTER; - v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_routing, + v4l2_device_call_all(v4l2_dev, 0, video, s_routing, INPUT(index)->vmux, 0, 0); if (dev->board.has_msp34xx) { if (dev->i2s_speed) { - v4l2_device_call_all(&dev->v4l2_dev, 0, audio, + v4l2_device_call_all(v4l2_dev, 0, audio, s_i2s_clock_freq, dev->i2s_speed); } /* Note: this is msp3400 specific */ - v4l2_device_call_all(&dev->v4l2_dev, 0, audio, s_routing, + v4l2_device_call_all(v4l2_dev, 0, audio, s_routing, dev->ctl_ainput, MSP_OUTPUT(MSP_SC_IN_DSP_SCART1), 0); } if (dev->board.adecoder != EM28XX_NOADECODER) { - v4l2_device_call_all(&dev->v4l2_dev, 0, audio, s_routing, + v4l2_device_call_all(v4l2_dev, 0, audio, s_routing, dev->ctl_ainput, dev->ctl_aoutput, 0); } @@ -1112,7 +1152,9 @@ static void em28xx_ctrl_notify(struct v4l2_ctrl *ctrl, void *priv) static int em28xx_s_ctrl(struct v4l2_ctrl *ctrl) { - struct em28xx *dev = container_of(ctrl->handler, struct em28xx, ctrl_handler); + struct em28xx_v4l2 *v4l2 = + container_of(ctrl->handler, struct em28xx_v4l2, ctrl_handler); + struct em28xx *dev = v4l2->dev; int ret = -EINVAL; switch (ctrl->id) { @@ -1187,19 +1229,20 @@ static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, { struct em28xx_fh *fh = priv; struct em28xx *dev = fh->dev; + struct em28xx_v4l2 *v4l2 = dev->v4l2; - f->fmt.pix.width = dev->width; - f->fmt.pix.height = dev->height; - f->fmt.pix.pixelformat = dev->format->fourcc; - f->fmt.pix.bytesperline = (dev->width * dev->format->depth + 7) >> 3; - f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * dev->height; + f->fmt.pix.width = v4l2->width; + f->fmt.pix.height = v4l2->height; + f->fmt.pix.pixelformat = v4l2->format->fourcc; + f->fmt.pix.bytesperline = (v4l2->width * v4l2->format->depth + 7) >> 3; + f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * v4l2->height; f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; /* FIXME: TOP? NONE? BOTTOM? ALTENATE? */ - if (dev->progressive) + if (v4l2->progressive) f->fmt.pix.field = V4L2_FIELD_NONE; else - f->fmt.pix.field = dev->interlaced ? + f->fmt.pix.field = v4l2->interlaced_fieldmode ? V4L2_FIELD_INTERLACED : V4L2_FIELD_TOP; return 0; } @@ -1220,6 +1263,7 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, { struct em28xx_fh *fh = priv; struct em28xx *dev = fh->dev; + struct em28xx_v4l2 *v4l2 = dev->v4l2; unsigned int width = f->fmt.pix.width; unsigned int height = f->fmt.pix.height; unsigned int maxw = norm_maxw(dev); @@ -1261,10 +1305,10 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, f->fmt.pix.bytesperline = (width * fmt->depth + 7) >> 3; f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * height; f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; - if (dev->progressive) + if (v4l2->progressive) f->fmt.pix.field = V4L2_FIELD_NONE; else - f->fmt.pix.field = dev->interlaced ? + f->fmt.pix.field = v4l2->interlaced_fieldmode ? V4L2_FIELD_INTERLACED : V4L2_FIELD_TOP; f->fmt.pix.priv = 0; @@ -1275,17 +1319,19 @@ static int em28xx_set_video_format(struct em28xx *dev, unsigned int fourcc, unsigned width, unsigned height) { struct em28xx_fmt *fmt; + struct em28xx_v4l2 *v4l2 = dev->v4l2; fmt = format_by_fourcc(fourcc); if (!fmt) return -EINVAL; - dev->format = fmt; - dev->width = width; - dev->height = height; + v4l2->format = fmt; + v4l2->width = width; + v4l2->height = height; /* set new image size */ - size_to_scale(dev, dev->width, dev->height, &dev->hscale, &dev->vscale); + size_to_scale(dev, v4l2->width, v4l2->height, + &v4l2->hscale, &v4l2->vscale); em28xx_resolution_set(dev); @@ -1296,8 +1342,9 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { struct em28xx *dev = video_drvdata(file); + struct em28xx_v4l2 *v4l2 = dev->v4l2; - if (dev->streaming_users > 0) + if (v4l2->streaming_users > 0) return -EBUSY; vidioc_try_fmt_vid_cap(file, priv, f); @@ -1311,7 +1358,7 @@ static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *norm) struct em28xx_fh *fh = priv; struct em28xx *dev = fh->dev; - *norm = dev->norm; + *norm = dev->v4l2->norm; return 0; } @@ -1321,24 +1368,25 @@ static int vidioc_querystd(struct file *file, void *priv, v4l2_std_id *norm) struct em28xx_fh *fh = priv; struct em28xx *dev = fh->dev; - v4l2_device_call_all(&dev->v4l2_dev, 0, video, querystd, norm); + v4l2_device_call_all(&dev->v4l2->v4l2_dev, 0, video, querystd, norm); return 0; } static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id norm) { - struct em28xx_fh *fh = priv; - struct em28xx *dev = fh->dev; + struct em28xx_fh *fh = priv; + struct em28xx *dev = fh->dev; + struct em28xx_v4l2 *v4l2 = dev->v4l2; struct v4l2_format f; - if (norm == dev->norm) + if (norm == v4l2->norm) return 0; - if (dev->streaming_users > 0) + if (v4l2->streaming_users > 0) return -EBUSY; - dev->norm = norm; + v4l2->norm = norm; /* Adjusts width/height, if needed */ f.fmt.pix.width = 720; @@ -1346,12 +1394,13 @@ static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id norm) vidioc_try_fmt_vid_cap(file, priv, &f); /* set new image size */ - dev->width = f.fmt.pix.width; - dev->height = f.fmt.pix.height; - size_to_scale(dev, dev->width, dev->height, &dev->hscale, &dev->vscale); + v4l2->width = f.fmt.pix.width; + v4l2->height = f.fmt.pix.height; + size_to_scale(dev, v4l2->width, v4l2->height, + &v4l2->hscale, &v4l2->vscale); em28xx_resolution_set(dev); - v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_std, dev->norm); + v4l2_device_call_all(&v4l2->v4l2_dev, 0, video, s_std, v4l2->norm); return 0; } @@ -1359,16 +1408,17 @@ static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id norm) static int vidioc_g_parm(struct file *file, void *priv, struct v4l2_streamparm *p) { - struct em28xx_fh *fh = priv; - struct em28xx *dev = fh->dev; + struct em28xx_fh *fh = priv; + struct em28xx *dev = fh->dev; + struct em28xx_v4l2 *v4l2 = dev->v4l2; int rc = 0; p->parm.capture.readbuffers = EM28XX_MIN_BUF; if (dev->board.is_webcam) - rc = v4l2_device_call_until_err(&dev->v4l2_dev, 0, + rc = v4l2_device_call_until_err(&v4l2->v4l2_dev, 0, video, g_parm, p); else - v4l2_video_std_frame_period(dev->norm, + v4l2_video_std_frame_period(v4l2->norm, &p->parm.capture.timeperframe); return rc; @@ -1381,7 +1431,8 @@ static int vidioc_s_parm(struct file *file, void *priv, struct em28xx *dev = fh->dev; p->parm.capture.readbuffers = EM28XX_MIN_BUF; - return v4l2_device_call_until_err(&dev->v4l2_dev, 0, video, s_parm, p); + return v4l2_device_call_until_err(&dev->v4l2->v4l2_dev, + 0, video, s_parm, p); } static const char *iname[] = { @@ -1418,7 +1469,7 @@ static int vidioc_enum_input(struct file *file, void *priv, (EM28XX_VMUX_CABLE == INPUT(n)->type)) i->type = V4L2_INPUT_TYPE_TUNER; - i->std = dev->vdev->tvnorms; + i->std = dev->v4l2->vdev->tvnorms; /* webcams do not have the STD API */ if (dev->board.is_webcam) i->capabilities = 0; @@ -1520,7 +1571,7 @@ static int vidioc_g_tuner(struct file *file, void *priv, strcpy(t->name, "Tuner"); - v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, g_tuner, t); + v4l2_device_call_all(&dev->v4l2->v4l2_dev, 0, tuner, g_tuner, t); return 0; } @@ -1533,7 +1584,7 @@ static int vidioc_s_tuner(struct file *file, void *priv, if (0 != t->index) return -EINVAL; - v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_tuner, t); + v4l2_device_call_all(&dev->v4l2->v4l2_dev, 0, tuner, s_tuner, t); return 0; } @@ -1542,27 +1593,29 @@ static int vidioc_g_frequency(struct file *file, void *priv, { struct em28xx_fh *fh = priv; struct em28xx *dev = fh->dev; + struct em28xx_v4l2 *v4l2 = dev->v4l2; if (0 != f->tuner) return -EINVAL; - f->frequency = dev->ctl_freq; + f->frequency = v4l2->frequency; return 0; } static int vidioc_s_frequency(struct file *file, void *priv, const struct v4l2_frequency *f) { - struct v4l2_frequency new_freq = *f; - struct em28xx_fh *fh = priv; - struct em28xx *dev = fh->dev; + struct v4l2_frequency new_freq = *f; + struct em28xx_fh *fh = priv; + struct em28xx *dev = fh->dev; + struct em28xx_v4l2 *v4l2 = dev->v4l2; if (0 != f->tuner) return -EINVAL; - v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_frequency, f); - v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, g_frequency, &new_freq); - dev->ctl_freq = new_freq.frequency; + v4l2_device_call_all(&v4l2->v4l2_dev, 0, tuner, s_frequency, f); + v4l2_device_call_all(&v4l2->v4l2_dev, 0, tuner, g_frequency, &new_freq); + v4l2->frequency = new_freq.frequency; return 0; } @@ -1579,7 +1632,8 @@ static int vidioc_g_chip_info(struct file *file, void *priv, if (chip->match.addr == 1) strlcpy(chip->name, "ac97", sizeof(chip->name)); else - strlcpy(chip->name, dev->v4l2_dev.name, sizeof(chip->name)); + strlcpy(chip->name, + dev->v4l2->v4l2_dev.name, sizeof(chip->name)); return 0; } @@ -1660,9 +1714,10 @@ static int vidioc_s_register(struct file *file, void *priv, static int vidioc_querycap(struct file *file, void *priv, struct v4l2_capability *cap) { - struct video_device *vdev = video_devdata(file); - struct em28xx_fh *fh = priv; - struct em28xx *dev = fh->dev; + struct video_device *vdev = video_devdata(file); + struct em28xx_fh *fh = priv; + struct em28xx *dev = fh->dev; + struct em28xx_v4l2 *v4l2 = dev->v4l2; strlcpy(cap->driver, "em28xx", sizeof(cap->driver)); strlcpy(cap->card, em28xx_boards[dev->model].name, sizeof(cap->card)); @@ -1684,9 +1739,9 @@ static int vidioc_querycap(struct file *file, void *priv, cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS | V4L2_CAP_READWRITE | V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; - if (dev->vbi_dev) + if (v4l2->vbi_dev) cap->capabilities |= V4L2_CAP_VBI_CAPTURE; - if (dev->radio_dev) + if (v4l2->radio_dev) cap->capabilities |= V4L2_CAP_RADIO; return 0; } @@ -1751,24 +1806,25 @@ static int vidioc_enum_framesizes(struct file *file, void *priv, static int vidioc_g_fmt_vbi_cap(struct file *file, void *priv, struct v4l2_format *format) { - struct em28xx_fh *fh = priv; - struct em28xx *dev = fh->dev; + struct em28xx_fh *fh = priv; + struct em28xx *dev = fh->dev; + struct em28xx_v4l2 *v4l2 = dev->v4l2; - format->fmt.vbi.samples_per_line = dev->vbi_width; + format->fmt.vbi.samples_per_line = v4l2->vbi_width; format->fmt.vbi.sample_format = V4L2_PIX_FMT_GREY; format->fmt.vbi.offset = 0; format->fmt.vbi.flags = 0; format->fmt.vbi.sampling_rate = 6750000 * 4 / 2; - format->fmt.vbi.count[0] = dev->vbi_height; - format->fmt.vbi.count[1] = dev->vbi_height; + format->fmt.vbi.count[0] = v4l2->vbi_height; + format->fmt.vbi.count[1] = v4l2->vbi_height; memset(format->fmt.vbi.reserved, 0, sizeof(format->fmt.vbi.reserved)); /* Varies by video standard (NTSC, PAL, etc.) */ - if (dev->norm & V4L2_STD_525_60) { + if (v4l2->norm & V4L2_STD_525_60) { /* NTSC */ format->fmt.vbi.start[0] = 10; format->fmt.vbi.start[1] = 273; - } else if (dev->norm & V4L2_STD_625_50) { + } else if (v4l2->norm & V4L2_STD_625_50) { /* PAL */ format->fmt.vbi.start[0] = 6; format->fmt.vbi.start[1] = 318; @@ -1791,7 +1847,7 @@ static int radio_g_tuner(struct file *file, void *priv, strcpy(t->name, "Radio"); - v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, g_tuner, t); + v4l2_device_call_all(&dev->v4l2->v4l2_dev, 0, tuner, g_tuner, t); return 0; } @@ -1804,12 +1860,27 @@ static int radio_s_tuner(struct file *file, void *priv, if (0 != t->index) return -EINVAL; - v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_tuner, t); + v4l2_device_call_all(&dev->v4l2->v4l2_dev, 0, tuner, s_tuner, t); return 0; } /* + * em28xx_free_v4l2() - Free struct em28xx_v4l2 + * + * @ref: struct kref for struct em28xx_v4l2 + * + * Called when all users of struct em28xx_v4l2 are gone + */ +static void em28xx_free_v4l2(struct kref *ref) +{ + struct em28xx_v4l2 *v4l2 = container_of(ref, struct em28xx_v4l2, ref); + + v4l2->dev->v4l2 = NULL; + kfree(v4l2); +} + +/* * em28xx_v4l2_open() * inits the device and starts isoc transfer */ @@ -1817,6 +1888,7 @@ static int em28xx_v4l2_open(struct file *filp) { struct video_device *vdev = video_devdata(filp); struct em28xx *dev = video_drvdata(filp); + struct em28xx_v4l2 *v4l2 = dev->v4l2; enum v4l2_buf_type fh_type = 0; struct em28xx_fh *fh; @@ -1835,7 +1907,7 @@ static int em28xx_v4l2_open(struct file *filp) em28xx_videodbg("open dev=%s type=%s users=%d\n", video_device_node_name(vdev), v4l2_type_names[fh_type], - dev->users); + v4l2->users); if (mutex_lock_interruptible(&dev->lock)) return -ERESTARTSYS; @@ -1850,7 +1922,7 @@ static int em28xx_v4l2_open(struct file *filp) fh->type = fh_type; filp->private_data = fh; - if (dev->users == 0) { + if (v4l2->users == 0) { em28xx_set_mode(dev, EM28XX_ANALOG_MODE); if (vdev->vfl_type != VFL_TYPE_RADIO) @@ -1865,11 +1937,12 @@ static int em28xx_v4l2_open(struct file *filp) if (vdev->vfl_type == VFL_TYPE_RADIO) { em28xx_videodbg("video_open: setting radio device\n"); - v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_radio); + v4l2_device_call_all(&v4l2->v4l2_dev, 0, tuner, s_radio); } kref_get(&dev->ref); - dev->users++; + kref_get(&v4l2->ref); + v4l2->users++; mutex_unlock(&dev->lock); v4l2_fh_add(&fh->fh); @@ -1884,6 +1957,8 @@ static int em28xx_v4l2_open(struct file *filp) */ static int em28xx_v4l2_fini(struct em28xx *dev) { + struct em28xx_v4l2 *v4l2 = dev->v4l2; + if (dev->is_audio_only) { /* Shouldn't initialize IR for this interface */ return 0; @@ -1894,39 +1969,45 @@ static int em28xx_v4l2_fini(struct em28xx *dev) return 0; } + if (v4l2 == NULL) + return 0; + em28xx_info("Closing video extension"); mutex_lock(&dev->lock); - v4l2_device_disconnect(&dev->v4l2_dev); + v4l2_device_disconnect(&v4l2->v4l2_dev); em28xx_uninit_usb_xfer(dev, EM28XX_ANALOG_MODE); - if (dev->radio_dev) { + if (v4l2->radio_dev) { em28xx_info("V4L2 device %s deregistered\n", - video_device_node_name(dev->radio_dev)); - video_unregister_device(dev->radio_dev); + video_device_node_name(v4l2->radio_dev)); + video_unregister_device(v4l2->radio_dev); } - if (dev->vbi_dev) { + if (v4l2->vbi_dev) { em28xx_info("V4L2 device %s deregistered\n", - video_device_node_name(dev->vbi_dev)); - video_unregister_device(dev->vbi_dev); + video_device_node_name(v4l2->vbi_dev)); + video_unregister_device(v4l2->vbi_dev); } - if (dev->vdev) { + if (v4l2->vdev) { em28xx_info("V4L2 device %s deregistered\n", - video_device_node_name(dev->vdev)); - video_unregister_device(dev->vdev); + video_device_node_name(v4l2->vdev)); + video_unregister_device(v4l2->vdev); } - v4l2_ctrl_handler_free(&dev->ctrl_handler); - v4l2_device_unregister(&dev->v4l2_dev); + v4l2_ctrl_handler_free(&v4l2->ctrl_handler); + v4l2_device_unregister(&v4l2->v4l2_dev); - if (dev->clk) { - v4l2_clk_unregister_fixed(dev->clk); - dev->clk = NULL; + if (v4l2->clk) { + v4l2_clk_unregister_fixed(v4l2->clk); + v4l2->clk = NULL; } + kref_put(&v4l2->ref, em28xx_free_v4l2); + mutex_unlock(&dev->lock); + kref_put(&dev->ref, em28xx_free_device); return 0; @@ -1965,22 +2046,23 @@ static int em28xx_v4l2_resume(struct em28xx *dev) */ static int em28xx_v4l2_close(struct file *filp) { - struct em28xx_fh *fh = filp->private_data; - struct em28xx *dev = fh->dev; + struct em28xx_fh *fh = filp->private_data; + struct em28xx *dev = fh->dev; + struct em28xx_v4l2 *v4l2 = dev->v4l2; int errCode; - em28xx_videodbg("users=%d\n", dev->users); + em28xx_videodbg("users=%d\n", v4l2->users); vb2_fop_release(filp); mutex_lock(&dev->lock); - if (dev->users == 1) { + if (v4l2->users == 1) { /* No sense to try to write to the device */ if (dev->disconnected) goto exit; /* Save some power by putting tuner to sleep */ - v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_power, 0); + v4l2_device_call_all(&v4l2->v4l2_dev, 0, core, s_power, 0); /* do this before setting alternate! */ em28xx_set_mode(dev, EM28XX_SUSPEND); @@ -1996,30 +2078,14 @@ static int em28xx_v4l2_close(struct file *filp) } exit: - dev->users--; + v4l2->users--; + kref_put(&v4l2->ref, em28xx_free_v4l2); mutex_unlock(&dev->lock); kref_put(&dev->ref, em28xx_free_device); return 0; } -/* - * em28xx_videodevice_release() - * called when the last user of the video device exits and frees the memeory - */ -static void em28xx_videodevice_release(struct video_device *vdev) -{ - struct em28xx *dev = video_get_drvdata(vdev); - - video_device_release(vdev); - if (vdev == dev->vdev) - dev->vdev = NULL; - else if (vdev == dev->vbi_dev) - dev->vbi_dev = NULL; - else if (vdev == dev->radio_dev) - dev->radio_dev = NULL; -} - static const struct v4l2_file_operations em28xx_v4l_fops = { .owner = THIS_MODULE, .open = em28xx_v4l2_open, @@ -2076,7 +2142,7 @@ static const struct v4l2_ioctl_ops video_ioctl_ops = { static const struct video_device em28xx_video_template = { .fops = &em28xx_v4l_fops, .ioctl_ops = &video_ioctl_ops, - .release = em28xx_videodevice_release, + .release = video_device_release, .tvnorms = V4L2_STD_ALL, }; @@ -2105,7 +2171,7 @@ static const struct v4l2_ioctl_ops radio_ioctl_ops = { static struct video_device em28xx_radio_template = { .fops = &radio_fops, .ioctl_ops = &radio_ioctl_ops, - .release = em28xx_videodevice_release, + .release = video_device_release, }; /* I2C possible address to saa7115, tvp5150, msp3400, tvaudio */ @@ -2139,7 +2205,7 @@ static struct video_device *em28xx_vdev_init(struct em28xx *dev, return NULL; *vfd = *template; - vfd->v4l2_dev = &dev->v4l2_dev; + vfd->v4l2_dev = &dev->v4l2->v4l2_dev; vfd->debug = video_debug; vfd->lock = &dev->lock; set_bit(V4L2_FL_USE_FH_PRIO, &vfd->flags); @@ -2153,13 +2219,12 @@ static struct video_device *em28xx_vdev_init(struct em28xx *dev, return vfd; } -static void em28xx_tuner_setup(struct em28xx *dev) +static void em28xx_tuner_setup(struct em28xx *dev, unsigned short tuner_addr) { - struct tuner_setup tun_setup; - struct v4l2_frequency f; - - if (dev->tuner_type == TUNER_ABSENT) - return; + struct em28xx_v4l2 *v4l2 = dev->v4l2; + struct v4l2_device *v4l2_dev = &v4l2->v4l2_dev; + struct tuner_setup tun_setup; + struct v4l2_frequency f; memset(&tun_setup, 0, sizeof(tun_setup)); @@ -2170,23 +2235,26 @@ static void em28xx_tuner_setup(struct em28xx *dev) tun_setup.type = dev->board.radio.type; tun_setup.addr = dev->board.radio_addr; - v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_type_addr, &tun_setup); + v4l2_device_call_all(v4l2_dev, + 0, tuner, s_type_addr, &tun_setup); } if ((dev->tuner_type != TUNER_ABSENT) && (dev->tuner_type)) { tun_setup.type = dev->tuner_type; - tun_setup.addr = dev->tuner_addr; + tun_setup.addr = tuner_addr; - v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_type_addr, &tun_setup); + v4l2_device_call_all(v4l2_dev, + 0, tuner, s_type_addr, &tun_setup); } - if (dev->tda9887_conf) { + if (dev->board.tda9887_conf) { struct v4l2_priv_tun_config tda9887_cfg; tda9887_cfg.tuner = TUNER_TDA9887; - tda9887_cfg.priv = &dev->tda9887_conf; + tda9887_cfg.priv = &dev->board.tda9887_conf; - v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_config, &tda9887_cfg); + v4l2_device_call_all(v4l2_dev, + 0, tuner, s_config, &tda9887_cfg); } if (dev->tuner_type == TUNER_XC2028) { @@ -2201,15 +2269,15 @@ static void em28xx_tuner_setup(struct em28xx *dev) xc2028_cfg.tuner = TUNER_XC2028; xc2028_cfg.priv = &ctl; - v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_config, &xc2028_cfg); + v4l2_device_call_all(v4l2_dev, 0, tuner, s_config, &xc2028_cfg); } /* configure tuner */ f.tuner = 0; f.type = V4L2_TUNER_ANALOG_TV; f.frequency = 9076; /* just a magic number */ - dev->ctl_freq = f.frequency; - v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_frequency, &f); + v4l2->frequency = f.frequency; + v4l2_device_call_all(v4l2_dev, 0, tuner, s_frequency, &f); } static int em28xx_v4l2_init(struct em28xx *dev) @@ -2217,7 +2285,8 @@ static int em28xx_v4l2_init(struct em28xx *dev) u8 val; int ret; unsigned int maxw; - struct v4l2_ctrl_handler *hdl = &dev->ctrl_handler; + struct v4l2_ctrl_handler *hdl; + struct em28xx_v4l2 *v4l2; if (dev->is_audio_only) { /* Shouldn't initialize IR for this interface */ @@ -2233,71 +2302,93 @@ static int em28xx_v4l2_init(struct em28xx *dev) mutex_lock(&dev->lock); - ret = v4l2_device_register(&dev->udev->dev, &dev->v4l2_dev); + v4l2 = kzalloc(sizeof(struct em28xx_v4l2), GFP_KERNEL); + if (v4l2 == NULL) { + em28xx_info("em28xx_v4l: memory allocation failed\n"); + mutex_unlock(&dev->lock); + return -ENOMEM; + } + kref_init(&v4l2->ref); + v4l2->dev = dev; + dev->v4l2 = v4l2; + + ret = v4l2_device_register(&dev->udev->dev, &v4l2->v4l2_dev); if (ret < 0) { em28xx_errdev("Call to v4l2_device_register() failed!\n"); goto err; } + hdl = &v4l2->ctrl_handler; v4l2_ctrl_handler_init(hdl, 8); - dev->v4l2_dev.ctrl_handler = hdl; + v4l2->v4l2_dev.ctrl_handler = hdl; + + if (dev->board.is_webcam) + v4l2->progressive = 1; /* * Default format, used for tvp5150 or saa711x output formats */ - dev->vinmode = 0x10; - dev->vinctl = EM28XX_VINCTRL_INTERLACED | - EM28XX_VINCTRL_CCIR656_ENABLE; + v4l2->vinmode = 0x10; + v4l2->vinctl = EM28XX_VINCTRL_INTERLACED | + EM28XX_VINCTRL_CCIR656_ENABLE; /* request some modules */ if (dev->board.has_msp34xx) - v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap[dev->def_i2c_bus], - "msp3400", 0, msp3400_addrs); + v4l2_i2c_new_subdev(&v4l2->v4l2_dev, + &dev->i2c_adap[dev->def_i2c_bus], + "msp3400", 0, msp3400_addrs); if (dev->board.decoder == EM28XX_SAA711X) - v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap[dev->def_i2c_bus], - "saa7115_auto", 0, saa711x_addrs); + v4l2_i2c_new_subdev(&v4l2->v4l2_dev, + &dev->i2c_adap[dev->def_i2c_bus], + "saa7115_auto", 0, saa711x_addrs); if (dev->board.decoder == EM28XX_TVP5150) - v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap[dev->def_i2c_bus], - "tvp5150", 0, tvp5150_addrs); + v4l2_i2c_new_subdev(&v4l2->v4l2_dev, + &dev->i2c_adap[dev->def_i2c_bus], + "tvp5150", 0, tvp5150_addrs); if (dev->board.adecoder == EM28XX_TVAUDIO) - v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap[dev->def_i2c_bus], - "tvaudio", dev->board.tvaudio_addr, NULL); + v4l2_i2c_new_subdev(&v4l2->v4l2_dev, + &dev->i2c_adap[dev->def_i2c_bus], + "tvaudio", dev->board.tvaudio_addr, NULL); /* Initialize tuner and camera */ if (dev->board.tuner_type != TUNER_ABSENT) { - int has_demod = (dev->tda9887_conf & TDA9887_PRESENT); + unsigned short tuner_addr = dev->board.tuner_addr; + int has_demod = (dev->board.tda9887_conf & TDA9887_PRESENT); if (dev->board.radio.type) - v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap[dev->def_i2c_bus], - "tuner", dev->board.radio_addr, NULL); + v4l2_i2c_new_subdev(&v4l2->v4l2_dev, + &dev->i2c_adap[dev->def_i2c_bus], + "tuner", dev->board.radio_addr, NULL); if (has_demod) - v4l2_i2c_new_subdev(&dev->v4l2_dev, + v4l2_i2c_new_subdev(&v4l2->v4l2_dev, &dev->i2c_adap[dev->def_i2c_bus], "tuner", 0, v4l2_i2c_tuner_addrs(ADDRS_DEMOD)); - if (dev->tuner_addr == 0) { + if (tuner_addr == 0) { enum v4l2_i2c_tuner_type type = has_demod ? ADDRS_TV_WITH_DEMOD : ADDRS_TV; struct v4l2_subdev *sd; - sd = v4l2_i2c_new_subdev(&dev->v4l2_dev, + sd = v4l2_i2c_new_subdev(&v4l2->v4l2_dev, &dev->i2c_adap[dev->def_i2c_bus], "tuner", 0, v4l2_i2c_tuner_addrs(type)); if (sd) - dev->tuner_addr = v4l2_i2c_subdev_addr(sd); + tuner_addr = v4l2_i2c_subdev_addr(sd); } else { - v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap[dev->def_i2c_bus], - "tuner", dev->tuner_addr, NULL); + v4l2_i2c_new_subdev(&v4l2->v4l2_dev, + &dev->i2c_adap[dev->def_i2c_bus], + "tuner", tuner_addr, NULL); } + + em28xx_tuner_setup(dev, tuner_addr); } - em28xx_tuner_setup(dev); if (dev->em28xx_sensor != EM28XX_NOSENSOR) em28xx_init_camera(dev); @@ -2348,12 +2439,12 @@ static int em28xx_v4l2_init(struct em28xx *dev) } /* set default norm */ - dev->norm = V4L2_STD_PAL; - v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_std, dev->norm); - dev->interlaced = EM28XX_INTERLACED_DEFAULT; + v4l2->norm = V4L2_STD_PAL; + v4l2_device_call_all(&v4l2->v4l2_dev, 0, video, s_std, v4l2->norm); + v4l2->interlaced_fieldmode = EM28XX_INTERLACED_DEFAULT; /* Analog specific initialization */ - dev->format = &format[0]; + v4l2->format = &format[0]; maxw = norm_maxw(dev); /* MaxPacketSize for em2800 is too small to capture at full resolution @@ -2376,74 +2467,75 @@ static int em28xx_v4l2_init(struct em28xx *dev) (EM28XX_XCLK_AUDIO_UNMUTE | val)); em28xx_set_outfmt(dev); - em28xx_compression_disable(dev); /* Add image controls */ /* NOTE: at this point, the subdevices are already registered, so bridge * controls are only added/enabled when no subdevice provides them */ - if (NULL == v4l2_ctrl_find(&dev->ctrl_handler, V4L2_CID_CONTRAST)) - v4l2_ctrl_new_std(&dev->ctrl_handler, &em28xx_ctrl_ops, + if (NULL == v4l2_ctrl_find(hdl, V4L2_CID_CONTRAST)) + v4l2_ctrl_new_std(hdl, &em28xx_ctrl_ops, V4L2_CID_CONTRAST, 0, 0x1f, 1, CONTRAST_DEFAULT); - if (NULL == v4l2_ctrl_find(&dev->ctrl_handler, V4L2_CID_BRIGHTNESS)) - v4l2_ctrl_new_std(&dev->ctrl_handler, &em28xx_ctrl_ops, + if (NULL == v4l2_ctrl_find(hdl, V4L2_CID_BRIGHTNESS)) + v4l2_ctrl_new_std(hdl, &em28xx_ctrl_ops, V4L2_CID_BRIGHTNESS, -0x80, 0x7f, 1, BRIGHTNESS_DEFAULT); - if (NULL == v4l2_ctrl_find(&dev->ctrl_handler, V4L2_CID_SATURATION)) - v4l2_ctrl_new_std(&dev->ctrl_handler, &em28xx_ctrl_ops, + if (NULL == v4l2_ctrl_find(hdl, V4L2_CID_SATURATION)) + v4l2_ctrl_new_std(hdl, &em28xx_ctrl_ops, V4L2_CID_SATURATION, 0, 0x1f, 1, SATURATION_DEFAULT); - if (NULL == v4l2_ctrl_find(&dev->ctrl_handler, V4L2_CID_BLUE_BALANCE)) - v4l2_ctrl_new_std(&dev->ctrl_handler, &em28xx_ctrl_ops, + if (NULL == v4l2_ctrl_find(hdl, V4L2_CID_BLUE_BALANCE)) + v4l2_ctrl_new_std(hdl, &em28xx_ctrl_ops, V4L2_CID_BLUE_BALANCE, -0x30, 0x30, 1, BLUE_BALANCE_DEFAULT); - if (NULL == v4l2_ctrl_find(&dev->ctrl_handler, V4L2_CID_RED_BALANCE)) - v4l2_ctrl_new_std(&dev->ctrl_handler, &em28xx_ctrl_ops, + if (NULL == v4l2_ctrl_find(hdl, V4L2_CID_RED_BALANCE)) + v4l2_ctrl_new_std(hdl, &em28xx_ctrl_ops, V4L2_CID_RED_BALANCE, -0x30, 0x30, 1, RED_BALANCE_DEFAULT); - if (NULL == v4l2_ctrl_find(&dev->ctrl_handler, V4L2_CID_SHARPNESS)) - v4l2_ctrl_new_std(&dev->ctrl_handler, &em28xx_ctrl_ops, + if (NULL == v4l2_ctrl_find(hdl, V4L2_CID_SHARPNESS)) + v4l2_ctrl_new_std(hdl, &em28xx_ctrl_ops, V4L2_CID_SHARPNESS, 0, 0x0f, 1, SHARPNESS_DEFAULT); /* Reset image controls */ em28xx_colorlevels_set_default(dev); - v4l2_ctrl_handler_setup(&dev->ctrl_handler); - ret = dev->ctrl_handler.error; + v4l2_ctrl_handler_setup(hdl); + ret = hdl->error; if (ret) goto unregister_dev; /* allocate and fill video video_device struct */ - dev->vdev = em28xx_vdev_init(dev, &em28xx_video_template, "video"); - if (!dev->vdev) { + v4l2->vdev = em28xx_vdev_init(dev, &em28xx_video_template, "video"); + if (!v4l2->vdev) { em28xx_errdev("cannot allocate video_device.\n"); ret = -ENODEV; goto unregister_dev; } - dev->vdev->queue = &dev->vb_vidq; - dev->vdev->queue->lock = &dev->vb_queue_lock; + mutex_init(&v4l2->vb_queue_lock); + mutex_init(&v4l2->vb_vbi_queue_lock); + v4l2->vdev->queue = &v4l2->vb_vidq; + v4l2->vdev->queue->lock = &v4l2->vb_queue_lock; /* disable inapplicable ioctls */ if (dev->board.is_webcam) { - v4l2_disable_ioctl(dev->vdev, VIDIOC_QUERYSTD); - v4l2_disable_ioctl(dev->vdev, VIDIOC_G_STD); - v4l2_disable_ioctl(dev->vdev, VIDIOC_S_STD); + v4l2_disable_ioctl(v4l2->vdev, VIDIOC_QUERYSTD); + v4l2_disable_ioctl(v4l2->vdev, VIDIOC_G_STD); + v4l2_disable_ioctl(v4l2->vdev, VIDIOC_S_STD); } else { - v4l2_disable_ioctl(dev->vdev, VIDIOC_S_PARM); + v4l2_disable_ioctl(v4l2->vdev, VIDIOC_S_PARM); } if (dev->tuner_type == TUNER_ABSENT) { - v4l2_disable_ioctl(dev->vdev, VIDIOC_G_TUNER); - v4l2_disable_ioctl(dev->vdev, VIDIOC_S_TUNER); - v4l2_disable_ioctl(dev->vdev, VIDIOC_G_FREQUENCY); - v4l2_disable_ioctl(dev->vdev, VIDIOC_S_FREQUENCY); + v4l2_disable_ioctl(v4l2->vdev, VIDIOC_G_TUNER); + v4l2_disable_ioctl(v4l2->vdev, VIDIOC_S_TUNER); + v4l2_disable_ioctl(v4l2->vdev, VIDIOC_G_FREQUENCY); + v4l2_disable_ioctl(v4l2->vdev, VIDIOC_S_FREQUENCY); } if (!dev->audio_mode.has_audio) { - v4l2_disable_ioctl(dev->vdev, VIDIOC_G_AUDIO); - v4l2_disable_ioctl(dev->vdev, VIDIOC_S_AUDIO); + v4l2_disable_ioctl(v4l2->vdev, VIDIOC_G_AUDIO); + v4l2_disable_ioctl(v4l2->vdev, VIDIOC_S_AUDIO); } /* register v4l2 video video_device */ - ret = video_register_device(dev->vdev, VFL_TYPE_GRABBER, + ret = video_register_device(v4l2->vdev, VFL_TYPE_GRABBER, video_nr[dev->devno]); if (ret) { em28xx_errdev("unable to register video device (error=%i).\n", @@ -2453,27 +2545,27 @@ static int em28xx_v4l2_init(struct em28xx *dev) /* Allocate and fill vbi video_device struct */ if (em28xx_vbi_supported(dev) == 1) { - dev->vbi_dev = em28xx_vdev_init(dev, &em28xx_video_template, + v4l2->vbi_dev = em28xx_vdev_init(dev, &em28xx_video_template, "vbi"); - dev->vbi_dev->queue = &dev->vb_vbiq; - dev->vbi_dev->queue->lock = &dev->vb_vbi_queue_lock; + v4l2->vbi_dev->queue = &v4l2->vb_vbiq; + v4l2->vbi_dev->queue->lock = &v4l2->vb_vbi_queue_lock; /* disable inapplicable ioctls */ - v4l2_disable_ioctl(dev->vdev, VIDIOC_S_PARM); + v4l2_disable_ioctl(v4l2->vdev, VIDIOC_S_PARM); if (dev->tuner_type == TUNER_ABSENT) { - v4l2_disable_ioctl(dev->vbi_dev, VIDIOC_G_TUNER); - v4l2_disable_ioctl(dev->vbi_dev, VIDIOC_S_TUNER); - v4l2_disable_ioctl(dev->vbi_dev, VIDIOC_G_FREQUENCY); - v4l2_disable_ioctl(dev->vbi_dev, VIDIOC_S_FREQUENCY); + v4l2_disable_ioctl(v4l2->vbi_dev, VIDIOC_G_TUNER); + v4l2_disable_ioctl(v4l2->vbi_dev, VIDIOC_S_TUNER); + v4l2_disable_ioctl(v4l2->vbi_dev, VIDIOC_G_FREQUENCY); + v4l2_disable_ioctl(v4l2->vbi_dev, VIDIOC_S_FREQUENCY); } if (!dev->audio_mode.has_audio) { - v4l2_disable_ioctl(dev->vbi_dev, VIDIOC_G_AUDIO); - v4l2_disable_ioctl(dev->vbi_dev, VIDIOC_S_AUDIO); + v4l2_disable_ioctl(v4l2->vbi_dev, VIDIOC_G_AUDIO); + v4l2_disable_ioctl(v4l2->vbi_dev, VIDIOC_S_AUDIO); } /* register v4l2 vbi video_device */ - ret = video_register_device(dev->vbi_dev, VFL_TYPE_VBI, + ret = video_register_device(v4l2->vbi_dev, VFL_TYPE_VBI, vbi_nr[dev->devno]); if (ret < 0) { em28xx_errdev("unable to register vbi device\n"); @@ -2482,32 +2574,32 @@ static int em28xx_v4l2_init(struct em28xx *dev) } if (em28xx_boards[dev->model].radio.type == EM28XX_RADIO) { - dev->radio_dev = em28xx_vdev_init(dev, &em28xx_radio_template, - "radio"); - if (!dev->radio_dev) { + v4l2->radio_dev = em28xx_vdev_init(dev, &em28xx_radio_template, + "radio"); + if (!v4l2->radio_dev) { em28xx_errdev("cannot allocate video_device.\n"); ret = -ENODEV; goto unregister_dev; } - ret = video_register_device(dev->radio_dev, VFL_TYPE_RADIO, + ret = video_register_device(v4l2->radio_dev, VFL_TYPE_RADIO, radio_nr[dev->devno]); if (ret < 0) { em28xx_errdev("can't register radio device\n"); goto unregister_dev; } em28xx_info("Registered radio device as %s\n", - video_device_node_name(dev->radio_dev)); + video_device_node_name(v4l2->radio_dev)); } em28xx_info("V4L2 video device registered as %s\n", - video_device_node_name(dev->vdev)); + video_device_node_name(v4l2->vdev)); - if (dev->vbi_dev) + if (v4l2->vbi_dev) em28xx_info("V4L2 VBI device registered as %s\n", - video_device_node_name(dev->vbi_dev)); + video_device_node_name(v4l2->vbi_dev)); /* Save some power by putting tuner to sleep */ - v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_power, 0); + v4l2_device_call_all(&v4l2->v4l2_dev, 0, core, s_power, 0); /* initialize videobuf2 stuff */ em28xx_vb2_setup(dev); @@ -2520,9 +2612,11 @@ static int em28xx_v4l2_init(struct em28xx *dev) return 0; unregister_dev: - v4l2_ctrl_handler_free(&dev->ctrl_handler); - v4l2_device_unregister(&dev->v4l2_dev); + v4l2_ctrl_handler_free(&v4l2->ctrl_handler); + v4l2_device_unregister(&v4l2->v4l2_dev); err: + dev->v4l2 = NULL; + kref_put(&v4l2->ref, em28xx_free_v4l2); mutex_unlock(&dev->lock); return ret; } diff --git a/drivers/media/usb/em28xx/em28xx.h b/drivers/media/usb/em28xx/em28xx.h index 2051fc9fb932..b4c837d77e5d 100644 --- a/drivers/media/usb/em28xx/em28xx.h +++ b/drivers/media/usb/em28xx/em28xx.h @@ -140,6 +140,7 @@ #define EM2765_BOARD_SPEEDLINK_VAD_LAPLACE 91 #define EM28178_BOARD_PCTV_461E 92 #define EM2874_BOARD_KWORLD_UB435Q_V3 93 +#define EM28178_BOARD_PCTV_292E 94 /* Limits minimum and default number of buffers */ #define EM28XX_MIN_BUF 4 @@ -497,6 +498,60 @@ struct em28xx_eeprom { #define EM28XX_RESOURCE_VIDEO 0x01 #define EM28XX_RESOURCE_VBI 0x02 +struct em28xx_v4l2 { + struct kref ref; + struct em28xx *dev; + + struct v4l2_device v4l2_dev; + struct v4l2_ctrl_handler ctrl_handler; + struct v4l2_clk *clk; + + struct video_device *vdev; + struct video_device *vbi_dev; + struct video_device *radio_dev; + + /* Videobuf2 */ + struct vb2_queue vb_vidq; + struct vb2_queue vb_vbiq; + struct mutex vb_queue_lock; + struct mutex vb_vbi_queue_lock; + + u8 vinmode; + u8 vinctl; + + /* Camera specific fields */ + int sensor_xres; + int sensor_yres; + int sensor_xtal; + + int users; /* user count for exclusive use */ + int streaming_users; /* number of actively streaming users */ + + u32 frequency; /* selected tuner frequency */ + + struct em28xx_fmt *format; + v4l2_std_id norm; /* selected tv norm */ + + /* Progressive/interlaced mode */ + bool progressive; + int interlaced_fieldmode; /* 1=interlaced fields, 0=just top fields */ + /* FIXME: everything else than interlaced_fieldmode=1 doesn't work */ + + /* Frame properties */ + int width; /* current frame width */ + int height; /* current frame height */ + unsigned hscale; /* horizontal scale factor (see datasheet) */ + unsigned vscale; /* vertical scale factor (see datasheet) */ + unsigned int vbi_width; + unsigned int vbi_height; /* lines per field */ + + /* Capture state tracking */ + int capture_type; + bool top_field; + int vbi_read; + unsigned int field_count; +}; + struct em28xx_audio { char name[50]; unsigned num_urb; @@ -513,6 +568,10 @@ struct em28xx_audio { int users; spinlock_t slock; + + /* Controls streaming */ + struct work_struct wq_trigger; /* trigger to start/stop audio */ + atomic_t stream_started; /* stream should be running if true */ }; struct em28xx; @@ -541,6 +600,12 @@ struct em28xx_i2c_bus { struct em28xx { struct kref ref; + /* Sub-module data */ + struct em28xx_v4l2 *v4l2; + struct em28xx_dvb *dvb; + struct em28xx_audio adev; + struct em28xx_IR *ir; + /* generic device properties */ char name[30]; /* name (including minor) of the device */ int model; /* index in the device_data struct */ @@ -554,29 +619,9 @@ struct em28xx { unsigned int has_alsa_audio:1; unsigned int is_audio_only:1; - struct v4l2_device v4l2_dev; - struct v4l2_ctrl_handler ctrl_handler; - struct v4l2_clk *clk; struct em28xx_board board; - /* Webcam specific fields */ - enum em28xx_sensor em28xx_sensor; - int sensor_xres, sensor_yres; - int sensor_xtal; - - /* Progressive (non-interlaced) mode */ - int progressive; - - /* Vinmode/Vinctl used at the driver */ - int vinmode, vinctl; - - /* Controls audio streaming */ - struct work_struct wq_trigger; /* Trigger to start/stop audio for alsa module */ - atomic_t stream_started; /* stream should be running if true */ - - struct em28xx_fmt *format; - - struct em28xx_IR *ir; + enum em28xx_sensor em28xx_sensor; /* camera specific */ /* Some older em28xx chips needs a waiting time after writing */ unsigned int wait_after_write; @@ -588,8 +633,6 @@ struct em28xx { struct em28xx_audio_mode audio_mode; int tuner_type; /* type of the tuner */ - int tuner_addr; /* tuner address */ - int tda9887_conf; /* i2c i/o */ struct i2c_adapter i2c_adap[NUM_I2C_BUSES]; @@ -602,52 +645,21 @@ struct em28xx { struct rt_mutex i2c_bus_lock; /* video for linux */ - int users; /* user count for exclusive use */ - int streaming_users; /* Number of actively streaming users */ - struct video_device *vdev; /* video for linux device struct */ - v4l2_std_id norm; /* selected tv norm */ - int ctl_freq; /* selected frequency */ unsigned int ctl_input; /* selected input */ unsigned int ctl_ainput;/* selected audio input */ unsigned int ctl_aoutput;/* selected audio output */ int mute; int volume; - /* frame properties */ - int width; /* current frame width */ - int height; /* current frame height */ - unsigned hscale; /* horizontal scale factor (see datasheet) */ - unsigned vscale; /* vertical scale factor (see datasheet) */ - int interlaced; /* 1=interlace fileds, 0=just top fileds */ - unsigned int video_bytesread; /* Number of bytes read */ unsigned long hash; /* eeprom hash - for boards with generic ID */ unsigned long i2c_hash; /* i2c devicelist hash - for boards with generic ID */ - struct em28xx_audio adev; - - /* capture state tracking */ - int capture_type; - unsigned char top_field:1; - int vbi_read; - unsigned int vbi_width; - unsigned int vbi_height; /* lines per field */ - struct work_struct request_module_wk; /* locks */ struct mutex lock; struct mutex ctrl_urb_lock; /* protects urb_buf */ - /* spinlock_t queue_lock; */ - struct list_head inqueue, outqueue; - struct video_device *vbi_dev; - struct video_device *radio_dev; - - /* Videobuf2 */ - struct vb2_queue vb_vidq; - struct vb2_queue vb_vbiq; - struct mutex vb_queue_lock; - struct mutex vb_vbi_queue_lock; /* resources in use */ unsigned int resources; @@ -662,9 +674,6 @@ struct em28xx { struct em28xx_usb_ctl usb_ctl; spinlock_t slock; - unsigned int field_count; - unsigned int vbi_field_count; - /* usb transfer */ struct usb_device *udev; /* the usb device */ u8 ifnum; /* number of the assigned usb interface */ @@ -708,8 +717,6 @@ struct em28xx { /* Snapshot button input device */ char snapshot_button_path[30]; /* path of the input dev */ struct input_dev *sbutton_input_dev; - - struct em28xx_dvb *dvb; }; #define kref_to_dev(d) container_of(d, struct em28xx, ref) @@ -797,32 +804,4 @@ int em28xx_init_camera(struct em28xx *dev); printk(KERN_WARNING "%s: "fmt,\ dev->name , ##arg); } while (0) -static inline int em28xx_compression_disable(struct em28xx *dev) -{ - /* side effect of disabling scaler and mixer */ - return em28xx_write_reg(dev, EM28XX_R26_COMPR, 0x00); -} - -/*FIXME: maxw should be dependent of alt mode */ -static inline unsigned int norm_maxw(struct em28xx *dev) -{ - if (dev->board.is_webcam) - return dev->sensor_xres; - - if (dev->board.max_range_640_480) - return 640; - - return 720; -} - -static inline unsigned int norm_maxh(struct em28xx *dev) -{ - if (dev->board.is_webcam) - return dev->sensor_yres; - - if (dev->board.max_range_640_480) - return 480; - - return (dev->norm & V4L2_STD_625_50) ? 576 : 480; -} #endif diff --git a/drivers/media/usb/gspca/Kconfig b/drivers/media/usb/gspca/Kconfig index 4f0c6d566c85..eed10d782535 100644 --- a/drivers/media/usb/gspca/Kconfig +++ b/drivers/media/usb/gspca/Kconfig @@ -50,6 +50,16 @@ config USB_GSPCA_CPIA1 To compile this driver as a module, choose M here: the module will be called gspca_cpia1. +config USB_GSPCA_DTCS033 + tristate "DTCS033 (Scopium) USB Astro-Camera Driver" + depends on VIDEO_V4L2 && USB_GSPCA + help + Say Y here if you want support for the Scopium camera + for planetary astrophotography. + + To compile this driver as a module, choose M here: the + module will be called gspca_dtcs033. + config USB_GSPCA_ETOMS tristate "Etoms USB Camera Driver" depends on VIDEO_V4L2 && USB_GSPCA diff --git a/drivers/media/usb/gspca/Makefile b/drivers/media/usb/gspca/Makefile index 5855131ab8b6..f46975e4c82d 100644 --- a/drivers/media/usb/gspca/Makefile +++ b/drivers/media/usb/gspca/Makefile @@ -2,6 +2,7 @@ obj-$(CONFIG_USB_GSPCA) += gspca_main.o obj-$(CONFIG_USB_GSPCA_BENQ) += gspca_benq.o obj-$(CONFIG_USB_GSPCA_CONEX) += gspca_conex.o obj-$(CONFIG_USB_GSPCA_CPIA1) += gspca_cpia1.o +obj-$(CONFIG_USB_GSPCA_DTCS033) += gspca_dtcs033.o obj-$(CONFIG_USB_GSPCA_ETOMS) += gspca_etoms.o obj-$(CONFIG_USB_GSPCA_FINEPIX) += gspca_finepix.o obj-$(CONFIG_USB_GSPCA_JEILINJ) += gspca_jeilinj.o @@ -48,6 +49,7 @@ gspca_main-objs := gspca.o autogain_functions.o gspca_benq-objs := benq.o gspca_conex-objs := conex.o gspca_cpia1-objs := cpia1.o +gspca_dtcs033-objs := dtcs033.o gspca_etoms-objs := etoms.o gspca_finepix-objs := finepix.o gspca_jeilinj-objs := jeilinj.o diff --git a/drivers/media/usb/gspca/dtcs033.c b/drivers/media/usb/gspca/dtcs033.c new file mode 100644 index 000000000000..96bfd4e0f0eb --- /dev/null +++ b/drivers/media/usb/gspca/dtcs033.c @@ -0,0 +1,441 @@ +/* + * Subdriver for Scopium astro-camera (DTCS033, 0547:7303) + * + * Copyright (C) 2014 Robert Butora (robert.butora.fi@gmail.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * 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. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#define MODULE_NAME "dtcs033" +#include "gspca.h" + +MODULE_AUTHOR("Robert Butora <robert.butora.fi@gmail.com>"); +MODULE_DESCRIPTION("Scopium DTCS033 astro-cam USB Camera Driver"); +MODULE_LICENSE("GPL"); + +struct dtcs033_usb_requests { + u8 bRequestType; + u8 bRequest; + u16 wValue; + u16 wIndex; + u16 wLength; +}; + +/* send a usb request */ +static void reg_rw(struct gspca_dev *gspca_dev, + u8 bRequestType, u8 bRequest, + u16 wValue, u16 wIndex, u16 wLength) +{ + struct usb_device *udev = gspca_dev->dev; + int ret; + + if (gspca_dev->usb_err < 0) + return; + + ret = usb_control_msg(udev, + usb_rcvctrlpipe(udev, 0), + bRequest, + bRequestType, + wValue, wIndex, + gspca_dev->usb_buf, wLength, 500); + + if (ret < 0) { + gspca_dev->usb_err = ret; + pr_err("usb_control_msg error %d\n", ret); + } + + return; +} +/* send several usb in/out requests */ +static int reg_reqs(struct gspca_dev *gspca_dev, + const struct dtcs033_usb_requests *preqs, int n_reqs) +{ + int i = 0; + const struct dtcs033_usb_requests *preq; + + while ((i < n_reqs) && (gspca_dev->usb_err >= 0)) { + + preq = &preqs[i]; + + reg_rw(gspca_dev, preq->bRequestType, preq->bRequest, + preq->wValue, preq->wIndex, preq->wLength); + + if (gspca_dev->usb_err < 0) { + + PERR("usb error request no: %d / %d\n", + i, n_reqs); + } else if (preq->bRequestType & USB_DIR_IN) { + + PDEBUG(D_STREAM, + "USB IN (%d) returned[%d] %02X %02X %02X %s", + i, + preq->wLength, + gspca_dev->usb_buf[0], + gspca_dev->usb_buf[1], + gspca_dev->usb_buf[2], + preq->wLength > 3 ? "...\n" : "\n"); + } + + i++; + } + return gspca_dev->usb_err; +} + +/* -- subdriver interface implementation -- */ + +#define DT_COLS (640) +static const struct v4l2_pix_format dtcs033_mode[] = { + /* raw Bayer patterned output */ + {DT_COLS, 480, V4L2_PIX_FMT_GREY, V4L2_FIELD_NONE, + .bytesperline = DT_COLS, + .sizeimage = DT_COLS*480, + .colorspace = V4L2_COLORSPACE_SRGB, + }, + /* this mode will demosaic the Bayer pattern */ + {DT_COLS, 480, V4L2_PIX_FMT_SRGGB8, V4L2_FIELD_NONE, + .bytesperline = DT_COLS, + .sizeimage = DT_COLS*480, + .colorspace = V4L2_COLORSPACE_SRGB, + } +}; + +/* config called at probe time */ +static int sd_config(struct gspca_dev *gspca_dev, + const struct usb_device_id *id) +{ + gspca_dev->cam.cam_mode = dtcs033_mode; + gspca_dev->cam.nmodes = ARRAY_SIZE(dtcs033_mode); + + gspca_dev->cam.bulk = 1; + gspca_dev->cam.bulk_nurbs = 1; + gspca_dev->cam.bulk_size = DT_COLS*512; + + return 0; +} + +/* init called at probe and resume time */ +static int sd_init(struct gspca_dev *gspca_dev) +{ + return 0; +} + +/* start stop the camera */ +static int dtcs033_start(struct gspca_dev *gspca_dev); +static void dtcs033_stopN(struct gspca_dev *gspca_dev); + +/* intercept camera image data */ +static void dtcs033_pkt_scan(struct gspca_dev *gspca_dev, + u8 *data, /* packet data */ + int len) /* packet data length */ +{ + /* drop incomplete frames */ + if (len != DT_COLS*512) { + gspca_dev->last_packet_type = DISCARD_PACKET; + /* gspca.c: discard invalidates the whole frame. */ + return; + } + + /* forward complete frames */ + gspca_frame_add(gspca_dev, FIRST_PACKET, NULL, 0); + gspca_frame_add(gspca_dev, INTER_PACKET, + data + 16*DT_COLS, + len - 32*DT_COLS); /* skip first & last 16 lines */ + gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0); + + return; +} + +/* -- controls: exposure and gain -- */ + +static void dtcs033_setexposure(struct gspca_dev *gspca_dev, + s32 expo, s32 gain) +{ + /* gain [dB] encoding */ + u16 sGain = (u16)gain; + u16 gainVal = 224+(sGain-14)*(768-224)/(33-14); + u16 wIndex = 0x0100|(0x00FF&gainVal); + u16 wValue = (0xFF00&gainVal)>>8; + + /* exposure time [msec] encoding */ + u16 sXTime = (u16)expo; + u16 xtimeVal = (524*(150-(sXTime-1)))/150; + + const u8 bRequestType = + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE; + const u8 bRequest = 0x18; + + reg_rw(gspca_dev, + bRequestType, bRequest, wValue, wIndex, 0); + if (gspca_dev->usb_err < 0) + PERR("usb error in setexposure(gain) sequence.\n"); + + reg_rw(gspca_dev, + bRequestType, bRequest, (xtimeVal<<4), 0x6300, 0); + if (gspca_dev->usb_err < 0) + PERR("usb error in setexposure(time) sequence.\n"); +} + +/* specific webcam descriptor */ +struct sd { + struct gspca_dev gspca_dev;/* !! must be the first item */ + + /* exposure & gain controls */ + struct { + struct v4l2_ctrl *exposure; + struct v4l2_ctrl *gain; + }; +}; + +static int sd_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct gspca_dev *gspca_dev = + container_of(ctrl->handler, + struct gspca_dev, ctrl_handler); + struct sd *sd = (struct sd *) gspca_dev; + + gspca_dev->usb_err = 0; + + if (!gspca_dev->streaming) + return 0; + + switch (ctrl->id) { + case V4L2_CID_EXPOSURE: + dtcs033_setexposure(gspca_dev, + ctrl->val, sd->gain->val); + break; + case V4L2_CID_GAIN: + dtcs033_setexposure(gspca_dev, + sd->exposure->val, ctrl->val); + break; + } + return gspca_dev->usb_err; +} + +static const struct v4l2_ctrl_ops sd_ctrl_ops = { + .s_ctrl = sd_s_ctrl, +}; + +static int dtcs033_init_controls(struct gspca_dev *gspca_dev) +{ + struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler; + struct sd *sd = (struct sd *) gspca_dev; + + gspca_dev->vdev.ctrl_handler = hdl; + v4l2_ctrl_handler_init(hdl, 2); + /* min max step default */ + sd->exposure = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_EXPOSURE, + 1, 150, 1, 75);/* [msec] */ + sd->gain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_GAIN, + 14, 33, 1, 24);/* [dB] */ + if (hdl->error) { + PERR("Could not initialize controls: %d\n", + hdl->error); + return hdl->error; + } + + v4l2_ctrl_cluster(2, &sd->exposure); + return 0; +} + +/* sub-driver description */ +static const struct sd_desc sd_desc = { + .name = MODULE_NAME, + .config = sd_config, + .init = sd_init, + .start = dtcs033_start, + .stopN = dtcs033_stopN, + .pkt_scan = dtcs033_pkt_scan, + .init_controls = dtcs033_init_controls, +}; + +/* -- module initialisation -- */ + +static const struct usb_device_id device_table[] = { + {USB_DEVICE(0x0547, 0x7303)}, + {} +}; +MODULE_DEVICE_TABLE(usb, device_table); + +/* device connect */ +static int sd_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + return gspca_dev_probe(intf, id, + &sd_desc, sizeof(struct sd), + THIS_MODULE); +} + +static struct usb_driver sd_driver = { + .name = MODULE_NAME, + .id_table = device_table, + .probe = sd_probe, + .disconnect = gspca_disconnect, +#ifdef CONFIG_PM + .suspend = gspca_suspend, + .resume = gspca_resume, + .reset_resume = gspca_resume, +#endif +}; +module_usb_driver(sd_driver); + + +/* --------------------------------------------------------- + USB requests to start/stop the camera [USB 2.0 spec Ch.9]. + + bRequestType : + 0x40 = USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0xC0 = USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, +*/ +static const struct dtcs033_usb_requests dtcs033_start_reqs[] = { +/* -- bRequest,wValue,wIndex,wLength */ +{ 0x40, 0x01, 0x0001, 0x000F, 0x0000 }, +{ 0x40, 0x01, 0x0000, 0x000F, 0x0000 }, +{ 0x40, 0x01, 0x0001, 0x000F, 0x0000 }, +{ 0x40, 0x18, 0x0000, 0x7F00, 0x0000 }, +{ 0x40, 0x18, 0x0000, 0x1001, 0x0000 }, +{ 0x40, 0x18, 0x0000, 0x0004, 0x0000 }, +{ 0x40, 0x18, 0x0000, 0x7F01, 0x0000 }, +{ 0x40, 0x18, 0x30E0, 0x0009, 0x0000 }, +{ 0x40, 0x18, 0x0500, 0x012C, 0x0000 }, +{ 0x40, 0x18, 0x0380, 0x0200, 0x0000 }, +{ 0x40, 0x18, 0x0000, 0x035C, 0x0000 }, +{ 0x40, 0x18, 0x05C0, 0x0438, 0x0000 }, +{ 0x40, 0x18, 0x0440, 0x0500, 0x0000 }, +{ 0x40, 0x18, 0x0000, 0x0668, 0x0000 }, +{ 0x40, 0x18, 0x0000, 0x0700, 0x0000 }, +{ 0x40, 0x18, 0x0000, 0x0800, 0x0000 }, +{ 0x40, 0x18, 0x0000, 0x0900, 0x0000 }, +{ 0x40, 0x18, 0x0000, 0x0A00, 0x0000 }, +{ 0x40, 0x18, 0x0000, 0x0B00, 0x0000 }, +{ 0x40, 0x18, 0x30E0, 0x6009, 0x0000 }, +{ 0x40, 0x18, 0x0500, 0x612C, 0x0000 }, +{ 0x40, 0x18, 0x2090, 0x6274, 0x0000 }, +{ 0x40, 0x18, 0x05C0, 0x6338, 0x0000 }, +{ 0x40, 0x18, 0x0000, 0x6400, 0x0000 }, +{ 0x40, 0x18, 0x05C0, 0x6538, 0x0000 }, +{ 0x40, 0x18, 0x0000, 0x6600, 0x0000 }, +{ 0x40, 0x18, 0x0680, 0x6744, 0x0000 }, +{ 0x40, 0x18, 0x0000, 0x6800, 0x0000 }, +{ 0x40, 0x18, 0x0000, 0x6900, 0x0000 }, +{ 0x40, 0x18, 0x0000, 0x6A00, 0x0000 }, +{ 0x40, 0x18, 0x0000, 0x6B00, 0x0000 }, +{ 0x40, 0x18, 0x0000, 0x6C00, 0x0000 }, +{ 0x40, 0x18, 0x0000, 0x6D00, 0x0000 }, +{ 0x40, 0x18, 0x0000, 0x6E00, 0x0000 }, +{ 0x40, 0x18, 0x0000, 0x808C, 0x0000 }, +{ 0x40, 0x18, 0x0010, 0x8101, 0x0000 }, +{ 0x40, 0x18, 0x30E0, 0x8200, 0x0000 }, +{ 0x40, 0x18, 0x0810, 0x832C, 0x0000 }, +{ 0x40, 0x18, 0x0680, 0x842B, 0x0000 }, +{ 0x40, 0x18, 0x0000, 0x8500, 0x0000 }, +{ 0x40, 0x18, 0x0000, 0x8600, 0x0000 }, +{ 0x40, 0x18, 0x0280, 0x8715, 0x0000 }, +{ 0x40, 0x18, 0x0000, 0x880C, 0x0000 }, +{ 0x40, 0x18, 0x0010, 0x8901, 0x0000 }, +{ 0x40, 0x18, 0x30E0, 0x8A00, 0x0000 }, +{ 0x40, 0x18, 0x0810, 0x8B2C, 0x0000 }, +{ 0x40, 0x18, 0x0680, 0x8C2B, 0x0000 }, +{ 0x40, 0x18, 0x0000, 0x8D00, 0x0000 }, +{ 0x40, 0x18, 0x0000, 0x8E00, 0x0000 }, +{ 0x40, 0x18, 0x0280, 0x8F15, 0x0000 }, +{ 0x40, 0x18, 0x0010, 0xD040, 0x0000 }, +{ 0x40, 0x18, 0x0000, 0xD100, 0x0000 }, +{ 0x40, 0x18, 0x00B0, 0xD20A, 0x0000 }, +{ 0x40, 0x18, 0x0000, 0xD300, 0x0000 }, +{ 0x40, 0x18, 0x30E2, 0xD40D, 0x0000 }, +{ 0x40, 0x18, 0x0001, 0xD5C0, 0x0000 }, +{ 0x40, 0x18, 0x00A0, 0xD60A, 0x0000 }, +{ 0x40, 0x18, 0x0000, 0xD700, 0x0000 }, +{ 0x40, 0x18, 0x0000, 0x7F00, 0x0000 }, +{ 0x40, 0x18, 0x0000, 0x1501, 0x0000 }, +{ 0x40, 0x18, 0x0001, 0x01FF, 0x0000 }, +{ 0x40, 0x18, 0x0000, 0x0200, 0x0000 }, +{ 0x40, 0x18, 0x0000, 0x0304, 0x0000 }, +{ 0x40, 0x18, 0x0000, 0x1101, 0x0000 }, +{ 0x40, 0x18, 0x0000, 0x1201, 0x0000 }, +{ 0x40, 0x18, 0x0000, 0x1300, 0x0000 }, +{ 0x40, 0x18, 0x0000, 0x1400, 0x0000 }, +{ 0x40, 0x18, 0x0000, 0x1601, 0x0000 }, +{ 0x40, 0x18, 0x0000, 0x1800, 0x0000 }, +{ 0x40, 0x18, 0x0000, 0x1900, 0x0000 }, +{ 0x40, 0x18, 0x0000, 0x1A00, 0x0000 }, +{ 0x40, 0x18, 0x2000, 0x1B00, 0x0000 }, +{ 0x40, 0x18, 0x0000, 0x1C00, 0x0000 }, +{ 0x40, 0x18, 0x0000, 0x2100, 0x0000 }, +{ 0x40, 0x18, 0x00C0, 0x228E, 0x0000 }, +{ 0x40, 0x18, 0x0000, 0x3001, 0x0000 }, +{ 0x40, 0x18, 0x0010, 0x3101, 0x0000 }, +{ 0x40, 0x18, 0x0008, 0x3301, 0x0000 }, +{ 0x40, 0x18, 0x0000, 0x3400, 0x0000 }, +{ 0x40, 0x18, 0x0012, 0x3549, 0x0000 }, +{ 0x40, 0x18, 0x0000, 0x3620, 0x0000 }, +{ 0x40, 0x18, 0x0001, 0x3700, 0x0000 }, +{ 0x40, 0x18, 0x0000, 0x4000, 0x0000 }, +{ 0x40, 0x18, 0xFFFF, 0x41FF, 0x0000 }, +{ 0x40, 0x18, 0xFFFF, 0x42FF, 0x0000 }, +{ 0x40, 0x18, 0x0000, 0x500F, 0x0000 }, +{ 0x40, 0x18, 0x2272, 0x5108, 0x0000 }, +{ 0x40, 0x18, 0x2272, 0x5208, 0x0000 }, +{ 0x40, 0x18, 0xFFFF, 0x53FF, 0x0000 }, +{ 0x40, 0x18, 0xFFFF, 0x54FF, 0x0000 }, +{ 0x40, 0x18, 0x0000, 0x6000, 0x0000 }, +{ 0x40, 0x18, 0x0000, 0x6102, 0x0000 }, +{ 0x40, 0x18, 0x0010, 0x6214, 0x0000 }, +{ 0x40, 0x18, 0x0C80, 0x6300, 0x0000 }, +{ 0x40, 0x18, 0x0000, 0x6401, 0x0000 }, +{ 0x40, 0x18, 0x0680, 0x6551, 0x0000 }, +{ 0x40, 0x18, 0xFFFF, 0x66FF, 0x0000 }, +{ 0x40, 0x18, 0x0000, 0x6702, 0x0000 }, +{ 0x40, 0x18, 0x0010, 0x6800, 0x0000 }, +{ 0x40, 0x18, 0x0000, 0x6900, 0x0000 }, +{ 0x40, 0x18, 0x0000, 0x6A00, 0x0000 }, +{ 0x40, 0x18, 0x0000, 0x6B00, 0x0000 }, +{ 0x40, 0x18, 0x0000, 0x6C00, 0x0000 }, +{ 0x40, 0x18, 0x0000, 0x6D01, 0x0000 }, +{ 0x40, 0x18, 0x0000, 0x6E00, 0x0000 }, +{ 0x40, 0x18, 0x0000, 0x6F00, 0x0000 }, +{ 0x40, 0x18, 0x0000, 0x7000, 0x0000 }, +{ 0x40, 0x18, 0x0001, 0x7118, 0x0000 }, +{ 0x40, 0x18, 0x0000, 0x2001, 0x0000 }, +{ 0x40, 0x18, 0x0000, 0x1101, 0x0000 }, +{ 0x40, 0x18, 0x0000, 0x1301, 0x0000 }, +{ 0x40, 0x18, 0x0000, 0x1300, 0x0000 }, +{ 0x40, 0x18, 0x0000, 0x1501, 0x0000 }, +{ 0xC0, 0x11, 0x0000, 0x24C0, 0x0003 }, +{ 0x40, 0x18, 0x0000, 0x3000, 0x0000 }, +{ 0x40, 0x18, 0x0000, 0x3620, 0x0000 }, +{ 0x40, 0x18, 0x0000, 0x1501, 0x0000 }, +{ 0x40, 0x18, 0x0010, 0x6300, 0x0000 }, +{ 0x40, 0x18, 0x0002, 0x01F0, 0x0000 }, +{ 0x40, 0x01, 0x0003, 0x000F, 0x0000 } +}; + +static const struct dtcs033_usb_requests dtcs033_stop_reqs[] = { +/* -- bRequest,wValue,wIndex,wLength */ +{ 0x40, 0x01, 0x0001, 0x000F, 0x0000 }, +{ 0x40, 0x01, 0x0000, 0x000F, 0x0000 }, +{ 0x40, 0x18, 0x0000, 0x0003, 0x0000 } +}; +static int dtcs033_start(struct gspca_dev *gspca_dev) +{ + return reg_reqs(gspca_dev, dtcs033_start_reqs, + ARRAY_SIZE(dtcs033_start_reqs)); +} + +static void dtcs033_stopN(struct gspca_dev *gspca_dev) +{ + reg_reqs(gspca_dev, dtcs033_stop_reqs, + ARRAY_SIZE(dtcs033_stop_reqs)); + return; +} diff --git a/drivers/media/usb/gspca/gl860/gl860-mi2020.c b/drivers/media/usb/gspca/gl860/gl860-mi2020.c index 2edda6b7d653..a785828d4b9d 100644 --- a/drivers/media/usb/gspca/gl860/gl860-mi2020.c +++ b/drivers/media/usb/gspca/gl860/gl860-mi2020.c @@ -35,32 +35,34 @@ static u8 dat_hvflip5[] = {0x8c, 0xa1, 0x03}; static u8 dat_hvflip6[] = {0x90, 0x00, 0x06}; static struct idxdata tbl_middle_hvflip_low[] = { - {0x33, "\x90\x00\x06"}, - {6, "\xff\xff\xff"}, - {0x33, "\x90\x00\x06"}, - {6, "\xff\xff\xff"}, - {0x33, "\x90\x00\x06"}, - {6, "\xff\xff\xff"}, - {0x33, "\x90\x00\x06"}, - {6, "\xff\xff\xff"}, + {0x33, {0x90, 0x00, 0x06}}, + {6, {0xff, 0xff, 0xff}}, + {0x33, {0x90, 0x00, 0x06}}, + {6, {0xff, 0xff, 0xff}}, + {0x33, {0x90, 0x00, 0x06}}, + {6, {0xff, 0xff, 0xff}}, + {0x33, {0x90, 0x00, 0x06}}, + {6, {0xff, 0xff, 0xff}}, }; static struct idxdata tbl_middle_hvflip_big[] = { - {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x01"}, {0x33, "\x8c\xa1\x20"}, - {0x33, "\x90\x00\x00"}, {0x33, "\x8c\xa7\x02"}, {0x33, "\x90\x00\x00"}, - {102, "\xff\xff\xff"}, - {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x02"}, {0x33, "\x8c\xa1\x20"}, - {0x33, "\x90\x00\x72"}, {0x33, "\x8c\xa7\x02"}, {0x33, "\x90\x00\x01"}, + {0x33, {0x8c, 0xa1, 0x03}}, {0x33, {0x90, 0x00, 0x01}}, + {0x33, {0x8c, 0xa1, 0x20}}, {0x33, {0x90, 0x00, 0x00}}, + {0x33, {0x8c, 0xa7, 0x02}}, {0x33, {0x90, 0x00, 0x00}}, + {102, {0xff, 0xff, 0xff}}, + {0x33, {0x8c, 0xa1, 0x03}}, {0x33, {0x90, 0x00, 0x02}}, + {0x33, {0x8c, 0xa1, 0x20}}, {0x33, {0x90, 0x00, 0x72}}, + {0x33, {0x8c, 0xa7, 0x02}}, {0x33, {0x90, 0x00, 0x01}}, }; static struct idxdata tbl_end_hvflip[] = { - {0x33, "\x8c\xa1\x02"}, {0x33, "\x90\x00\x1f"}, - {6, "\xff\xff\xff"}, - {0x33, "\x8c\xa1\x02"}, {0x33, "\x90\x00\x1f"}, - {6, "\xff\xff\xff"}, - {0x33, "\x8c\xa1\x02"}, {0x33, "\x90\x00\x1f"}, - {6, "\xff\xff\xff"}, - {0x33, "\x8c\xa1\x02"}, {0x33, "\x90\x00\x1f"}, + {0x33, {0x8c, 0xa1, 0x02}}, {0x33, {0x90, 0x00, 0x1f}}, + {6, {0xff, 0xff, 0xff}}, + {0x33, {0x8c, 0xa1, 0x02}}, {0x33, {0x90, 0x00, 0x1f}}, + {6, {0xff, 0xff, 0xff}}, + {0x33, {0x8c, 0xa1, 0x02}}, {0x33, {0x90, 0x00, 0x1f}}, + {6, {0xff, 0xff, 0xff}}, + {0x33, {0x8c, 0xa1, 0x02}}, {0x33, {0x90, 0x00, 0x1f}}, }; static u8 dat_freq1[] = { 0x8c, 0xa4, 0x04 }; @@ -82,197 +84,267 @@ static struct validx tbl_common_0B[] = { }; static struct idxdata tbl_common_3B[] = { - {0x33, "\x86\x25\x01"}, {0x33, "\x86\x25\x00"}, - {2, "\xff\xff\xff"}, - {0x30, "\x1a\x0a\xcc"}, {0x32, "\x02\x00\x08"}, {0x33, "\xf4\x03\x1d"}, - {6, "\xff\xff\xff"}, /* 12 */ - {0x34, "\x1e\x8f\x09"}, {0x34, "\x1c\x01\x28"}, {0x34, "\x1e\x8f\x09"}, - {2, "\xff\xff\xff"}, /* - */ - {0x34, "\x1e\x8f\x09"}, {0x32, "\x14\x06\xe6"}, {0x33, "\x8c\x22\x23"}, - {0x33, "\x90\x00\x00"}, {0x33, "\x8c\xa2\x0f"}, {0x33, "\x90\x00\x0d"}, - {0x33, "\x8c\xa2\x10"}, {0x33, "\x90\x00\x0b"}, {0x33, "\x8c\xa2\x11"}, - {0x33, "\x90\x00\x07"}, {0x33, "\xf4\x03\x1d"}, {0x35, "\xa2\x00\xe2"}, - {0x33, "\x8c\xab\x05"}, {0x33, "\x90\x00\x01"}, {0x32, "\x6e\x00\x86"}, - {0x32, "\x70\x0f\xaa"}, {0x32, "\x72\x0f\xe4"}, {0x33, "\x8c\xa3\x4a"}, - {0x33, "\x90\x00\x5a"}, {0x33, "\x8c\xa3\x4b"}, {0x33, "\x90\x00\xa6"}, - {0x33, "\x8c\xa3\x61"}, {0x33, "\x90\x00\xc8"}, {0x33, "\x8c\xa3\x62"}, - {0x33, "\x90\x00\xe1"}, {0x34, "\xce\x01\xa8"}, {0x34, "\xd0\x66\x33"}, - {0x34, "\xd2\x31\x9a"}, {0x34, "\xd4\x94\x63"}, {0x34, "\xd6\x4b\x25"}, - {0x34, "\xd8\x26\x70"}, {0x34, "\xda\x72\x4c"}, {0x34, "\xdc\xff\x04"}, - {0x34, "\xde\x01\x5b"}, {0x34, "\xe6\x01\x13"}, {0x34, "\xee\x0b\xf0"}, - {0x34, "\xf6\x0b\xa4"}, {0x35, "\x00\xf6\xe7"}, {0x35, "\x08\x0d\xfd"}, - {0x35, "\x10\x25\x63"}, {0x35, "\x18\x35\x6c"}, {0x35, "\x20\x42\x7e"}, - {0x35, "\x28\x19\x44"}, {0x35, "\x30\x39\xd4"}, {0x35, "\x38\xf5\xa8"}, - {0x35, "\x4c\x07\x90"}, {0x35, "\x44\x07\xb8"}, {0x35, "\x5c\x06\x88"}, - {0x35, "\x54\x07\xff"}, {0x34, "\xe0\x01\x52"}, {0x34, "\xe8\x00\xcc"}, - {0x34, "\xf0\x0d\x83"}, {0x34, "\xf8\x0c\xb3"}, {0x35, "\x02\xfe\xba"}, - {0x35, "\x0a\x04\xe0"}, {0x35, "\x12\x1c\x63"}, {0x35, "\x1a\x2b\x5a"}, - {0x35, "\x22\x32\x5e"}, {0x35, "\x2a\x0d\x28"}, {0x35, "\x32\x2c\x02"}, - {0x35, "\x3a\xf4\xfa"}, {0x35, "\x4e\x07\xef"}, {0x35, "\x46\x07\x88"}, - {0x35, "\x5e\x07\xc1"}, {0x35, "\x56\x04\x64"}, {0x34, "\xe4\x01\x15"}, - {0x34, "\xec\x00\x82"}, {0x34, "\xf4\x0c\xce"}, {0x34, "\xfc\x0c\xba"}, - {0x35, "\x06\x1f\x02"}, {0x35, "\x0e\x02\xe3"}, {0x35, "\x16\x1a\x50"}, - {0x35, "\x1e\x24\x39"}, {0x35, "\x26\x23\x4c"}, {0x35, "\x2e\xf9\x1b"}, - {0x35, "\x36\x23\x19"}, {0x35, "\x3e\x12\x08"}, {0x35, "\x52\x07\x22"}, - {0x35, "\x4a\x03\xd3"}, {0x35, "\x62\x06\x54"}, {0x35, "\x5a\x04\x5d"}, - {0x34, "\xe2\x01\x04"}, {0x34, "\xea\x00\xa0"}, {0x34, "\xf2\x0c\xbc"}, - {0x34, "\xfa\x0c\x5b"}, {0x35, "\x04\x17\xf2"}, {0x35, "\x0c\x02\x08"}, - {0x35, "\x14\x28\x43"}, {0x35, "\x1c\x28\x62"}, {0x35, "\x24\x2b\x60"}, - {0x35, "\x2c\x07\x33"}, {0x35, "\x34\x1f\xb0"}, {0x35, "\x3c\xed\xcd"}, - {0x35, "\x50\x00\x06"}, {0x35, "\x48\x07\xff"}, {0x35, "\x60\x05\x89"}, - {0x35, "\x58\x07\xff"}, {0x35, "\x40\x00\xa0"}, {0x35, "\x42\x00\x00"}, - {0x32, "\x10\x01\xfc"}, {0x33, "\x8c\xa1\x18"}, {0x33, "\x90\x00\x3c"}, - {0x33, "\x78\x00\x00"}, - {2, "\xff\xff\xff"}, - {0x35, "\xb8\x1f\x20"}, {0x33, "\x8c\xa2\x06"}, {0x33, "\x90\x00\x10"}, - {0x33, "\x8c\xa2\x07"}, {0x33, "\x90\x00\x08"}, {0x33, "\x8c\xa2\x42"}, - {0x33, "\x90\x00\x0b"}, {0x33, "\x8c\xa2\x4a"}, {0x33, "\x90\x00\x8c"}, - {0x35, "\xba\xfa\x08"}, {0x33, "\x8c\xa2\x02"}, {0x33, "\x90\x00\x22"}, - {0x33, "\x8c\xa2\x03"}, {0x33, "\x90\x00\xbb"}, {0x33, "\x8c\xa4\x04"}, - {0x33, "\x90\x00\x80"}, {0x33, "\x8c\xa7\x9d"}, {0x33, "\x90\x00\x00"}, - {0x33, "\x8c\xa7\x9e"}, {0x33, "\x90\x00\x00"}, {0x33, "\x8c\xa2\x0c"}, - {0x33, "\x90\x00\x17"}, {0x33, "\x8c\xa2\x15"}, {0x33, "\x90\x00\x04"}, - {0x33, "\x8c\xa2\x14"}, {0x33, "\x90\x00\x20"}, {0x33, "\x8c\xa1\x03"}, - {0x33, "\x90\x00\x00"}, {0x33, "\x8c\x27\x17"}, {0x33, "\x90\x21\x11"}, - {0x33, "\x8c\x27\x1b"}, {0x33, "\x90\x02\x4f"}, {0x33, "\x8c\x27\x25"}, - {0x33, "\x90\x06\x0f"}, {0x33, "\x8c\x27\x39"}, {0x33, "\x90\x21\x11"}, - {0x33, "\x8c\x27\x3d"}, {0x33, "\x90\x01\x20"}, {0x33, "\x8c\x27\x47"}, - {0x33, "\x90\x09\x4c"}, {0x33, "\x8c\x27\x03"}, {0x33, "\x90\x02\x84"}, - {0x33, "\x8c\x27\x05"}, {0x33, "\x90\x01\xe2"}, {0x33, "\x8c\x27\x07"}, - {0x33, "\x90\x06\x40"}, {0x33, "\x8c\x27\x09"}, {0x33, "\x90\x04\xb0"}, - {0x33, "\x8c\x27\x0d"}, {0x33, "\x90\x00\x00"}, {0x33, "\x8c\x27\x0f"}, - {0x33, "\x90\x00\x00"}, {0x33, "\x8c\x27\x11"}, {0x33, "\x90\x04\xbd"}, - {0x33, "\x8c\x27\x13"}, {0x33, "\x90\x06\x4d"}, {0x33, "\x8c\x27\x15"}, - {0x33, "\x90\x00\x00"}, {0x33, "\x8c\x27\x17"}, {0x33, "\x90\x21\x11"}, - {0x33, "\x8c\x27\x19"}, {0x33, "\x90\x04\x6c"}, {0x33, "\x8c\x27\x1b"}, - {0x33, "\x90\x02\x4f"}, {0x33, "\x8c\x27\x1d"}, {0x33, "\x90\x01\x02"}, - {0x33, "\x8c\x27\x1f"}, {0x33, "\x90\x02\x79"}, {0x33, "\x8c\x27\x21"}, - {0x33, "\x90\x01\x55"}, {0x33, "\x8c\x27\x23"}, {0x33, "\x90\x02\x85"}, - {0x33, "\x8c\x27\x25"}, {0x33, "\x90\x06\x0f"}, {0x33, "\x8c\x27\x27"}, - {0x33, "\x90\x20\x20"}, {0x33, "\x8c\x27\x29"}, {0x33, "\x90\x20\x20"}, - {0x33, "\x8c\x27\x2b"}, {0x33, "\x90\x10\x20"}, {0x33, "\x8c\x27\x2d"}, - {0x33, "\x90\x20\x07"}, {0x33, "\x8c\x27\x2f"}, {0x33, "\x90\x00\x04"}, - {0x33, "\x8c\x27\x31"}, {0x33, "\x90\x00\x04"}, {0x33, "\x8c\x27\x33"}, - {0x33, "\x90\x04\xbb"}, {0x33, "\x8c\x27\x35"}, {0x33, "\x90\x06\x4b"}, - {0x33, "\x8c\x27\x37"}, {0x33, "\x90\x00\x00"}, {0x33, "\x8c\x27\x39"}, - {0x33, "\x90\x21\x11"}, {0x33, "\x8c\x27\x3b"}, {0x33, "\x90\x00\x24"}, - {0x33, "\x8c\x27\x3d"}, {0x33, "\x90\x01\x20"}, {0x33, "\x8c\x27\x41"}, - {0x33, "\x90\x01\x69"}, {0x33, "\x8c\x27\x45"}, {0x33, "\x90\x04\xed"}, - {0x33, "\x8c\x27\x47"}, {0x33, "\x90\x09\x4c"}, {0x33, "\x8c\x27\x51"}, - {0x33, "\x90\x00\x00"}, {0x33, "\x8c\x27\x53"}, {0x33, "\x90\x03\x20"}, - {0x33, "\x8c\x27\x55"}, {0x33, "\x90\x00\x00"}, {0x33, "\x8c\x27\x57"}, - {0x33, "\x90\x02\x58"}, {0x33, "\x8c\x27\x5f"}, {0x33, "\x90\x00\x00"}, - {0x33, "\x8c\x27\x61"}, {0x33, "\x90\x06\x40"}, {0x33, "\x8c\x27\x63"}, - {0x33, "\x90\x00\x00"}, {0x33, "\x8c\x27\x65"}, {0x33, "\x90\x04\xb0"}, - {0x33, "\x8c\x22\x2e"}, {0x33, "\x90\x00\xa1"}, {0x33, "\x8c\xa4\x08"}, - {0x33, "\x90\x00\x1f"}, {0x33, "\x8c\xa4\x09"}, {0x33, "\x90\x00\x21"}, - {0x33, "\x8c\xa4\x0a"}, {0x33, "\x90\x00\x25"}, {0x33, "\x8c\xa4\x0b"}, - {0x33, "\x90\x00\x27"}, {0x33, "\x8c\x24\x11"}, {0x33, "\x90\x00\xa1"}, - {0x33, "\x8c\x24\x13"}, {0x33, "\x90\x00\xc1"}, {0x33, "\x8c\x24\x15"}, - {0x33, "\x90\x00\x6a"}, {0x33, "\x8c\x24\x17"}, {0x33, "\x90\x00\x80"}, - {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x05"}, - {2, "\xff\xff\xff"}, - {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x06"}, - {3, "\xff\xff\xff"}, + {0x33, {0x86, 0x25, 0x01}}, {0x33, {0x86, 0x25, 0x00}}, + {2, {0xff, 0xff, 0xff}}, + {0x30, {0x1a, 0x0a, 0xcc}}, {0x32, {0x02, 0x00, 0x08}}, + {0x33, {0xf4, 0x03, 0x1d}}, + {6, {0xff, 0xff, 0xff}}, /* 12 */ + {0x34, {0x1e, 0x8f, 0x09}}, {0x34, {0x1c, 0x01, 0x28}}, + {0x34, {0x1e, 0x8f, 0x09}}, + {2, {0xff, 0xff, 0xff}}, /* - */ + {0x34, {0x1e, 0x8f, 0x09}}, {0x32, {0x14, 0x06, 0xe6}}, + {0x33, {0x8c, 0x22, 0x23}}, {0x33, {0x90, 0x00, 0x00}}, + {0x33, {0x8c, 0xa2, 0x0f}}, {0x33, {0x90, 0x00, 0x0d}}, + {0x33, {0x8c, 0xa2, 0x10}}, {0x33, {0x90, 0x00, 0x0b}}, + {0x33, {0x8c, 0xa2, 0x11}}, {0x33, {0x90, 0x00, 0x07}}, + {0x33, {0xf4, 0x03, 0x1d}}, {0x35, {0xa2, 0x00, 0xe2}}, + {0x33, {0x8c, 0xab, 0x05}}, {0x33, {0x90, 0x00, 0x01}}, + {0x32, {0x6e, 0x00, 0x86}}, {0x32, {0x70, 0x0f, 0xaa}}, + {0x32, {0x72, 0x0f, 0xe4}}, {0x33, {0x8c, 0xa3, 0x4a}}, + {0x33, {0x90, 0x00, 0x5a}}, {0x33, {0x8c, 0xa3, 0x4b}}, + {0x33, {0x90, 0x00, 0xa6}}, {0x33, {0x8c, 0xa3, 0x61}}, + {0x33, {0x90, 0x00, 0xc8}}, {0x33, {0x8c, 0xa3, 0x62}}, + {0x33, {0x90, 0x00, 0xe1}}, {0x34, {0xce, 0x01, 0xa8}}, + {0x34, {0xd0, 0x66, 0x33}}, {0x34, {0xd2, 0x31, 0x9a}}, + {0x34, {0xd4, 0x94, 0x63}}, {0x34, {0xd6, 0x4b, 0x25}}, + {0x34, {0xd8, 0x26, 0x70}}, {0x34, {0xda, 0x72, 0x4c}}, + {0x34, {0xdc, 0xff, 0x04}}, {0x34, {0xde, 0x01, 0x5b}}, + {0x34, {0xe6, 0x01, 0x13}}, {0x34, {0xee, 0x0b, 0xf0}}, + {0x34, {0xf6, 0x0b, 0xa4}}, {0x35, {0x00, 0xf6, 0xe7}}, + {0x35, {0x08, 0x0d, 0xfd}}, {0x35, {0x10, 0x25, 0x63}}, + {0x35, {0x18, 0x35, 0x6c}}, {0x35, {0x20, 0x42, 0x7e}}, + {0x35, {0x28, 0x19, 0x44}}, {0x35, {0x30, 0x39, 0xd4}}, + {0x35, {0x38, 0xf5, 0xa8}}, {0x35, {0x4c, 0x07, 0x90}}, + {0x35, {0x44, 0x07, 0xb8}}, {0x35, {0x5c, 0x06, 0x88}}, + {0x35, {0x54, 0x07, 0xff}}, {0x34, {0xe0, 0x01, 0x52}}, + {0x34, {0xe8, 0x00, 0xcc}}, {0x34, {0xf0, 0x0d, 0x83}}, + {0x34, {0xf8, 0x0c, 0xb3}}, {0x35, {0x02, 0xfe, 0xba}}, + {0x35, {0x0a, 0x04, 0xe0}}, {0x35, {0x12, 0x1c, 0x63}}, + {0x35, {0x1a, 0x2b, 0x5a}}, {0x35, {0x22, 0x32, 0x5e}}, + {0x35, {0x2a, 0x0d, 0x28}}, {0x35, {0x32, 0x2c, 0x02}}, + {0x35, {0x3a, 0xf4, 0xfa}}, {0x35, {0x4e, 0x07, 0xef}}, + {0x35, {0x46, 0x07, 0x88}}, {0x35, {0x5e, 0x07, 0xc1}}, + {0x35, {0x56, 0x04, 0x64}}, {0x34, {0xe4, 0x01, 0x15}}, + {0x34, {0xec, 0x00, 0x82}}, {0x34, {0xf4, 0x0c, 0xce}}, + {0x34, {0xfc, 0x0c, 0xba}}, {0x35, {0x06, 0x1f, 0x02}}, + {0x35, {0x0e, 0x02, 0xe3}}, {0x35, {0x16, 0x1a, 0x50}}, + {0x35, {0x1e, 0x24, 0x39}}, {0x35, {0x26, 0x23, 0x4c}}, + {0x35, {0x2e, 0xf9, 0x1b}}, {0x35, {0x36, 0x23, 0x19}}, + {0x35, {0x3e, 0x12, 0x08}}, {0x35, {0x52, 0x07, 0x22}}, + {0x35, {0x4a, 0x03, 0xd3}}, {0x35, {0x62, 0x06, 0x54}}, + {0x35, {0x5a, 0x04, 0x5d}}, {0x34, {0xe2, 0x01, 0x04}}, + {0x34, {0xea, 0x00, 0xa0}}, {0x34, {0xf2, 0x0c, 0xbc}}, + {0x34, {0xfa, 0x0c, 0x5b}}, {0x35, {0x04, 0x17, 0xf2}}, + {0x35, {0x0c, 0x02, 0x08}}, {0x35, {0x14, 0x28, 0x43}}, + {0x35, {0x1c, 0x28, 0x62}}, {0x35, {0x24, 0x2b, 0x60}}, + {0x35, {0x2c, 0x07, 0x33}}, {0x35, {0x34, 0x1f, 0xb0}}, + {0x35, {0x3c, 0xed, 0xcd}}, {0x35, {0x50, 0x00, 0x06}}, + {0x35, {0x48, 0x07, 0xff}}, {0x35, {0x60, 0x05, 0x89}}, + {0x35, {0x58, 0x07, 0xff}}, {0x35, {0x40, 0x00, 0xa0}}, + {0x35, {0x42, 0x00, 0x00}}, {0x32, {0x10, 0x01, 0xfc}}, + {0x33, {0x8c, 0xa1, 0x18}}, {0x33, {0x90, 0x00, 0x3c}}, + {0x33, {0x78, 0x00, 0x00}}, + {2, {0xff, 0xff, 0xff}}, + {0x35, {0xb8, 0x1f, 0x20}}, {0x33, {0x8c, 0xa2, 0x06}}, + {0x33, {0x90, 0x00, 0x10}}, {0x33, {0x8c, 0xa2, 0x07}}, + {0x33, {0x90, 0x00, 0x08}}, {0x33, {0x8c, 0xa2, 0x42}}, + {0x33, {0x90, 0x00, 0x0b}}, {0x33, {0x8c, 0xa2, 0x4a}}, + {0x33, {0x90, 0x00, 0x8c}}, {0x35, {0xba, 0xfa, 0x08}}, + {0x33, {0x8c, 0xa2, 0x02}}, {0x33, {0x90, 0x00, 0x22}}, + {0x33, {0x8c, 0xa2, 0x03}}, {0x33, {0x90, 0x00, 0xbb}}, + {0x33, {0x8c, 0xa4, 0x04}}, {0x33, {0x90, 0x00, 0x80}}, + {0x33, {0x8c, 0xa7, 0x9d}}, {0x33, {0x90, 0x00, 0x00}}, + {0x33, {0x8c, 0xa7, 0x9e}}, {0x33, {0x90, 0x00, 0x00}}, + {0x33, {0x8c, 0xa2, 0x0c}}, {0x33, {0x90, 0x00, 0x17}}, + {0x33, {0x8c, 0xa2, 0x15}}, {0x33, {0x90, 0x00, 0x04}}, + {0x33, {0x8c, 0xa2, 0x14}}, {0x33, {0x90, 0x00, 0x20}}, + {0x33, {0x8c, 0xa1, 0x03}}, {0x33, {0x90, 0x00, 0x00}}, + {0x33, {0x8c, 0x27, 0x17}}, {0x33, {0x90, 0x21, 0x11}}, + {0x33, {0x8c, 0x27, 0x1b}}, {0x33, {0x90, 0x02, 0x4f}}, + {0x33, {0x8c, 0x27, 0x25}}, {0x33, {0x90, 0x06, 0x0f}}, + {0x33, {0x8c, 0x27, 0x39}}, {0x33, {0x90, 0x21, 0x11}}, + {0x33, {0x8c, 0x27, 0x3d}}, {0x33, {0x90, 0x01, 0x20}}, + {0x33, {0x8c, 0x27, 0x47}}, {0x33, {0x90, 0x09, 0x4c}}, + {0x33, {0x8c, 0x27, 0x03}}, {0x33, {0x90, 0x02, 0x84}}, + {0x33, {0x8c, 0x27, 0x05}}, {0x33, {0x90, 0x01, 0xe2}}, + {0x33, {0x8c, 0x27, 0x07}}, {0x33, {0x90, 0x06, 0x40}}, + {0x33, {0x8c, 0x27, 0x09}}, {0x33, {0x90, 0x04, 0xb0}}, + {0x33, {0x8c, 0x27, 0x0d}}, {0x33, {0x90, 0x00, 0x00}}, + {0x33, {0x8c, 0x27, 0x0f}}, {0x33, {0x90, 0x00, 0x00}}, + {0x33, {0x8c, 0x27, 0x11}}, {0x33, {0x90, 0x04, 0xbd}}, + {0x33, {0x8c, 0x27, 0x13}}, {0x33, {0x90, 0x06, 0x4d}}, + {0x33, {0x8c, 0x27, 0x15}}, {0x33, {0x90, 0x00, 0x00}}, + {0x33, {0x8c, 0x27, 0x17}}, {0x33, {0x90, 0x21, 0x11}}, + {0x33, {0x8c, 0x27, 0x19}}, {0x33, {0x90, 0x04, 0x6c}}, + {0x33, {0x8c, 0x27, 0x1b}}, {0x33, {0x90, 0x02, 0x4f}}, + {0x33, {0x8c, 0x27, 0x1d}}, {0x33, {0x90, 0x01, 0x02}}, + {0x33, {0x8c, 0x27, 0x1f}}, {0x33, {0x90, 0x02, 0x79}}, + {0x33, {0x8c, 0x27, 0x21}}, {0x33, {0x90, 0x01, 0x55}}, + {0x33, {0x8c, 0x27, 0x23}}, {0x33, {0x90, 0x02, 0x85}}, + {0x33, {0x8c, 0x27, 0x25}}, {0x33, {0x90, 0x06, 0x0f}}, + {0x33, {0x8c, 0x27, 0x27}}, {0x33, {0x90, 0x20, 0x20}}, + {0x33, {0x8c, 0x27, 0x29}}, {0x33, {0x90, 0x20, 0x20}}, + {0x33, {0x8c, 0x27, 0x2b}}, {0x33, {0x90, 0x10, 0x20}}, + {0x33, {0x8c, 0x27, 0x2d}}, {0x33, {0x90, 0x20, 0x07}}, + {0x33, {0x8c, 0x27, 0x2f}}, {0x33, {0x90, 0x00, 0x04}}, + {0x33, {0x8c, 0x27, 0x31}}, {0x33, {0x90, 0x00, 0x04}}, + {0x33, {0x8c, 0x27, 0x33}}, {0x33, {0x90, 0x04, 0xbb}}, + {0x33, {0x8c, 0x27, 0x35}}, {0x33, {0x90, 0x06, 0x4b}}, + {0x33, {0x8c, 0x27, 0x37}}, {0x33, {0x90, 0x00, 0x00}}, + {0x33, {0x8c, 0x27, 0x39}}, {0x33, {0x90, 0x21, 0x11}}, + {0x33, {0x8c, 0x27, 0x3b}}, {0x33, {0x90, 0x00, 0x24}}, + {0x33, {0x8c, 0x27, 0x3d}}, {0x33, {0x90, 0x01, 0x20}}, + {0x33, {0x8c, 0x27, 0x41}}, {0x33, {0x90, 0x01, 0x69}}, + {0x33, {0x8c, 0x27, 0x45}}, {0x33, {0x90, 0x04, 0xed}}, + {0x33, {0x8c, 0x27, 0x47}}, {0x33, {0x90, 0x09, 0x4c}}, + {0x33, {0x8c, 0x27, 0x51}}, {0x33, {0x90, 0x00, 0x00}}, + {0x33, {0x8c, 0x27, 0x53}}, {0x33, {0x90, 0x03, 0x20}}, + {0x33, {0x8c, 0x27, 0x55}}, {0x33, {0x90, 0x00, 0x00}}, + {0x33, {0x8c, 0x27, 0x57}}, {0x33, {0x90, 0x02, 0x58}}, + {0x33, {0x8c, 0x27, 0x5f}}, {0x33, {0x90, 0x00, 0x00}}, + {0x33, {0x8c, 0x27, 0x61}}, {0x33, {0x90, 0x06, 0x40}}, + {0x33, {0x8c, 0x27, 0x63}}, {0x33, {0x90, 0x00, 0x00}}, + {0x33, {0x8c, 0x27, 0x65}}, {0x33, {0x90, 0x04, 0xb0}}, + {0x33, {0x8c, 0x22, 0x2e}}, {0x33, {0x90, 0x00, 0xa1}}, + {0x33, {0x8c, 0xa4, 0x08}}, {0x33, {0x90, 0x00, 0x1f}}, + {0x33, {0x8c, 0xa4, 0x09}}, {0x33, {0x90, 0x00, 0x21}}, + {0x33, {0x8c, 0xa4, 0x0a}}, {0x33, {0x90, 0x00, 0x25}}, + {0x33, {0x8c, 0xa4, 0x0b}}, {0x33, {0x90, 0x00, 0x27}}, + {0x33, {0x8c, 0x24, 0x11}}, {0x33, {0x90, 0x00, 0xa1}}, + {0x33, {0x8c, 0x24, 0x13}}, {0x33, {0x90, 0x00, 0xc1}}, + {0x33, {0x8c, 0x24, 0x15}}, {0x33, {0x90, 0x00, 0x6a}}, + {0x33, {0x8c, 0x24, 0x17}}, {0x33, {0x90, 0x00, 0x80}}, + {0x33, {0x8c, 0xa1, 0x03}}, {0x33, {0x90, 0x00, 0x05}}, + {2, {0xff, 0xff, 0xff}}, + {0x33, {0x8c, 0xa1, 0x03}}, {0x33, {0x90, 0x00, 0x06}}, + {3, {0xff, 0xff, 0xff}}, }; static struct idxdata tbl_init_post_alt_low1[] = { - {0x33, "\x8c\x27\x15"}, {0x33, "\x90\x00\x25"}, {0x33, "\x8c\x22\x2e"}, - {0x33, "\x90\x00\x81"}, {0x33, "\x8c\xa4\x08"}, {0x33, "\x90\x00\x17"}, - {0x33, "\x8c\xa4\x09"}, {0x33, "\x90\x00\x1a"}, {0x33, "\x8c\xa4\x0a"}, - {0x33, "\x90\x00\x1d"}, {0x33, "\x8c\xa4\x0b"}, {0x33, "\x90\x00\x20"}, - {0x33, "\x8c\x24\x11"}, {0x33, "\x90\x00\x81"}, {0x33, "\x8c\x24\x13"}, - {0x33, "\x90\x00\x9b"}, + {0x33, {0x8c, 0x27, 0x15}}, {0x33, {0x90, 0x00, 0x25}}, + {0x33, {0x8c, 0x22, 0x2e}}, {0x33, {0x90, 0x00, 0x81}}, + {0x33, {0x8c, 0xa4, 0x08}}, {0x33, {0x90, 0x00, 0x17}}, + {0x33, {0x8c, 0xa4, 0x09}}, {0x33, {0x90, 0x00, 0x1a}}, + {0x33, {0x8c, 0xa4, 0x0a}}, {0x33, {0x90, 0x00, 0x1d}}, + {0x33, {0x8c, 0xa4, 0x0b}}, {0x33, {0x90, 0x00, 0x20}}, + {0x33, {0x8c, 0x24, 0x11}}, {0x33, {0x90, 0x00, 0x81}}, + {0x33, {0x8c, 0x24, 0x13}}, {0x33, {0x90, 0x00, 0x9b}}, }; static struct idxdata tbl_init_post_alt_low2[] = { - {0x33, "\x8c\x27\x03"}, {0x33, "\x90\x03\x24"}, {0x33, "\x8c\x27\x05"}, - {0x33, "\x90\x02\x58"}, {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x05"}, - {2, "\xff\xff\xff"}, - {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x06"}, - {2, "\xff\xff\xff"}, + {0x33, {0x8c, 0x27, 0x03}}, {0x33, {0x90, 0x03, 0x24}}, + {0x33, {0x8c, 0x27, 0x05}}, {0x33, {0x90, 0x02, 0x58}}, + {0x33, {0x8c, 0xa1, 0x03}}, {0x33, {0x90, 0x00, 0x05}}, + {2, {0xff, 0xff, 0xff}}, + {0x33, {0x8c, 0xa1, 0x03}}, {0x33, {0x90, 0x00, 0x06}}, + {2, {0xff, 0xff, 0xff}}, }; static struct idxdata tbl_init_post_alt_low3[] = { - {0x34, "\x1e\x8f\x09"}, {0x34, "\x1c\x01\x28"}, {0x34, "\x1e\x8f\x09"}, - {2, "\xff\xff\xff"}, - {0x34, "\x1e\x8f\x09"}, {0x32, "\x14\x06\xe6"}, {0x33, "\x8c\xa1\x20"}, - {0x33, "\x90\x00\x00"}, {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x01"}, - {0x33, "\x2e\x01\x00"}, {0x34, "\x04\x00\x2a"}, {0x33, "\x8c\xa7\x02"}, - {0x33, "\x90\x00\x00"}, {0x33, "\x8c\x27\x95"}, {0x33, "\x90\x01\x00"}, - {2, "\xff\xff\xff"}, - {0x33, "\x8c\xa1\x20"}, {0x33, "\x90\x00\x72"}, {0x33, "\x8c\xa1\x03"}, - {0x33, "\x90\x00\x02"}, {0x33, "\x8c\xa7\x02"}, {0x33, "\x90\x00\x01"}, - {2, "\xff\xff\xff"}, - {0x33, "\x8c\xa1\x20"}, {0x33, "\x90\x00\x00"}, {0x33, "\x8c\xa1\x03"}, - {0x33, "\x90\x00\x01"}, {0x33, "\x8c\xa7\x02"}, {0x33, "\x90\x00\x00"}, - {2, "\xff\xff\xff"}, - {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x05"}, - {2, "\xff\xff\xff"}, - {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x06"}, - {2, "\xff\xff\xff"}, - {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x05"}, - {2, "\xff\xff\xff"}, - {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x06"}, + {0x34, {0x1e, 0x8f, 0x09}}, {0x34, {0x1c, 0x01, 0x28}}, + {0x34, {0x1e, 0x8f, 0x09}}, + {2, {0xff, 0xff, 0xff}}, + {0x34, {0x1e, 0x8f, 0x09}}, {0x32, {0x14, 0x06, 0xe6}}, + {0x33, {0x8c, 0xa1, 0x20}}, {0x33, {0x90, 0x00, 0x00}}, + {0x33, {0x8c, 0xa1, 0x03}}, {0x33, {0x90, 0x00, 0x01}}, + {0x33, {0x2e, 0x01, 0x00}}, {0x34, {0x04, 0x00, 0x2a}}, + {0x33, {0x8c, 0xa7, 0x02}}, {0x33, {0x90, 0x00, 0x00}}, + {0x33, {0x8c, 0x27, 0x95}}, {0x33, {0x90, 0x01, 0x00}}, + {2, {0xff, 0xff, 0xff}}, + {0x33, {0x8c, 0xa1, 0x20}}, {0x33, {0x90, 0x00, 0x72}}, + {0x33, {0x8c, 0xa1, 0x03}}, {0x33, {0x90, 0x00, 0x02}}, + {0x33, {0x8c, 0xa7, 0x02}}, {0x33, {0x90, 0x00, 0x01}}, + {2, {0xff, 0xff, 0xff}}, + {0x33, {0x8c, 0xa1, 0x20}}, {0x33, {0x90, 0x00, 0x00}}, + {0x33, {0x8c, 0xa1, 0x03}}, {0x33, {0x90, 0x00, 0x01}}, + {0x33, {0x8c, 0xa7, 0x02}}, {0x33, {0x90, 0x00, 0x00}}, + {2, {0xff, 0xff, 0xff}}, + {0x33, {0x8c, 0xa1, 0x03}}, {0x33, {0x90, 0x00, 0x05}}, + {2, {0xff, 0xff, 0xff}}, + {0x33, {0x8c, 0xa1, 0x03}}, {0x33, {0x90, 0x00, 0x06}}, + {2, {0xff, 0xff, 0xff}}, + {0x33, {0x8c, 0xa1, 0x03}}, {0x33, {0x90, 0x00, 0x05}}, + {2, {0xff, 0xff, 0xff}}, + {0x33, {0x8c, 0xa1, 0x03}}, {0x33, {0x90, 0x00, 0x06}}, }; static struct idxdata tbl_init_post_alt_big[] = { - {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x05"}, - {2, "\xff\xff\xff"}, - {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x06"}, - {2, "\xff\xff\xff"}, - {0x34, "\x1e\x8f\x09"}, {0x34, "\x1c\x01\x28"}, {0x34, "\x1e\x8f\x09"}, - {2, "\xff\xff\xff"}, - {0x34, "\x1e\x8f\x09"}, {0x32, "\x14\x06\xe6"}, {0x33, "\x8c\xa1\x03"}, - {0x33, "\x90\x00\x05"}, - {2, "\xff\xff\xff"}, - {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x06"}, - {2, "\xff\xff\xff"}, - {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x05"}, - {2, "\xff\xff\xff"}, - {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x06"}, {0x33, "\x8c\xa1\x20"}, - {0x33, "\x90\x00\x72"}, {0x33, "\x8c\xa1\x30"}, {0x33, "\x90\x00\x03"}, - {0x33, "\x8c\xa1\x31"}, {0x33, "\x90\x00\x02"}, {0x33, "\x8c\xa1\x32"}, - {0x33, "\x90\x00\x03"}, {0x33, "\x8c\xa1\x34"}, {0x33, "\x90\x00\x03"}, - {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x02"}, {0x33, "\x2e\x01\x00"}, - {0x34, "\x04\x00\x2a"}, {0x33, "\x8c\xa7\x02"}, {0x33, "\x90\x00\x01"}, - {0x33, "\x8c\x27\x97"}, {0x33, "\x90\x01\x00"}, - {51, "\xff\xff\xff"}, - {0x33, "\x8c\xa1\x20"}, {0x33, "\x90\x00\x00"}, {0x33, "\x8c\xa1\x03"}, - {0x33, "\x90\x00\x01"}, {0x33, "\x8c\xa7\x02"}, {0x33, "\x90\x00\x00"}, - {51, "\xff\xff\xff"}, - {0x33, "\x8c\xa1\x20"}, {0x33, "\x90\x00\x72"}, {0x33, "\x8c\xa1\x03"}, - {0x33, "\x90\x00\x02"}, {0x33, "\x8c\xa7\x02"}, {0x33, "\x90\x00\x01"}, - {51, "\xff\xff\xff"}, + {0x33, {0x8c, 0xa1, 0x03}}, {0x33, {0x90, 0x00, 0x05}}, + {2, {0xff, 0xff, 0xff}}, + {0x33, {0x8c, 0xa1, 0x03}}, {0x33, {0x90, 0x00, 0x06}}, + {2, {0xff, 0xff, 0xff}}, + {0x34, {0x1e, 0x8f, 0x09}}, {0x34, {0x1c, 0x01, 0x28}}, + {0x34, {0x1e, 0x8f, 0x09}}, + {2, {0xff, 0xff, 0xff}}, + {0x34, {0x1e, 0x8f, 0x09}}, {0x32, {0x14, 0x06, 0xe6}}, + {0x33, {0x8c, 0xa1, 0x03}}, + {0x33, {0x90, 0x00, 0x05}}, + {2, {0xff, 0xff, 0xff}}, + {0x33, {0x8c, 0xa1, 0x03}}, {0x33, {0x90, 0x00, 0x06}}, + {2, {0xff, 0xff, 0xff}}, + {0x33, {0x8c, 0xa1, 0x03}}, {0x33, {0x90, 0x00, 0x05}}, + {2, {0xff, 0xff, 0xff}}, + {0x33, {0x8c, 0xa1, 0x03}}, {0x33, {0x90, 0x00, 0x06}}, + {0x33, {0x8c, 0xa1, 0x20}}, {0x33, {0x90, 0x00, 0x72}}, + {0x33, {0x8c, 0xa1, 0x30}}, {0x33, {0x90, 0x00, 0x03}}, + {0x33, {0x8c, 0xa1, 0x31}}, {0x33, {0x90, 0x00, 0x02}}, + {0x33, {0x8c, 0xa1, 0x32}}, {0x33, {0x90, 0x00, 0x03}}, + {0x33, {0x8c, 0xa1, 0x34}}, {0x33, {0x90, 0x00, 0x03}}, + {0x33, {0x8c, 0xa1, 0x03}}, {0x33, {0x90, 0x00, 0x02}}, + {0x33, {0x2e, 0x01, 0x00}}, {0x34, {0x04, 0x00, 0x2a}}, + {0x33, {0x8c, 0xa7, 0x02}}, {0x33, {0x90, 0x00, 0x01}}, + {0x33, {0x8c, 0x27, 0x97}}, {0x33, {0x90, 0x01, 0x00}}, + {51, {0xff, 0xff, 0xff}}, + {0x33, {0x8c, 0xa1, 0x20}}, {0x33, {0x90, 0x00, 0x00}}, + {0x33, {0x8c, 0xa1, 0x03}}, {0x33, {0x90, 0x00, 0x01}}, + {0x33, {0x8c, 0xa7, 0x02}}, {0x33, {0x90, 0x00, 0x00}}, + {51, {0xff, 0xff, 0xff}}, + {0x33, {0x8c, 0xa1, 0x20}}, {0x33, {0x90, 0x00, 0x72}}, + {0x33, {0x8c, 0xa1, 0x03}}, {0x33, {0x90, 0x00, 0x02}}, + {0x33, {0x8c, 0xa7, 0x02}}, {0x33, {0x90, 0x00, 0x01}}, + {51, {0xff, 0xff, 0xff}}, }; static struct idxdata tbl_init_post_alt_3B[] = { - {0x32, "\x10\x01\xf8"}, {0x34, "\xce\x01\xa8"}, {0x34, "\xd0\x66\x33"}, - {0x34, "\xd2\x31\x9a"}, {0x34, "\xd4\x94\x63"}, {0x34, "\xd6\x4b\x25"}, - {0x34, "\xd8\x26\x70"}, {0x34, "\xda\x72\x4c"}, {0x34, "\xdc\xff\x04"}, - {0x34, "\xde\x01\x5b"}, {0x34, "\xe6\x01\x13"}, {0x34, "\xee\x0b\xf0"}, - {0x34, "\xf6\x0b\xa4"}, {0x35, "\x00\xf6\xe7"}, {0x35, "\x08\x0d\xfd"}, - {0x35, "\x10\x25\x63"}, {0x35, "\x18\x35\x6c"}, {0x35, "\x20\x42\x7e"}, - {0x35, "\x28\x19\x44"}, {0x35, "\x30\x39\xd4"}, {0x35, "\x38\xf5\xa8"}, - {0x35, "\x4c\x07\x90"}, {0x35, "\x44\x07\xb8"}, {0x35, "\x5c\x06\x88"}, - {0x35, "\x54\x07\xff"}, {0x34, "\xe0\x01\x52"}, {0x34, "\xe8\x00\xcc"}, - {0x34, "\xf0\x0d\x83"}, {0x34, "\xf8\x0c\xb3"}, {0x35, "\x02\xfe\xba"}, - {0x35, "\x0a\x04\xe0"}, {0x35, "\x12\x1c\x63"}, {0x35, "\x1a\x2b\x5a"}, - {0x35, "\x22\x32\x5e"}, {0x35, "\x2a\x0d\x28"}, {0x35, "\x32\x2c\x02"}, - {0x35, "\x3a\xf4\xfa"}, {0x35, "\x4e\x07\xef"}, {0x35, "\x46\x07\x88"}, - {0x35, "\x5e\x07\xc1"}, {0x35, "\x56\x04\x64"}, {0x34, "\xe4\x01\x15"}, - {0x34, "\xec\x00\x82"}, {0x34, "\xf4\x0c\xce"}, {0x34, "\xfc\x0c\xba"}, - {0x35, "\x06\x1f\x02"}, {0x35, "\x0e\x02\xe3"}, {0x35, "\x16\x1a\x50"}, - {0x35, "\x1e\x24\x39"}, {0x35, "\x26\x23\x4c"}, {0x35, "\x2e\xf9\x1b"}, - {0x35, "\x36\x23\x19"}, {0x35, "\x3e\x12\x08"}, {0x35, "\x52\x07\x22"}, - {0x35, "\x4a\x03\xd3"}, {0x35, "\x62\x06\x54"}, {0x35, "\x5a\x04\x5d"}, - {0x34, "\xe2\x01\x04"}, {0x34, "\xea\x00\xa0"}, {0x34, "\xf2\x0c\xbc"}, - {0x34, "\xfa\x0c\x5b"}, {0x35, "\x04\x17\xf2"}, {0x35, "\x0c\x02\x08"}, - {0x35, "\x14\x28\x43"}, {0x35, "\x1c\x28\x62"}, {0x35, "\x24\x2b\x60"}, - {0x35, "\x2c\x07\x33"}, {0x35, "\x34\x1f\xb0"}, {0x35, "\x3c\xed\xcd"}, - {0x35, "\x50\x00\x06"}, {0x35, "\x48\x07\xff"}, {0x35, "\x60\x05\x89"}, - {0x35, "\x58\x07\xff"}, {0x35, "\x40\x00\xa0"}, {0x35, "\x42\x00\x00"}, - {0x32, "\x10\x01\xfc"}, {0x33, "\x8c\xa1\x18"}, {0x33, "\x90\x00\x3c"}, + {0x32, {0x10, 0x01, 0xf8}}, {0x34, {0xce, 0x01, 0xa8}}, + {0x34, {0xd0, 0x66, 0x33}}, {0x34, {0xd2, 0x31, 0x9a}}, + {0x34, {0xd4, 0x94, 0x63}}, {0x34, {0xd6, 0x4b, 0x25}}, + {0x34, {0xd8, 0x26, 0x70}}, {0x34, {0xda, 0x72, 0x4c}}, + {0x34, {0xdc, 0xff, 0x04}}, {0x34, {0xde, 0x01, 0x5b}}, + {0x34, {0xe6, 0x01, 0x13}}, {0x34, {0xee, 0x0b, 0xf0}}, + {0x34, {0xf6, 0x0b, 0xa4}}, {0x35, {0x00, 0xf6, 0xe7}}, + {0x35, {0x08, 0x0d, 0xfd}}, {0x35, {0x10, 0x25, 0x63}}, + {0x35, {0x18, 0x35, 0x6c}}, {0x35, {0x20, 0x42, 0x7e}}, + {0x35, {0x28, 0x19, 0x44}}, {0x35, {0x30, 0x39, 0xd4}}, + {0x35, {0x38, 0xf5, 0xa8}}, {0x35, {0x4c, 0x07, 0x90}}, + {0x35, {0x44, 0x07, 0xb8}}, {0x35, {0x5c, 0x06, 0x88}}, + {0x35, {0x54, 0x07, 0xff}}, {0x34, {0xe0, 0x01, 0x52}}, + {0x34, {0xe8, 0x00, 0xcc}}, {0x34, {0xf0, 0x0d, 0x83}}, + {0x34, {0xf8, 0x0c, 0xb3}}, {0x35, {0x02, 0xfe, 0xba}}, + {0x35, {0x0a, 0x04, 0xe0}}, {0x35, {0x12, 0x1c, 0x63}}, + {0x35, {0x1a, 0x2b, 0x5a}}, {0x35, {0x22, 0x32, 0x5e}}, + {0x35, {0x2a, 0x0d, 0x28}}, {0x35, {0x32, 0x2c, 0x02}}, + {0x35, {0x3a, 0xf4, 0xfa}}, {0x35, {0x4e, 0x07, 0xef}}, + {0x35, {0x46, 0x07, 0x88}}, {0x35, {0x5e, 0x07, 0xc1}}, + {0x35, {0x56, 0x04, 0x64}}, {0x34, {0xe4, 0x01, 0x15}}, + {0x34, {0xec, 0x00, 0x82}}, {0x34, {0xf4, 0x0c, 0xce}}, + {0x34, {0xfc, 0x0c, 0xba}}, {0x35, {0x06, 0x1f, 0x02}}, + {0x35, {0x0e, 0x02, 0xe3}}, {0x35, {0x16, 0x1a, 0x50}}, + {0x35, {0x1e, 0x24, 0x39}}, {0x35, {0x26, 0x23, 0x4c}}, + {0x35, {0x2e, 0xf9, 0x1b}}, {0x35, {0x36, 0x23, 0x19}}, + {0x35, {0x3e, 0x12, 0x08}}, {0x35, {0x52, 0x07, 0x22}}, + {0x35, {0x4a, 0x03, 0xd3}}, {0x35, {0x62, 0x06, 0x54}}, + {0x35, {0x5a, 0x04, 0x5d}}, {0x34, {0xe2, 0x01, 0x04}}, + {0x34, {0xea, 0x00, 0xa0}}, {0x34, {0xf2, 0x0c, 0xbc}}, + {0x34, {0xfa, 0x0c, 0x5b}}, {0x35, {0x04, 0x17, 0xf2}}, + {0x35, {0x0c, 0x02, 0x08}}, {0x35, {0x14, 0x28, 0x43}}, + {0x35, {0x1c, 0x28, 0x62}}, {0x35, {0x24, 0x2b, 0x60}}, + {0x35, {0x2c, 0x07, 0x33}}, {0x35, {0x34, 0x1f, 0xb0}}, + {0x35, {0x3c, 0xed, 0xcd}}, {0x35, {0x50, 0x00, 0x06}}, + {0x35, {0x48, 0x07, 0xff}}, {0x35, {0x60, 0x05, 0x89}}, + {0x35, {0x58, 0x07, 0xff}}, {0x35, {0x40, 0x00, 0xa0}}, + {0x35, {0x42, 0x00, 0x00}}, {0x32, {0x10, 0x01, 0xfc}}, + {0x33, {0x8c, 0xa1, 0x18}}, {0x33, {0x90, 0x00, 0x3c}}, }; static u8 *dat_640 = "\xd0\x02\xd1\x08\xd2\xe1\xd3\x02\xd4\x10\xd5\x81"; diff --git a/drivers/media/usb/pvrusb2/pvrusb2-hdw.c b/drivers/media/usb/pvrusb2/pvrusb2-hdw.c index ea05f678b559..9623b6218214 100644 --- a/drivers/media/usb/pvrusb2/pvrusb2-hdw.c +++ b/drivers/media/usb/pvrusb2/pvrusb2-hdw.c @@ -2910,7 +2910,7 @@ static void pvr2_subdev_update(struct pvr2_hdw *hdw) v4l2_std_id vs; vs = hdw->std_mask_cur; v4l2_device_call_all(&hdw->v4l2_dev, 0, - core, s_std, vs); + video, s_std, vs); pvr2_hdw_cx25840_vbi_hack(hdw); } hdw->tuner_signal_stale = !0; diff --git a/drivers/media/usb/pwc/pwc-if.c b/drivers/media/usb/pwc/pwc-if.c index 84a6720b1d00..a73b0bced96f 100644 --- a/drivers/media/usb/pwc/pwc-if.c +++ b/drivers/media/usb/pwc/pwc-if.c @@ -681,12 +681,11 @@ static int start_streaming(struct vb2_queue *vq, unsigned int count) return r; } -static int stop_streaming(struct vb2_queue *vq) +static void stop_streaming(struct vb2_queue *vq) { struct pwc_device *pdev = vb2_get_drv_priv(vq); - if (mutex_lock_interruptible(&pdev->v4l2_lock)) - return -ERESTARTSYS; + mutex_lock(&pdev->v4l2_lock); if (pdev->udev) { pwc_set_leds(pdev, 0, 0); pwc_camera_power(pdev, 0); @@ -695,8 +694,6 @@ static int stop_streaming(struct vb2_queue *vq) pwc_cleanup_queued_bufs(pdev); mutex_unlock(&pdev->v4l2_lock); - - return 0; } static struct vb2_ops pwc_vb_queue_ops = { diff --git a/drivers/media/usb/s2255/s2255drv.c b/drivers/media/usb/s2255/s2255drv.c index 1d4ba2b80490..a44466bc7b86 100644 --- a/drivers/media/usb/s2255/s2255drv.c +++ b/drivers/media/usb/s2255/s2255drv.c @@ -714,7 +714,7 @@ static void buffer_queue(struct vb2_buffer *vb) } static int start_streaming(struct vb2_queue *vq, unsigned int count); -static int stop_streaming(struct vb2_queue *vq); +static void stop_streaming(struct vb2_queue *vq); static struct vb2_ops s2255_video_qops = { .queue_setup = queue_setup, @@ -1109,7 +1109,7 @@ static int start_streaming(struct vb2_queue *vq, unsigned int count) } /* abort streaming and wait for last buffer */ -static int stop_streaming(struct vb2_queue *vq) +static void stop_streaming(struct vb2_queue *vq) { struct s2255_vc *vc = vb2_get_drv_priv(vq); struct s2255_buffer *buf, *node; @@ -1123,7 +1123,6 @@ static int stop_streaming(struct vb2_queue *vq) buf, buf->vb.v4l2_buf.index); } spin_unlock_irqrestore(&vc->qlock, flags); - return 0; } static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id i) @@ -1522,7 +1521,7 @@ static void s2255_destroy(struct s2255_dev *dev) /* board shutdown stops the read pipe if it is running */ s2255_board_shutdown(dev); /* make sure firmware still not trying to load */ - del_timer(&dev->timer); /* only started in .probe and .open */ + del_timer_sync(&dev->timer); /* only started in .probe and .open */ if (dev->fw_data->fw_urb) { usb_kill_urb(dev->fw_data->fw_urb); usb_free_urb(dev->fw_data->fw_urb); @@ -2243,7 +2242,7 @@ static int s2255_probe(struct usb_interface *interface, dev->cmdbuf = kzalloc(S2255_CMDBUF_SIZE, GFP_KERNEL); if (dev->cmdbuf == NULL) { s2255_dev_err(&interface->dev, "out of memory\n"); - return -ENOMEM; + goto errorFWDATA1; } atomic_set(&dev->num_channels, 0); @@ -2352,7 +2351,7 @@ errorREQFW: errorFWDATA2: usb_free_urb(dev->fw_data->fw_urb); errorFWURB: - del_timer(&dev->timer); + del_timer_sync(&dev->timer); errorEP: usb_put_dev(dev->udev); errorUDEV: diff --git a/drivers/media/usb/stk1160/stk1160-core.c b/drivers/media/usb/stk1160/stk1160-core.c index 34a26e0cfe77..03504dcf3c52 100644 --- a/drivers/media/usb/stk1160/stk1160-core.c +++ b/drivers/media/usb/stk1160/stk1160-core.c @@ -67,17 +67,25 @@ int stk1160_read_reg(struct stk1160 *dev, u16 reg, u8 *value) { int ret; int pipe = usb_rcvctrlpipe(dev->udev, 0); + u8 *buf; *value = 0; + + buf = kmalloc(sizeof(u8), GFP_KERNEL); + if (!buf) + return -ENOMEM; ret = usb_control_msg(dev->udev, pipe, 0x00, USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - 0x00, reg, value, sizeof(u8), HZ); + 0x00, reg, buf, sizeof(u8), HZ); if (ret < 0) { stk1160_err("read failed on reg 0x%x (%d)\n", reg, ret); + kfree(buf); return ret; } + *value = *buf; + kfree(buf); return 0; } diff --git a/drivers/media/usb/stk1160/stk1160-v4l.c b/drivers/media/usb/stk1160/stk1160-v4l.c index 37bc00f418f1..5461341a31cb 100644 --- a/drivers/media/usb/stk1160/stk1160-v4l.c +++ b/drivers/media/usb/stk1160/stk1160-v4l.c @@ -406,7 +406,7 @@ static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id norm) stk1160_set_std(dev); - v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_std, + v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_std, dev->norm); return 0; @@ -583,10 +583,10 @@ static int start_streaming(struct vb2_queue *vq, unsigned int count) } /* abort streaming and wait for last buffer */ -static int stop_streaming(struct vb2_queue *vq) +static void stop_streaming(struct vb2_queue *vq) { struct stk1160 *dev = vb2_get_drv_priv(vq); - return stk1160_stop_streaming(dev); + stk1160_stop_streaming(dev); } static struct vb2_ops stk1160_video_qops = { @@ -682,7 +682,7 @@ int stk1160_video_register(struct stk1160 *dev) dev->fmt = &format[0]; stk1160_set_std(dev); - v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_std, + v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_std, dev->norm); video_set_drvdata(&dev->vdev, dev); diff --git a/drivers/media/usb/stk1160/stk1160.h b/drivers/media/usb/stk1160/stk1160.h index 05b05b160e1e..abdea484c998 100644 --- a/drivers/media/usb/stk1160/stk1160.h +++ b/drivers/media/usb/stk1160/stk1160.h @@ -143,7 +143,6 @@ struct stk1160 { int num_alt; struct stk1160_isoc_ctl isoc_ctl; - char urb_buf[255]; /* urb control msg buffer */ /* frame properties */ int width; /* current frame width */ diff --git a/drivers/media/usb/tm6000/tm6000-cards.c b/drivers/media/usb/tm6000/tm6000-cards.c index 1ccaaddaa307..2e8c3afe4ec4 100644 --- a/drivers/media/usb/tm6000/tm6000-cards.c +++ b/drivers/media/usb/tm6000/tm6000-cards.c @@ -1120,7 +1120,7 @@ static int tm6000_init_dev(struct tm6000_core *dev) tm6000_config_tuner(dev); /* Set video standard */ - v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_std, dev->norm); + v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_std, dev->norm); /* Set tuner frequency - also loads firmware on xc2028/xc3028 */ f.tuner = 0; diff --git a/drivers/media/usb/tm6000/tm6000-video.c b/drivers/media/usb/tm6000/tm6000-video.c index cc1aa14996ff..e6b3d5d83d43 100644 --- a/drivers/media/usb/tm6000/tm6000-video.c +++ b/drivers/media/usb/tm6000/tm6000-video.c @@ -1071,7 +1071,7 @@ static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id norm) if (rc < 0) return rc; - v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_std, dev->norm); + v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_std, dev->norm); return 0; } diff --git a/drivers/media/usb/usbtv/usbtv-video.c b/drivers/media/usb/usbtv/usbtv-video.c index 20365bd69d05..2967e808408b 100644 --- a/drivers/media/usb/usbtv/usbtv-video.c +++ b/drivers/media/usb/usbtv/usbtv-video.c @@ -634,15 +634,12 @@ static int usbtv_start_streaming(struct vb2_queue *vq, unsigned int count) return usbtv_start(usbtv); } -static int usbtv_stop_streaming(struct vb2_queue *vq) +static void usbtv_stop_streaming(struct vb2_queue *vq) { struct usbtv *usbtv = vb2_get_drv_priv(vq); - if (usbtv->udev == NULL) - return -ENODEV; - - usbtv_stop(usbtv); - return 0; + if (usbtv->udev) + usbtv_stop(usbtv); } static struct vb2_ops usbtv_vb2_ops = { diff --git a/drivers/media/usb/usbvision/usbvision-video.c b/drivers/media/usb/usbvision/usbvision-video.c index 5c9e3123ad2e..68bc9615660e 100644 --- a/drivers/media/usb/usbvision/usbvision-video.c +++ b/drivers/media/usb/usbvision/usbvision-video.c @@ -597,7 +597,7 @@ static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id id) usbvision->tvnorm_id = id; - call_all(usbvision, core, s_std, usbvision->tvnorm_id); + call_all(usbvision, video, s_std, usbvision->tvnorm_id); /* propagate the change to the decoder */ usbvision_muxsel(usbvision, usbvision->ctl_input); diff --git a/drivers/media/usb/uvc/uvc_video.c b/drivers/media/usb/uvc/uvc_video.c index 8d52baf5952b..9144a2f3ed82 100644 --- a/drivers/media/usb/uvc/uvc_video.c +++ b/drivers/media/usb/uvc/uvc_video.c @@ -361,6 +361,14 @@ static int uvc_commit_video(struct uvc_streaming *stream, * Clocks and timestamps */ +static inline void uvc_video_get_ts(struct timespec *ts) +{ + if (uvc_clock_param == CLOCK_MONOTONIC) + ktime_get_ts(ts); + else + ktime_get_real_ts(ts); +} + static void uvc_video_clock_decode(struct uvc_streaming *stream, struct uvc_buffer *buf, const __u8 *data, int len) @@ -420,7 +428,7 @@ uvc_video_clock_decode(struct uvc_streaming *stream, struct uvc_buffer *buf, stream->clock.last_sof = dev_sof; host_sof = usb_get_current_frame_number(stream->dev->udev); - ktime_get_ts(&ts); + uvc_video_get_ts(&ts); /* The UVC specification allows device implementations that can't obtain * the USB frame number to keep their own frame counters as long as they @@ -1011,10 +1019,7 @@ static int uvc_video_decode_start(struct uvc_streaming *stream, return -ENODATA; } - if (uvc_clock_param == CLOCK_MONOTONIC) - ktime_get_ts(&ts); - else - ktime_get_real_ts(&ts); + uvc_video_get_ts(&ts); buf->buf.v4l2_buf.sequence = stream->sequence; buf->buf.v4l2_buf.timestamp.tv_sec = ts.tv_sec; @@ -1133,6 +1138,17 @@ static int uvc_video_encode_data(struct uvc_streaming *stream, */ /* + * Set error flag for incomplete buffer. + */ +static void uvc_video_validate_buffer(const struct uvc_streaming *stream, + struct uvc_buffer *buf) +{ + if (buf->length != buf->bytesused && + !(stream->cur_format->flags & UVC_FMT_FLAG_COMPRESSED)) + buf->error = 1; +} + +/* * Completion handler for video URBs. */ static void uvc_video_decode_isoc(struct urb *urb, struct uvc_streaming *stream, @@ -1156,9 +1172,11 @@ static void uvc_video_decode_isoc(struct urb *urb, struct uvc_streaming *stream, do { ret = uvc_video_decode_start(stream, buf, mem, urb->iso_frame_desc[i].actual_length); - if (ret == -EAGAIN) + if (ret == -EAGAIN) { + uvc_video_validate_buffer(stream, buf); buf = uvc_queue_next_buffer(&stream->queue, buf); + } } while (ret == -EAGAIN); if (ret < 0) @@ -1173,11 +1191,7 @@ static void uvc_video_decode_isoc(struct urb *urb, struct uvc_streaming *stream, urb->iso_frame_desc[i].actual_length); if (buf->state == UVC_BUF_STATE_READY) { - if (buf->length != buf->bytesused && - !(stream->cur_format->flags & - UVC_FMT_FLAG_COMPRESSED)) - buf->error = 1; - + uvc_video_validate_buffer(stream, buf); buf = uvc_queue_next_buffer(&stream->queue, buf); } } diff --git a/drivers/media/v4l2-core/Kconfig b/drivers/media/v4l2-core/Kconfig index 2189bfb2e828..9ca0f8d59a14 100644 --- a/drivers/media/v4l2-core/Kconfig +++ b/drivers/media/v4l2-core/Kconfig @@ -83,3 +83,7 @@ config VIDEOBUF2_DMA_SG #depends on HAS_DMA select VIDEOBUF2_CORE select VIDEOBUF2_MEMOPS + +config VIDEOBUF2_DVB + tristate + select VIDEOBUF2_CORE diff --git a/drivers/media/v4l2-core/Makefile b/drivers/media/v4l2-core/Makefile index c6ae7bad951e..63d29f27538c 100644 --- a/drivers/media/v4l2-core/Makefile +++ b/drivers/media/v4l2-core/Makefile @@ -33,6 +33,7 @@ obj-$(CONFIG_VIDEOBUF2_MEMOPS) += videobuf2-memops.o obj-$(CONFIG_VIDEOBUF2_VMALLOC) += videobuf2-vmalloc.o obj-$(CONFIG_VIDEOBUF2_DMA_CONTIG) += videobuf2-dma-contig.o obj-$(CONFIG_VIDEOBUF2_DMA_SG) += videobuf2-dma-sg.o +obj-$(CONFIG_VIDEOBUF2_DVB) += videobuf2-dvb.o ccflags-y += -I$(srctree)/drivers/media/dvb-core ccflags-y += -I$(srctree)/drivers/media/dvb-frontends diff --git a/drivers/media/v4l2-core/tuner-core.c b/drivers/media/v4l2-core/tuner-core.c index 20c09229a08e..06c18ba16fa0 100644 --- a/drivers/media/v4l2-core/tuner-core.c +++ b/drivers/media/v4l2-core/tuner-core.c @@ -1301,7 +1301,6 @@ static int tuner_command(struct i2c_client *client, unsigned cmd, void *arg) static const struct v4l2_subdev_core_ops tuner_core_ops = { .log_status = tuner_log_status, - .s_std = tuner_s_std, .s_power = tuner_s_power, }; @@ -1315,9 +1314,14 @@ static const struct v4l2_subdev_tuner_ops tuner_tuner_ops = { .s_config = tuner_s_config, }; +static const struct v4l2_subdev_video_ops tuner_video_ops = { + .s_std = tuner_s_std, +}; + static const struct v4l2_subdev_ops tuner_ops = { .core = &tuner_core_ops, .tuner = &tuner_tuner_ops, + .video = &tuner_video_ops, }; /* diff --git a/drivers/media/v4l2-core/v4l2-device.c b/drivers/media/v4l2-core/v4l2-device.c index 02d1b6327117..015f92aab44a 100644 --- a/drivers/media/v4l2-core/v4l2-device.c +++ b/drivers/media/v4l2-core/v4l2-device.c @@ -158,7 +158,17 @@ int v4l2_device_register_subdev(struct v4l2_device *v4l2_dev, /* Warn if we apparently re-register a subdev */ WARN_ON(sd->v4l2_dev != NULL); - if (!try_module_get(sd->owner)) + /* + * The reason to acquire the module here is to avoid unloading + * a module of sub-device which is registered to a media + * device. To make it possible to unload modules for media + * devices that also register sub-devices, do not + * try_module_get() such sub-device owners. + */ + sd->owner_v4l2_dev = v4l2_dev->dev && v4l2_dev->dev->driver && + sd->owner == v4l2_dev->dev->driver->owner; + + if (!sd->owner_v4l2_dev && !try_module_get(sd->owner)) return -ENODEV; sd->v4l2_dev = v4l2_dev; @@ -192,7 +202,8 @@ error_unregister: if (sd->internal_ops && sd->internal_ops->unregistered) sd->internal_ops->unregistered(sd); error_module: - module_put(sd->owner); + if (!sd->owner_v4l2_dev) + module_put(sd->owner); sd->v4l2_dev = NULL; return err; } @@ -280,6 +291,7 @@ void v4l2_device_unregister_subdev(struct v4l2_subdev *sd) } #endif video_unregister_device(sd->devnode); - module_put(sd->owner); + if (!sd->owner_v4l2_dev) + module_put(sd->owner); } EXPORT_SYMBOL_GPL(v4l2_device_unregister_subdev); diff --git a/drivers/media/v4l2-core/v4l2-dv-timings.c b/drivers/media/v4l2-core/v4l2-dv-timings.c index 48b20dfcc4d0..4ae54caadd03 100644 --- a/drivers/media/v4l2-core/v4l2-dv-timings.c +++ b/drivers/media/v4l2-core/v4l2-dv-timings.c @@ -131,6 +131,17 @@ const struct v4l2_dv_timings v4l2_dv_timings_presets[] = { V4L2_DV_BT_DMT_2560X1600P75, V4L2_DV_BT_DMT_2560X1600P85, V4L2_DV_BT_DMT_2560X1600P120_RB, + V4L2_DV_BT_CEA_3840X2160P24, + V4L2_DV_BT_CEA_3840X2160P25, + V4L2_DV_BT_CEA_3840X2160P30, + V4L2_DV_BT_CEA_3840X2160P50, + V4L2_DV_BT_CEA_3840X2160P60, + V4L2_DV_BT_CEA_4096X2160P24, + V4L2_DV_BT_CEA_4096X2160P25, + V4L2_DV_BT_CEA_4096X2160P30, + V4L2_DV_BT_CEA_4096X2160P50, + V4L2_DV_BT_DMT_4096X2160P59_94_RB, + V4L2_DV_BT_CEA_4096X2160P60, { } }; EXPORT_SYMBOL_GPL(v4l2_dv_timings_presets); diff --git a/drivers/media/v4l2-core/v4l2-event.c b/drivers/media/v4l2-core/v4l2-event.c index 86dcb5483c42..8761aab99de9 100644 --- a/drivers/media/v4l2-core/v4l2-event.c +++ b/drivers/media/v4l2-core/v4l2-event.c @@ -318,3 +318,39 @@ int v4l2_event_subdev_unsubscribe(struct v4l2_subdev *sd, struct v4l2_fh *fh, return v4l2_event_unsubscribe(fh, sub); } EXPORT_SYMBOL_GPL(v4l2_event_subdev_unsubscribe); + +static void v4l2_event_src_replace(struct v4l2_event *old, + const struct v4l2_event *new) +{ + u32 old_changes = old->u.src_change.changes; + + old->u.src_change = new->u.src_change; + old->u.src_change.changes |= old_changes; +} + +static void v4l2_event_src_merge(const struct v4l2_event *old, + struct v4l2_event *new) +{ + new->u.src_change.changes |= old->u.src_change.changes; +} + +static const struct v4l2_subscribed_event_ops v4l2_event_src_ch_ops = { + .replace = v4l2_event_src_replace, + .merge = v4l2_event_src_merge, +}; + +int v4l2_src_change_event_subscribe(struct v4l2_fh *fh, + const struct v4l2_event_subscription *sub) +{ + if (sub->type == V4L2_EVENT_SOURCE_CHANGE) + return v4l2_event_subscribe(fh, sub, 0, &v4l2_event_src_ch_ops); + return -EINVAL; +} +EXPORT_SYMBOL_GPL(v4l2_src_change_event_subscribe); + +int v4l2_src_change_event_subdev_subscribe(struct v4l2_subdev *sd, + struct v4l2_fh *fh, struct v4l2_event_subscription *sub) +{ + return v4l2_src_change_event_subscribe(fh, sub); +} +EXPORT_SYMBOL_GPL(v4l2_src_change_event_subdev_subscribe); diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c index d9113cc71c77..16bffd851bf9 100644 --- a/drivers/media/v4l2-core/v4l2-ioctl.c +++ b/drivers/media/v4l2-core/v4l2-ioctl.c @@ -562,7 +562,7 @@ static void v4l_print_cropcap(const void *arg, bool write_only) const struct v4l2_cropcap *p = arg; pr_cont("type=%s, bounds wxh=%dx%d, x,y=%d,%d, " - "defrect wxh=%dx%d, x,y=%d,%d\n, " + "defrect wxh=%dx%d, x,y=%d,%d, " "pixelaspect %d/%d\n", prt_names(p->type, v4l2_type_names), p->bounds.width, p->bounds.height, @@ -2260,7 +2260,7 @@ done: } static int check_array_args(unsigned int cmd, void *parg, size_t *array_size, - void * __user *user_ptr, void ***kernel_ptr) + void __user **user_ptr, void ***kernel_ptr) { int ret = 0; @@ -2277,7 +2277,7 @@ static int check_array_args(unsigned int cmd, void *parg, size_t *array_size, break; } *user_ptr = (void __user *)buf->m.planes; - *kernel_ptr = (void *)&buf->m.planes; + *kernel_ptr = (void **)&buf->m.planes; *array_size = sizeof(struct v4l2_plane) * buf->length; ret = 1; } @@ -2294,7 +2294,7 @@ static int check_array_args(unsigned int cmd, void *parg, size_t *array_size, break; } *user_ptr = (void __user *)edid->edid; - *kernel_ptr = (void *)&edid->edid; + *kernel_ptr = (void **)&edid->edid; *array_size = edid->blocks * 128; ret = 1; } @@ -2312,7 +2312,7 @@ static int check_array_args(unsigned int cmd, void *parg, size_t *array_size, break; } *user_ptr = (void __user *)ctrls->controls; - *kernel_ptr = (void *)&ctrls->controls; + *kernel_ptr = (void **)&ctrls->controls; *array_size = sizeof(struct v4l2_ext_control) * ctrls->count; ret = 1; @@ -2412,7 +2412,7 @@ video_usercopy(struct file *file, unsigned int cmd, unsigned long arg, } if (has_array_args) { - *kernel_ptr = user_ptr; + *kernel_ptr = (void __force *)user_ptr; if (copy_to_user(user_ptr, mbuf, array_size)) err = -EFAULT; goto out_array_args; diff --git a/drivers/media/v4l2-core/v4l2-subdev.c b/drivers/media/v4l2-core/v4l2-subdev.c index aea84ac5688a..058c1a6e8392 100644 --- a/drivers/media/v4l2-core/v4l2-subdev.c +++ b/drivers/media/v4l2-core/v4l2-subdev.c @@ -305,11 +305,23 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg) fse); } - case VIDIOC_SUBDEV_G_FRAME_INTERVAL: + case VIDIOC_SUBDEV_G_FRAME_INTERVAL: { + struct v4l2_subdev_frame_interval *fi = arg; + + if (fi->pad >= sd->entity.num_pads) + return -EINVAL; + return v4l2_subdev_call(sd, video, g_frame_interval, arg); + } + + case VIDIOC_SUBDEV_S_FRAME_INTERVAL: { + struct v4l2_subdev_frame_interval *fi = arg; + + if (fi->pad >= sd->entity.num_pads) + return -EINVAL; - case VIDIOC_SUBDEV_S_FRAME_INTERVAL: return v4l2_subdev_call(sd, video, s_frame_interval, arg); + } case VIDIOC_SUBDEV_ENUM_FRAME_INTERVAL: { struct v4l2_subdev_frame_interval_enum *fie = arg; @@ -349,11 +361,54 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg) sd, pad, set_selection, subdev_fh, sel); } - case VIDIOC_G_EDID: - return v4l2_subdev_call(sd, pad, get_edid, arg); + case VIDIOC_G_EDID: { + struct v4l2_subdev_edid *edid = arg; + + if (edid->pad >= sd->entity.num_pads) + return -EINVAL; + if (edid->blocks && edid->edid == NULL) + return -EINVAL; + + return v4l2_subdev_call(sd, pad, get_edid, edid); + } + + case VIDIOC_S_EDID: { + struct v4l2_subdev_edid *edid = arg; + + if (edid->pad >= sd->entity.num_pads) + return -EINVAL; + if (edid->blocks && edid->edid == NULL) + return -EINVAL; + + return v4l2_subdev_call(sd, pad, set_edid, edid); + } + + case VIDIOC_SUBDEV_DV_TIMINGS_CAP: { + struct v4l2_dv_timings_cap *cap = arg; + + if (cap->pad >= sd->entity.num_pads) + return -EINVAL; + + return v4l2_subdev_call(sd, pad, dv_timings_cap, cap); + } + + case VIDIOC_SUBDEV_ENUM_DV_TIMINGS: { + struct v4l2_enum_dv_timings *dvt = arg; + + if (dvt->pad >= sd->entity.num_pads) + return -EINVAL; + + return v4l2_subdev_call(sd, pad, enum_dv_timings, dvt); + } + + case VIDIOC_SUBDEV_QUERY_DV_TIMINGS: + return v4l2_subdev_call(sd, video, query_dv_timings, arg); + + case VIDIOC_SUBDEV_G_DV_TIMINGS: + return v4l2_subdev_call(sd, video, g_dv_timings, arg); - case VIDIOC_S_EDID: - return v4l2_subdev_call(sd, pad, set_edid, arg); + case VIDIOC_SUBDEV_S_DV_TIMINGS: + return v4l2_subdev_call(sd, video, s_dv_timings, arg); #endif default: return v4l2_subdev_call(sd, core, ioctl, cmd, arg); diff --git a/drivers/media/v4l2-core/videobuf-dma-contig.c b/drivers/media/v4l2-core/videobuf-dma-contig.c index 7e6b209b7002..bf80f0f7dfb8 100644 --- a/drivers/media/v4l2-core/videobuf-dma-contig.c +++ b/drivers/media/v4l2-core/videobuf-dma-contig.c @@ -305,7 +305,7 @@ static int __videobuf_mmap_mapper(struct videobuf_queue *q, /* Try to remap memory */ size = vma->vm_end - vma->vm_start; vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); - retval = vm_iomap_memory(vma, vma->vm_start, size); + retval = vm_iomap_memory(vma, mem->dma_handle, size); if (retval) { dev_err(q->dev, "mmap: remap failed with error %d. ", retval); diff --git a/drivers/media/v4l2-core/videobuf2-core.c b/drivers/media/v4l2-core/videobuf2-core.c index f9059bb73840..349e659d75fb 100644 --- a/drivers/media/v4l2-core/videobuf2-core.c +++ b/drivers/media/v4l2-core/videobuf2-core.c @@ -6,6 +6,9 @@ * Author: Pawel Osciak <pawel@osciak.com> * Marek Szyprowski <m.szyprowski@samsung.com> * + * The vb2_thread implementation was based on code from videobuf-dvb.c: + * (c) 2004 Gerd Knorr <kraxel@bytesex.org> [SUSE Labs] + * * 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. @@ -18,76 +21,154 @@ #include <linux/poll.h> #include <linux/slab.h> #include <linux/sched.h> +#include <linux/freezer.h> +#include <linux/kthread.h> #include <media/v4l2-dev.h> #include <media/v4l2-fh.h> #include <media/v4l2-event.h> +#include <media/v4l2-common.h> #include <media/videobuf2-core.h> static int debug; module_param(debug, int, 0644); -#define dprintk(level, fmt, arg...) \ - do { \ - if (debug >= level) \ - printk(KERN_DEBUG "vb2: " fmt, ## arg); \ +#define dprintk(level, fmt, arg...) \ + do { \ + if (debug >= level) \ + pr_debug("vb2: %s: " fmt, __func__, ## arg); \ } while (0) #ifdef CONFIG_VIDEO_ADV_DEBUG /* - * If advanced debugging is on, then count how often each op is called, - * which can either be per-buffer or per-queue. + * If advanced debugging is on, then count how often each op is called + * successfully, which can either be per-buffer or per-queue. * - * If the op failed then the 'fail_' variant is called to decrease the - * counter. That makes it easy to check that the 'init' and 'cleanup' + * This makes it easy to check that the 'init' and 'cleanup' * (and variations thereof) stay balanced. */ +#define log_memop(vb, op) \ + dprintk(2, "call_memop(%p, %d, %s)%s\n", \ + (vb)->vb2_queue, (vb)->v4l2_buf.index, #op, \ + (vb)->vb2_queue->mem_ops->op ? "" : " (nop)") + #define call_memop(vb, op, args...) \ ({ \ struct vb2_queue *_q = (vb)->vb2_queue; \ - dprintk(2, "call_memop(%p, %d, %s)%s\n", \ - _q, (vb)->v4l2_buf.index, #op, \ - _q->mem_ops->op ? "" : " (nop)"); \ + int err; \ + \ + log_memop(vb, op); \ + err = _q->mem_ops->op ? _q->mem_ops->op(args) : 0; \ + if (!err) \ + (vb)->cnt_mem_ ## op++; \ + err; \ +}) + +#define call_ptr_memop(vb, op, args...) \ +({ \ + struct vb2_queue *_q = (vb)->vb2_queue; \ + void *ptr; \ + \ + log_memop(vb, op); \ + ptr = _q->mem_ops->op ? _q->mem_ops->op(args) : NULL; \ + if (!IS_ERR_OR_NULL(ptr)) \ + (vb)->cnt_mem_ ## op++; \ + ptr; \ +}) + +#define call_void_memop(vb, op, args...) \ +({ \ + struct vb2_queue *_q = (vb)->vb2_queue; \ + \ + log_memop(vb, op); \ + if (_q->mem_ops->op) \ + _q->mem_ops->op(args); \ (vb)->cnt_mem_ ## op++; \ - _q->mem_ops->op ? _q->mem_ops->op(args) : 0; \ }) -#define fail_memop(vb, op) ((vb)->cnt_mem_ ## op--) + +#define log_qop(q, op) \ + dprintk(2, "call_qop(%p, %s)%s\n", q, #op, \ + (q)->ops->op ? "" : " (nop)") #define call_qop(q, op, args...) \ ({ \ - dprintk(2, "call_qop(%p, %s)%s\n", q, #op, \ - (q)->ops->op ? "" : " (nop)"); \ + int err; \ + \ + log_qop(q, op); \ + err = (q)->ops->op ? (q)->ops->op(args) : 0; \ + if (!err) \ + (q)->cnt_ ## op++; \ + err; \ +}) + +#define call_void_qop(q, op, args...) \ +({ \ + log_qop(q, op); \ + if ((q)->ops->op) \ + (q)->ops->op(args); \ (q)->cnt_ ## op++; \ - (q)->ops->op ? (q)->ops->op(args) : 0; \ }) -#define fail_qop(q, op) ((q)->cnt_ ## op--) + +#define log_vb_qop(vb, op, args...) \ + dprintk(2, "call_vb_qop(%p, %d, %s)%s\n", \ + (vb)->vb2_queue, (vb)->v4l2_buf.index, #op, \ + (vb)->vb2_queue->ops->op ? "" : " (nop)") #define call_vb_qop(vb, op, args...) \ ({ \ - struct vb2_queue *_q = (vb)->vb2_queue; \ - dprintk(2, "call_vb_qop(%p, %d, %s)%s\n", \ - _q, (vb)->v4l2_buf.index, #op, \ - _q->ops->op ? "" : " (nop)"); \ + int err; \ + \ + log_vb_qop(vb, op); \ + err = (vb)->vb2_queue->ops->op ? \ + (vb)->vb2_queue->ops->op(args) : 0; \ + if (!err) \ + (vb)->cnt_ ## op++; \ + err; \ +}) + +#define call_void_vb_qop(vb, op, args...) \ +({ \ + log_vb_qop(vb, op); \ + if ((vb)->vb2_queue->ops->op) \ + (vb)->vb2_queue->ops->op(args); \ (vb)->cnt_ ## op++; \ - _q->ops->op ? _q->ops->op(args) : 0; \ }) -#define fail_vb_qop(vb, op) ((vb)->cnt_ ## op--) #else #define call_memop(vb, op, args...) \ - ((vb)->vb2_queue->mem_ops->op ? (vb)->vb2_queue->mem_ops->op(args) : 0) -#define fail_memop(vb, op) + ((vb)->vb2_queue->mem_ops->op ? \ + (vb)->vb2_queue->mem_ops->op(args) : 0) + +#define call_ptr_memop(vb, op, args...) \ + ((vb)->vb2_queue->mem_ops->op ? \ + (vb)->vb2_queue->mem_ops->op(args) : NULL) + +#define call_void_memop(vb, op, args...) \ + do { \ + if ((vb)->vb2_queue->mem_ops->op) \ + (vb)->vb2_queue->mem_ops->op(args); \ + } while (0) #define call_qop(q, op, args...) \ ((q)->ops->op ? (q)->ops->op(args) : 0) -#define fail_qop(q, op) + +#define call_void_qop(q, op, args...) \ + do { \ + if ((q)->ops->op) \ + (q)->ops->op(args); \ + } while (0) #define call_vb_qop(vb, op, args...) \ ((vb)->vb2_queue->ops->op ? (vb)->vb2_queue->ops->op(args) : 0) -#define fail_vb_qop(vb, op) + +#define call_void_vb_qop(vb, op, args...) \ + do { \ + if ((vb)->vb2_queue->ops->op) \ + (vb)->vb2_queue->ops->op(args); \ + } while (0) #endif @@ -118,7 +199,7 @@ static int __vb2_buf_mem_alloc(struct vb2_buffer *vb) for (plane = 0; plane < vb->num_planes; ++plane) { unsigned long size = PAGE_ALIGN(q->plane_sizes[plane]); - mem_priv = call_memop(vb, alloc, q->alloc_ctx[plane], + mem_priv = call_ptr_memop(vb, alloc, q->alloc_ctx[plane], size, q->gfp_flags); if (IS_ERR_OR_NULL(mem_priv)) goto free; @@ -130,10 +211,9 @@ static int __vb2_buf_mem_alloc(struct vb2_buffer *vb) return 0; free: - fail_memop(vb, alloc); /* Free already allocated memory if one of the allocations failed */ for (; plane > 0; --plane) { - call_memop(vb, put, vb->planes[plane - 1].mem_priv); + call_void_memop(vb, put, vb->planes[plane - 1].mem_priv); vb->planes[plane - 1].mem_priv = NULL; } @@ -148,9 +228,9 @@ static void __vb2_buf_mem_free(struct vb2_buffer *vb) unsigned int plane; for (plane = 0; plane < vb->num_planes; ++plane) { - call_memop(vb, put, vb->planes[plane].mem_priv); + call_void_memop(vb, put, vb->planes[plane].mem_priv); vb->planes[plane].mem_priv = NULL; - dprintk(3, "Freed plane %d of buffer %d\n", plane, + dprintk(3, "freed plane %d of buffer %d\n", plane, vb->v4l2_buf.index); } } @@ -165,7 +245,7 @@ static void __vb2_buf_userptr_put(struct vb2_buffer *vb) for (plane = 0; plane < vb->num_planes; ++plane) { if (vb->planes[plane].mem_priv) - call_memop(vb, put_userptr, vb->planes[plane].mem_priv); + call_void_memop(vb, put_userptr, vb->planes[plane].mem_priv); vb->planes[plane].mem_priv = NULL; } } @@ -180,9 +260,9 @@ static void __vb2_plane_dmabuf_put(struct vb2_buffer *vb, struct vb2_plane *p) return; if (p->dbuf_mapped) - call_memop(vb, unmap_dmabuf, p->mem_priv); + call_void_memop(vb, unmap_dmabuf, p->mem_priv); - call_memop(vb, detach_dmabuf, p->mem_priv); + call_void_memop(vb, detach_dmabuf, p->mem_priv); dma_buf_put(p->dbuf); memset(p, 0, sizeof(*p)); } @@ -245,7 +325,7 @@ static void __setup_offsets(struct vb2_queue *q, unsigned int n) for (plane = 0; plane < vb->num_planes; ++plane) { vb->v4l2_planes[plane].m.mem_offset = off; - dprintk(3, "Buffer %d, plane %d offset 0x%08lx\n", + dprintk(3, "buffer %d, plane %d offset 0x%08lx\n", buffer, plane, off); off += vb->v4l2_planes[plane].length; @@ -272,7 +352,7 @@ static int __vb2_queue_alloc(struct vb2_queue *q, enum v4l2_memory memory, /* Allocate videobuf buffer structures */ vb = kzalloc(q->buf_struct_size, GFP_KERNEL); if (!vb) { - dprintk(1, "Memory alloc for buffer struct failed\n"); + dprintk(1, "memory alloc for buffer struct failed\n"); break; } @@ -291,7 +371,7 @@ static int __vb2_queue_alloc(struct vb2_queue *q, enum v4l2_memory memory, if (memory == V4L2_MEMORY_MMAP) { ret = __vb2_buf_mem_alloc(vb); if (ret) { - dprintk(1, "Failed allocating memory for " + dprintk(1, "failed allocating memory for " "buffer %d\n", buffer); kfree(vb); break; @@ -303,9 +383,8 @@ static int __vb2_queue_alloc(struct vb2_queue *q, enum v4l2_memory memory, */ ret = call_vb_qop(vb, buf_init, vb); if (ret) { - dprintk(1, "Buffer %d %p initialization" + dprintk(1, "buffer %d %p initialization" " failed\n", buffer, vb); - fail_vb_qop(vb, buf_init); __vb2_buf_mem_free(vb); kfree(vb); break; @@ -319,7 +398,7 @@ static int __vb2_queue_alloc(struct vb2_queue *q, enum v4l2_memory memory, if (memory == V4L2_MEMORY_MMAP) __setup_offsets(q, buffer); - dprintk(1, "Allocated %d buffers, %d plane(s) each\n", + dprintk(1, "allocated %d buffers, %d plane(s) each\n", buffer, num_planes); return buffer; @@ -371,7 +450,7 @@ static int __vb2_queue_free(struct vb2_queue *q, unsigned int buffers) if (q->bufs[buffer] == NULL) continue; if (q->bufs[buffer]->state == VB2_BUF_STATE_PREPARING) { - dprintk(1, "reqbufs: preparing buffers, cannot free\n"); + dprintk(1, "preparing buffers, cannot free\n"); return -EAGAIN; } } @@ -382,7 +461,7 @@ static int __vb2_queue_free(struct vb2_queue *q, unsigned int buffers) struct vb2_buffer *vb = q->bufs[buffer]; if (vb && vb->planes[0].mem_priv) - call_vb_qop(vb, buf_cleanup, vb); + call_void_vb_qop(vb, buf_cleanup, vb); } /* Release video buffer memory */ @@ -476,13 +555,13 @@ static int __verify_planes_array(struct vb2_buffer *vb, const struct v4l2_buffer /* Is memory for copying plane information present? */ if (NULL == b->m.planes) { - dprintk(1, "Multi-planar buffer passed but " + dprintk(1, "multi-planar buffer passed but " "planes array not provided\n"); return -EINVAL; } if (b->length < vb->num_planes || b->length > VIDEO_MAX_PLANES) { - dprintk(1, "Incorrect planes array length, " + dprintk(1, "incorrect planes array length, " "expected %d, got %d\n", vb->num_planes, b->length); return -EINVAL; } @@ -656,12 +735,12 @@ int vb2_querybuf(struct vb2_queue *q, struct v4l2_buffer *b) int ret; if (b->type != q->type) { - dprintk(1, "querybuf: wrong buffer type\n"); + dprintk(1, "wrong buffer type\n"); return -EINVAL; } if (b->index >= q->num_buffers) { - dprintk(1, "querybuf: buffer index out of range\n"); + dprintk(1, "buffer index out of range\n"); return -EINVAL; } vb = q->bufs[b->index]; @@ -721,12 +800,12 @@ static int __verify_memory_type(struct vb2_queue *q, { if (memory != V4L2_MEMORY_MMAP && memory != V4L2_MEMORY_USERPTR && memory != V4L2_MEMORY_DMABUF) { - dprintk(1, "reqbufs: unsupported memory type\n"); + dprintk(1, "unsupported memory type\n"); return -EINVAL; } if (type != q->type) { - dprintk(1, "reqbufs: requested type is incorrect\n"); + dprintk(1, "requested type is incorrect\n"); return -EINVAL; } @@ -735,17 +814,17 @@ static int __verify_memory_type(struct vb2_queue *q, * are available. */ if (memory == V4L2_MEMORY_MMAP && __verify_mmap_ops(q)) { - dprintk(1, "reqbufs: MMAP for current setup unsupported\n"); + dprintk(1, "MMAP for current setup unsupported\n"); return -EINVAL; } if (memory == V4L2_MEMORY_USERPTR && __verify_userptr_ops(q)) { - dprintk(1, "reqbufs: USERPTR for current setup unsupported\n"); + dprintk(1, "USERPTR for current setup unsupported\n"); return -EINVAL; } if (memory == V4L2_MEMORY_DMABUF && __verify_dmabuf_ops(q)) { - dprintk(1, "reqbufs: DMABUF for current setup unsupported\n"); + dprintk(1, "DMABUF for current setup unsupported\n"); return -EINVAL; } @@ -754,8 +833,8 @@ static int __verify_memory_type(struct vb2_queue *q, * create_bufs is called with count == 0, but count == 0 should still * do the memory and type validation. */ - if (q->fileio) { - dprintk(1, "reqbufs: file io in progress\n"); + if (vb2_fileio_is_active(q)) { + dprintk(1, "file io in progress\n"); return -EBUSY; } return 0; @@ -790,7 +869,7 @@ static int __reqbufs(struct vb2_queue *q, struct v4l2_requestbuffers *req) int ret; if (q->streaming) { - dprintk(1, "reqbufs: streaming active\n"); + dprintk(1, "streaming active\n"); return -EBUSY; } @@ -800,7 +879,7 @@ static int __reqbufs(struct vb2_queue *q, struct v4l2_requestbuffers *req) * are not in use and can be freed. */ if (q->memory == V4L2_MEMORY_MMAP && __buffers_in_use(q)) { - dprintk(1, "reqbufs: memory in use, cannot free\n"); + dprintk(1, "memory in use, cannot free\n"); return -EBUSY; } @@ -826,7 +905,7 @@ static int __reqbufs(struct vb2_queue *q, struct v4l2_requestbuffers *req) * Make sure the requested values and current defaults are sane. */ num_buffers = min_t(unsigned int, req->count, VIDEO_MAX_FRAME); - num_buffers = max_t(unsigned int, req->count, q->min_buffers_needed); + num_buffers = max_t(unsigned int, num_buffers, q->min_buffers_needed); memset(q->plane_sizes, 0, sizeof(q->plane_sizes)); memset(q->alloc_ctx, 0, sizeof(q->alloc_ctx)); q->memory = req->memory; @@ -837,15 +916,13 @@ static int __reqbufs(struct vb2_queue *q, struct v4l2_requestbuffers *req) */ ret = call_qop(q, queue_setup, q, NULL, &num_buffers, &num_planes, q->plane_sizes, q->alloc_ctx); - if (ret) { - fail_qop(q, queue_setup); + if (ret) return ret; - } /* Finally, allocate buffers and video memory */ allocated_buffers = __vb2_queue_alloc(q, req->memory, num_buffers, num_planes); if (allocated_buffers == 0) { - dprintk(1, "Memory allocation failed\n"); + dprintk(1, "memory allocation failed\n"); return -ENOMEM; } @@ -864,8 +941,6 @@ static int __reqbufs(struct vb2_queue *q, struct v4l2_requestbuffers *req) ret = call_qop(q, queue_setup, q, NULL, &num_buffers, &num_planes, q->plane_sizes, q->alloc_ctx); - if (ret) - fail_qop(q, queue_setup); if (!ret && allocated_buffers < num_buffers) ret = -ENOMEM; @@ -931,8 +1006,7 @@ static int __create_bufs(struct vb2_queue *q, struct v4l2_create_buffers *create int ret; if (q->num_buffers == VIDEO_MAX_FRAME) { - dprintk(1, "%s(): maximum number of buffers already allocated\n", - __func__); + dprintk(1, "maximum number of buffers already allocated\n"); return -ENOBUFS; } @@ -950,16 +1024,14 @@ static int __create_bufs(struct vb2_queue *q, struct v4l2_create_buffers *create */ ret = call_qop(q, queue_setup, q, &create->format, &num_buffers, &num_planes, q->plane_sizes, q->alloc_ctx); - if (ret) { - fail_qop(q, queue_setup); + if (ret) return ret; - } /* Finally, allocate buffers and video memory */ allocated_buffers = __vb2_queue_alloc(q, create->memory, num_buffers, num_planes); if (allocated_buffers == 0) { - dprintk(1, "Memory allocation failed\n"); + dprintk(1, "memory allocation failed\n"); return -ENOMEM; } @@ -975,8 +1047,6 @@ static int __create_bufs(struct vb2_queue *q, struct v4l2_create_buffers *create */ ret = call_qop(q, queue_setup, q, &create->format, &num_buffers, &num_planes, q->plane_sizes, q->alloc_ctx); - if (ret) - fail_qop(q, queue_setup); if (!ret && allocated_buffers < num_buffers) ret = -ENOMEM; @@ -1038,7 +1108,7 @@ void *vb2_plane_vaddr(struct vb2_buffer *vb, unsigned int plane_no) if (plane_no > vb->num_planes || !vb->planes[plane_no].mem_priv) return NULL; - return call_memop(vb, vaddr, vb->planes[plane_no].mem_priv); + return call_ptr_memop(vb, vaddr, vb->planes[plane_no].mem_priv); } EXPORT_SYMBOL_GPL(vb2_plane_vaddr); @@ -1059,7 +1129,7 @@ void *vb2_plane_cookie(struct vb2_buffer *vb, unsigned int plane_no) if (plane_no > vb->num_planes || !vb->planes[plane_no].mem_priv) return NULL; - return call_memop(vb, cookie, vb->planes[plane_no].mem_priv); + return call_ptr_memop(vb, cookie, vb->planes[plane_no].mem_priv); } EXPORT_SYMBOL_GPL(vb2_plane_cookie); @@ -1094,9 +1164,8 @@ void vb2_buffer_done(struct vb2_buffer *vb, enum vb2_buffer_state state) if (!q->start_streaming_called) { if (WARN_ON(state != VB2_BUF_STATE_QUEUED)) state = VB2_BUF_STATE_QUEUED; - } else if (!WARN_ON(!q->start_streaming_called)) { - if (WARN_ON(state != VB2_BUF_STATE_DONE && - state != VB2_BUF_STATE_ERROR)) + } else if (WARN_ON(state != VB2_BUF_STATE_DONE && + state != VB2_BUF_STATE_ERROR)) { state = VB2_BUF_STATE_ERROR; } @@ -1107,12 +1176,12 @@ void vb2_buffer_done(struct vb2_buffer *vb, enum vb2_buffer_state state) */ vb->cnt_buf_done++; #endif - dprintk(4, "Done processing on buffer %d, state: %d\n", + dprintk(4, "done processing on buffer %d, state: %d\n", vb->v4l2_buf.index, state); /* sync buffers */ for (plane = 0; plane < vb->num_planes; ++plane) - call_memop(vb, finish, vb->planes[plane].mem_priv); + call_void_memop(vb, finish, vb->planes[plane].mem_priv); /* Add the buffer to the done buffers list */ spin_lock_irqsave(&q->done_lock, flags); @@ -1143,15 +1212,30 @@ static void __fill_vb2_buffer(struct vb2_buffer *vb, const struct v4l2_buffer *b if (V4L2_TYPE_IS_MULTIPLANAR(b->type)) { /* Fill in driver-provided information for OUTPUT types */ if (V4L2_TYPE_IS_OUTPUT(b->type)) { + bool bytesused_is_used; + + /* Check if bytesused == 0 for all planes */ + for (plane = 0; plane < vb->num_planes; ++plane) + if (b->m.planes[plane].bytesused) + break; + bytesused_is_used = plane < vb->num_planes; + /* * Will have to go up to b->length when API starts * accepting variable number of planes. + * + * If bytesused_is_used is false, then fall back to the + * full buffer size. In that case userspace clearly + * never bothered to set it and it's a safe assumption + * that they really meant to use the full plane sizes. */ for (plane = 0; plane < vb->num_planes; ++plane) { - v4l2_planes[plane].bytesused = - b->m.planes[plane].bytesused; - v4l2_planes[plane].data_offset = - b->m.planes[plane].data_offset; + struct v4l2_plane *pdst = &v4l2_planes[plane]; + struct v4l2_plane *psrc = &b->m.planes[plane]; + + pdst->bytesused = bytesused_is_used ? + psrc->bytesused : psrc->length; + pdst->data_offset = psrc->data_offset; } } @@ -1169,8 +1253,6 @@ static void __fill_vb2_buffer(struct vb2_buffer *vb, const struct v4l2_buffer *b b->m.planes[plane].m.fd; v4l2_planes[plane].length = b->m.planes[plane].length; - v4l2_planes[plane].data_offset = - b->m.planes[plane].data_offset; } } } else { @@ -1179,11 +1261,15 @@ static void __fill_vb2_buffer(struct vb2_buffer *vb, const struct v4l2_buffer *b * so fill in relevant v4l2_buffer struct fields instead. * In videobuf we use our internal V4l2_planes struct for * single-planar buffers as well, for simplicity. + * + * If bytesused == 0, then fall back to the full buffer size + * as that's a sensible default. */ - if (V4L2_TYPE_IS_OUTPUT(b->type)) { - v4l2_planes[0].bytesused = b->bytesused; - v4l2_planes[0].data_offset = 0; - } + if (V4L2_TYPE_IS_OUTPUT(b->type)) + v4l2_planes[0].bytesused = + b->bytesused ? b->bytesused : b->length; + else + v4l2_planes[0].bytesused = 0; if (b->memory == V4L2_MEMORY_USERPTR) { v4l2_planes[0].m.userptr = b->m.userptr; @@ -1193,9 +1279,7 @@ static void __fill_vb2_buffer(struct vb2_buffer *vb, const struct v4l2_buffer *b if (b->memory == V4L2_MEMORY_DMABUF) { v4l2_planes[0].m.fd = b->m.fd; v4l2_planes[0].length = b->length; - v4l2_planes[0].data_offset = 0; } - } /* Zero flags that the vb2 core handles */ @@ -1226,6 +1310,15 @@ static void __fill_vb2_buffer(struct vb2_buffer *vb, const struct v4l2_buffer *b } /** + * __qbuf_mmap() - handle qbuf of an MMAP buffer + */ +static int __qbuf_mmap(struct vb2_buffer *vb, const struct v4l2_buffer *b) +{ + __fill_vb2_buffer(vb, b, vb->v4l2_planes); + return call_vb_qop(vb, buf_prepare, vb); +} + +/** * __qbuf_userptr() - handle qbuf of a USERPTR buffer */ static int __qbuf_userptr(struct vb2_buffer *vb, const struct v4l2_buffer *b) @@ -1238,6 +1331,7 @@ static int __qbuf_userptr(struct vb2_buffer *vb, const struct v4l2_buffer *b) int write = !V4L2_TYPE_IS_OUTPUT(q->type); bool reacquired = vb->planes[0].mem_priv == NULL; + memset(planes, 0, sizeof(planes[0]) * vb->num_planes); /* Copy relevant information provided by the userspace */ __fill_vb2_buffer(vb, b, planes); @@ -1248,12 +1342,12 @@ static int __qbuf_userptr(struct vb2_buffer *vb, const struct v4l2_buffer *b) && vb->v4l2_planes[plane].length == planes[plane].length) continue; - dprintk(3, "qbuf: userspace address for plane %d changed, " + dprintk(3, "userspace address for plane %d changed, " "reacquiring memory\n", plane); /* Check if the provided plane buffer is large enough */ if (planes[plane].length < q->plane_sizes[plane]) { - dprintk(1, "qbuf: provided buffer size %u is less than " + dprintk(1, "provided buffer size %u is less than " "setup size %u for plane %d\n", planes[plane].length, q->plane_sizes[plane], plane); @@ -1265,22 +1359,21 @@ static int __qbuf_userptr(struct vb2_buffer *vb, const struct v4l2_buffer *b) if (vb->planes[plane].mem_priv) { if (!reacquired) { reacquired = true; - call_vb_qop(vb, buf_cleanup, vb); + call_void_vb_qop(vb, buf_cleanup, vb); } - call_memop(vb, put_userptr, vb->planes[plane].mem_priv); + call_void_memop(vb, put_userptr, vb->planes[plane].mem_priv); } vb->planes[plane].mem_priv = NULL; memset(&vb->v4l2_planes[plane], 0, sizeof(struct v4l2_plane)); /* Acquire each plane's memory */ - mem_priv = call_memop(vb, get_userptr, q->alloc_ctx[plane], + mem_priv = call_ptr_memop(vb, get_userptr, q->alloc_ctx[plane], planes[plane].m.userptr, planes[plane].length, write); if (IS_ERR_OR_NULL(mem_priv)) { - dprintk(1, "qbuf: failed acquiring userspace " + dprintk(1, "failed acquiring userspace " "memory for plane %d\n", plane); - fail_memop(vb, get_userptr); ret = mem_priv ? PTR_ERR(mem_priv) : -EINVAL; goto err; } @@ -1302,17 +1395,15 @@ static int __qbuf_userptr(struct vb2_buffer *vb, const struct v4l2_buffer *b) */ ret = call_vb_qop(vb, buf_init, vb); if (ret) { - dprintk(1, "qbuf: buffer initialization failed\n"); - fail_vb_qop(vb, buf_init); + dprintk(1, "buffer initialization failed\n"); goto err; } } ret = call_vb_qop(vb, buf_prepare, vb); if (ret) { - dprintk(1, "qbuf: buffer preparation failed\n"); - fail_vb_qop(vb, buf_prepare); - call_vb_qop(vb, buf_cleanup, vb); + dprintk(1, "buffer preparation failed\n"); + call_void_vb_qop(vb, buf_cleanup, vb); goto err; } @@ -1321,7 +1412,7 @@ err: /* In case of errors, release planes that were already acquired */ for (plane = 0; plane < vb->num_planes; ++plane) { if (vb->planes[plane].mem_priv) - call_memop(vb, put_userptr, vb->planes[plane].mem_priv); + call_void_memop(vb, put_userptr, vb->planes[plane].mem_priv); vb->planes[plane].mem_priv = NULL; vb->v4l2_planes[plane].m.userptr = 0; vb->v4l2_planes[plane].length = 0; @@ -1331,20 +1422,6 @@ err: } /** - * __qbuf_mmap() - handle qbuf of an MMAP buffer - */ -static int __qbuf_mmap(struct vb2_buffer *vb, const struct v4l2_buffer *b) -{ - int ret; - - __fill_vb2_buffer(vb, b, vb->v4l2_planes); - ret = call_vb_qop(vb, buf_prepare, vb); - if (ret) - fail_vb_qop(vb, buf_prepare); - return ret; -} - -/** * __qbuf_dmabuf() - handle qbuf of a DMABUF buffer */ static int __qbuf_dmabuf(struct vb2_buffer *vb, const struct v4l2_buffer *b) @@ -1357,6 +1434,7 @@ static int __qbuf_dmabuf(struct vb2_buffer *vb, const struct v4l2_buffer *b) int write = !V4L2_TYPE_IS_OUTPUT(q->type); bool reacquired = vb->planes[0].mem_priv == NULL; + memset(planes, 0, sizeof(planes[0]) * vb->num_planes); /* Copy relevant information provided by the userspace */ __fill_vb2_buffer(vb, b, planes); @@ -1364,7 +1442,7 @@ static int __qbuf_dmabuf(struct vb2_buffer *vb, const struct v4l2_buffer *b) struct dma_buf *dbuf = dma_buf_get(planes[plane].m.fd); if (IS_ERR_OR_NULL(dbuf)) { - dprintk(1, "qbuf: invalid dmabuf fd for plane %d\n", + dprintk(1, "invalid dmabuf fd for plane %d\n", plane); ret = -EINVAL; goto err; @@ -1374,9 +1452,8 @@ static int __qbuf_dmabuf(struct vb2_buffer *vb, const struct v4l2_buffer *b) if (planes[plane].length == 0) planes[plane].length = dbuf->size; - if (planes[plane].length < planes[plane].data_offset + - q->plane_sizes[plane]) { - dprintk(1, "qbuf: invalid dmabuf length for plane %d\n", + if (planes[plane].length < q->plane_sizes[plane]) { + dprintk(1, "invalid dmabuf length for plane %d\n", plane); ret = -EINVAL; goto err; @@ -1389,11 +1466,11 @@ static int __qbuf_dmabuf(struct vb2_buffer *vb, const struct v4l2_buffer *b) continue; } - dprintk(1, "qbuf: buffer for plane %d changed\n", plane); + dprintk(1, "buffer for plane %d changed\n", plane); if (!reacquired) { reacquired = true; - call_vb_qop(vb, buf_cleanup, vb); + call_void_vb_qop(vb, buf_cleanup, vb); } /* Release previously acquired memory if present */ @@ -1401,11 +1478,10 @@ static int __qbuf_dmabuf(struct vb2_buffer *vb, const struct v4l2_buffer *b) memset(&vb->v4l2_planes[plane], 0, sizeof(struct v4l2_plane)); /* Acquire each plane's memory */ - mem_priv = call_memop(vb, attach_dmabuf, q->alloc_ctx[plane], + mem_priv = call_ptr_memop(vb, attach_dmabuf, q->alloc_ctx[plane], dbuf, planes[plane].length, write); if (IS_ERR(mem_priv)) { - dprintk(1, "qbuf: failed to attach dmabuf\n"); - fail_memop(vb, attach_dmabuf); + dprintk(1, "failed to attach dmabuf\n"); ret = PTR_ERR(mem_priv); dma_buf_put(dbuf); goto err; @@ -1422,9 +1498,8 @@ static int __qbuf_dmabuf(struct vb2_buffer *vb, const struct v4l2_buffer *b) for (plane = 0; plane < vb->num_planes; ++plane) { ret = call_memop(vb, map_dmabuf, vb->planes[plane].mem_priv); if (ret) { - dprintk(1, "qbuf: failed to map dmabuf for plane %d\n", + dprintk(1, "failed to map dmabuf for plane %d\n", plane); - fail_memop(vb, map_dmabuf); goto err; } vb->planes[plane].dbuf_mapped = 1; @@ -1444,17 +1519,15 @@ static int __qbuf_dmabuf(struct vb2_buffer *vb, const struct v4l2_buffer *b) */ ret = call_vb_qop(vb, buf_init, vb); if (ret) { - dprintk(1, "qbuf: buffer initialization failed\n"); - fail_vb_qop(vb, buf_init); + dprintk(1, "buffer initialization failed\n"); goto err; } } ret = call_vb_qop(vb, buf_prepare, vb); if (ret) { - dprintk(1, "qbuf: buffer preparation failed\n"); - fail_vb_qop(vb, buf_prepare); - call_vb_qop(vb, buf_cleanup, vb); + dprintk(1, "buffer preparation failed\n"); + call_void_vb_qop(vb, buf_cleanup, vb); goto err; } @@ -1479,9 +1552,9 @@ static void __enqueue_in_driver(struct vb2_buffer *vb) /* sync buffers */ for (plane = 0; plane < vb->num_planes; ++plane) - call_memop(vb, prepare, vb->planes[plane].mem_priv); + call_void_memop(vb, prepare, vb->planes[plane].mem_priv); - call_vb_qop(vb, buf_queue, vb); + call_void_vb_qop(vb, buf_queue, vb); } static int __buf_prepare(struct vb2_buffer *vb, const struct v4l2_buffer *b) @@ -1492,10 +1565,22 @@ static int __buf_prepare(struct vb2_buffer *vb, const struct v4l2_buffer *b) ret = __verify_length(vb, b); if (ret < 0) { - dprintk(1, "%s(): plane parameters verification failed: %d\n", - __func__, ret); + dprintk(1, "plane parameters verification failed: %d\n", ret); return ret; } + if (b->field == V4L2_FIELD_ALTERNATE && V4L2_TYPE_IS_OUTPUT(q->type)) { + /* + * If the format's field is ALTERNATE, then the buffer's field + * should be either TOP or BOTTOM, not ALTERNATE since that + * makes no sense. The driver has to know whether the + * buffer represents a top or a bottom field in order to + * program any DMA correctly. Using ALTERNATE is wrong, since + * that just says that it is either a top or a bottom field, + * but not which of the two it is. + */ + dprintk(1, "the field is incorrectly set to ALTERNATE for an output buffer\n"); + return -EINVAL; + } vb->state = VB2_BUF_STATE_PREPARING; vb->v4l2_buf.timestamp.tv_sec = 0; @@ -1520,9 +1605,9 @@ static int __buf_prepare(struct vb2_buffer *vb, const struct v4l2_buffer *b) * mmap_sem and then takes the driver's lock again. */ mmap_sem = ¤t->mm->mmap_sem; - call_qop(q, wait_prepare, q); + call_void_qop(q, wait_prepare, q); down_read(mmap_sem); - call_qop(q, wait_finish, q); + call_void_qop(q, wait_finish, q); ret = __qbuf_userptr(vb, b); @@ -1537,7 +1622,7 @@ static int __buf_prepare(struct vb2_buffer *vb, const struct v4l2_buffer *b) } if (ret) - dprintk(1, "qbuf: buffer preparation failed: %d\n", ret); + dprintk(1, "buffer preparation failed: %d\n", ret); vb->state = ret ? VB2_BUF_STATE_DEQUEUED : VB2_BUF_STATE_PREPARED; return ret; @@ -1547,23 +1632,23 @@ static int vb2_queue_or_prepare_buf(struct vb2_queue *q, struct v4l2_buffer *b, const char *opname) { if (b->type != q->type) { - dprintk(1, "%s(): invalid buffer type\n", opname); + dprintk(1, "%s: invalid buffer type\n", opname); return -EINVAL; } if (b->index >= q->num_buffers) { - dprintk(1, "%s(): buffer index out of range\n", opname); + dprintk(1, "%s: buffer index out of range\n", opname); return -EINVAL; } if (q->bufs[b->index] == NULL) { /* Should never happen */ - dprintk(1, "%s(): buffer is NULL\n", opname); + dprintk(1, "%s: buffer is NULL\n", opname); return -EINVAL; } if (b->memory != q->memory) { - dprintk(1, "%s(): invalid memory type\n", opname); + dprintk(1, "%s: invalid memory type\n", opname); return -EINVAL; } @@ -1590,8 +1675,8 @@ int vb2_prepare_buf(struct vb2_queue *q, struct v4l2_buffer *b) struct vb2_buffer *vb; int ret; - if (q->fileio) { - dprintk(1, "%s(): file io in progress\n", __func__); + if (vb2_fileio_is_active(q)) { + dprintk(1, "file io in progress\n"); return -EBUSY; } @@ -1601,7 +1686,7 @@ int vb2_prepare_buf(struct vb2_queue *q, struct v4l2_buffer *b) vb = q->bufs[b->index]; if (vb->state != VB2_BUF_STATE_DEQUEUED) { - dprintk(1, "%s(): invalid buffer state %d\n", __func__, + dprintk(1, "invalid buffer state %d\n", vb->state); return -EINVAL; } @@ -1611,7 +1696,7 @@ int vb2_prepare_buf(struct vb2_queue *q, struct v4l2_buffer *b) /* Fill buffer information for the userspace */ __fill_v4l2_buffer(vb, b); - dprintk(1, "%s() of buffer %d succeeded\n", __func__, vb->v4l2_buf.index); + dprintk(1, "prepare of buffer %d succeeded\n", vb->v4l2_buf.index); } return ret; } @@ -1647,8 +1732,7 @@ static int vb2_start_streaming(struct vb2_queue *q) if (!ret) return 0; - fail_qop(q, start_streaming); - dprintk(1, "qbuf: driver refused to start streaming\n"); + dprintk(1, "driver refused to start streaming\n"); if (WARN_ON(atomic_read(&q->owned_by_drv_count))) { unsigned i; @@ -1686,11 +1770,10 @@ static int vb2_internal_qbuf(struct vb2_queue *q, struct v4l2_buffer *b) case VB2_BUF_STATE_PREPARED: break; case VB2_BUF_STATE_PREPARING: - dprintk(1, "qbuf: buffer still being prepared\n"); + dprintk(1, "buffer still being prepared\n"); return -EINVAL; default: - dprintk(1, "%s(): invalid buffer state %d\n", __func__, - vb->state); + dprintk(1, "invalid buffer state %d\n", vb->state); return -EINVAL; } @@ -1737,7 +1820,7 @@ static int vb2_internal_qbuf(struct vb2_queue *q, struct v4l2_buffer *b) return ret; } - dprintk(1, "%s() of buffer %d succeeded\n", __func__, vb->v4l2_buf.index); + dprintk(1, "qbuf of buffer %d succeeded\n", vb->v4l2_buf.index); return 0; } @@ -1760,8 +1843,8 @@ static int vb2_internal_qbuf(struct vb2_queue *q, struct v4l2_buffer *b) */ int vb2_qbuf(struct vb2_queue *q, struct v4l2_buffer *b) { - if (q->fileio) { - dprintk(1, "%s(): file io in progress\n", __func__); + if (vb2_fileio_is_active(q)) { + dprintk(1, "file io in progress\n"); return -EBUSY; } @@ -1790,7 +1873,7 @@ static int __vb2_wait_for_done_vb(struct vb2_queue *q, int nonblocking) int ret; if (!q->streaming) { - dprintk(1, "Streaming off, will not wait for buffers\n"); + dprintk(1, "streaming off, will not wait for buffers\n"); return -EINVAL; } @@ -1802,7 +1885,7 @@ static int __vb2_wait_for_done_vb(struct vb2_queue *q, int nonblocking) } if (nonblocking) { - dprintk(1, "Nonblocking and no buffers to dequeue, " + dprintk(1, "nonblocking and no buffers to dequeue, " "will not wait\n"); return -EAGAIN; } @@ -1812,12 +1895,12 @@ static int __vb2_wait_for_done_vb(struct vb2_queue *q, int nonblocking) * become ready or for streamoff. Driver's lock is released to * allow streamoff or qbuf to be called while waiting. */ - call_qop(q, wait_prepare, q); + call_void_qop(q, wait_prepare, q); /* * All locks have been released, it is safe to sleep now. */ - dprintk(3, "Will sleep waiting for buffers\n"); + dprintk(3, "will sleep waiting for buffers\n"); ret = wait_event_interruptible(q->done_wq, !list_empty(&q->done_list) || !q->streaming); @@ -1825,9 +1908,9 @@ static int __vb2_wait_for_done_vb(struct vb2_queue *q, int nonblocking) * We need to reevaluate both conditions again after reacquiring * the locks or return an error if one occurred. */ - call_qop(q, wait_finish, q); + call_void_qop(q, wait_finish, q); if (ret) { - dprintk(1, "Sleep was interrupted\n"); + dprintk(1, "sleep was interrupted\n"); return ret; } } @@ -1882,7 +1965,7 @@ static int __vb2_get_done_vb(struct vb2_queue *q, struct vb2_buffer **vb, int vb2_wait_for_all_buffers(struct vb2_queue *q) { if (!q->streaming) { - dprintk(1, "Streaming off, will not wait for buffers\n"); + dprintk(1, "streaming off, will not wait for buffers\n"); return -EINVAL; } @@ -1911,7 +1994,7 @@ static void __vb2_dqbuf(struct vb2_buffer *vb) for (i = 0; i < vb->num_planes; ++i) { if (!vb->planes[i].dbuf_mapped) continue; - call_memop(vb, unmap_dmabuf, vb->planes[i].mem_priv); + call_void_memop(vb, unmap_dmabuf, vb->planes[i].mem_priv); vb->planes[i].dbuf_mapped = 0; } } @@ -1922,7 +2005,7 @@ static int vb2_internal_dqbuf(struct vb2_queue *q, struct v4l2_buffer *b, bool n int ret; if (b->type != q->type) { - dprintk(1, "dqbuf: invalid buffer type\n"); + dprintk(1, "invalid buffer type\n"); return -EINVAL; } ret = __vb2_get_done_vb(q, &vb, b, nonblocking); @@ -1931,17 +2014,17 @@ static int vb2_internal_dqbuf(struct vb2_queue *q, struct v4l2_buffer *b, bool n switch (vb->state) { case VB2_BUF_STATE_DONE: - dprintk(3, "dqbuf: Returning done buffer\n"); + dprintk(3, "returning done buffer\n"); break; case VB2_BUF_STATE_ERROR: - dprintk(3, "dqbuf: Returning done buffer with errors\n"); + dprintk(3, "returning done buffer with errors\n"); break; default: - dprintk(1, "dqbuf: Invalid buffer state\n"); + dprintk(1, "invalid buffer state\n"); return -EINVAL; } - call_vb_qop(vb, buf_finish, vb); + call_void_vb_qop(vb, buf_finish, vb); /* Fill buffer information for the userspace */ __fill_v4l2_buffer(vb, b); @@ -1980,8 +2063,8 @@ static int vb2_internal_dqbuf(struct vb2_queue *q, struct v4l2_buffer *b, bool n */ int vb2_dqbuf(struct vb2_queue *q, struct v4l2_buffer *b, bool nonblocking) { - if (q->fileio) { - dprintk(1, "dqbuf: file io in progress\n"); + if (vb2_fileio_is_active(q)) { + dprintk(1, "file io in progress\n"); return -EBUSY; } return vb2_internal_dqbuf(q, b, nonblocking); @@ -2003,10 +2086,7 @@ static void __vb2_queue_cancel(struct vb2_queue *q) * buffers. */ if (q->start_streaming_called) - call_qop(q, stop_streaming, q); - q->streaming = 0; - q->start_streaming_called = 0; - q->queued_count = 0; + call_void_qop(q, stop_streaming, q); if (WARN_ON(atomic_read(&q->owned_by_drv_count))) { for (i = 0; i < q->num_buffers; ++i) @@ -2016,6 +2096,10 @@ static void __vb2_queue_cancel(struct vb2_queue *q) WARN_ON(atomic_read(&q->owned_by_drv_count)); } + q->streaming = 0; + q->start_streaming_called = 0; + q->queued_count = 0; + /* * Remove all buffers from videobuf's list... */ @@ -2042,7 +2126,7 @@ static void __vb2_queue_cancel(struct vb2_queue *q) if (vb->state != VB2_BUF_STATE_DEQUEUED) { vb->state = VB2_BUF_STATE_PREPARED; - call_vb_qop(vb, buf_finish, vb); + call_void_vb_qop(vb, buf_finish, vb); } __vb2_dqbuf(vb); } @@ -2053,26 +2137,22 @@ static int vb2_internal_streamon(struct vb2_queue *q, enum v4l2_buf_type type) int ret; if (type != q->type) { - dprintk(1, "streamon: invalid stream type\n"); + dprintk(1, "invalid stream type\n"); return -EINVAL; } if (q->streaming) { - dprintk(3, "streamon successful: already streaming\n"); + dprintk(3, "already streaming\n"); return 0; } if (!q->num_buffers) { - dprintk(1, "streamon: no buffers have been allocated\n"); + dprintk(1, "no buffers have been allocated\n"); return -EINVAL; } - if (!q->num_buffers) { - dprintk(1, "streamon: no buffers have been allocated\n"); - return -EINVAL; - } if (q->num_buffers < q->min_buffers_needed) { - dprintk(1, "streamon: need at least %u allocated buffers\n", + dprintk(1, "need at least %u allocated buffers\n", q->min_buffers_needed); return -EINVAL; } @@ -2091,7 +2171,7 @@ static int vb2_internal_streamon(struct vb2_queue *q, enum v4l2_buf_type type) q->streaming = 1; - dprintk(3, "Streamon successful\n"); + dprintk(3, "successful\n"); return 0; } @@ -2110,8 +2190,8 @@ static int vb2_internal_streamon(struct vb2_queue *q, enum v4l2_buf_type type) */ int vb2_streamon(struct vb2_queue *q, enum v4l2_buf_type type) { - if (q->fileio) { - dprintk(1, "streamon: file io in progress\n"); + if (vb2_fileio_is_active(q)) { + dprintk(1, "file io in progress\n"); return -EBUSY; } return vb2_internal_streamon(q, type); @@ -2121,7 +2201,7 @@ EXPORT_SYMBOL_GPL(vb2_streamon); static int vb2_internal_streamoff(struct vb2_queue *q, enum v4l2_buf_type type) { if (type != q->type) { - dprintk(1, "streamoff: invalid stream type\n"); + dprintk(1, "invalid stream type\n"); return -EINVAL; } @@ -2136,7 +2216,7 @@ static int vb2_internal_streamoff(struct vb2_queue *q, enum v4l2_buf_type type) */ __vb2_queue_cancel(q); - dprintk(3, "Streamoff successful\n"); + dprintk(3, "successful\n"); return 0; } @@ -2157,8 +2237,8 @@ static int vb2_internal_streamoff(struct vb2_queue *q, enum v4l2_buf_type type) */ int vb2_streamoff(struct vb2_queue *q, enum v4l2_buf_type type) { - if (q->fileio) { - dprintk(1, "streamoff: file io in progress\n"); + if (vb2_fileio_is_active(q)) { + dprintk(1, "file io in progress\n"); return -EBUSY; } return vb2_internal_streamoff(q, type); @@ -2211,22 +2291,22 @@ int vb2_expbuf(struct vb2_queue *q, struct v4l2_exportbuffer *eb) struct dma_buf *dbuf; if (q->memory != V4L2_MEMORY_MMAP) { - dprintk(1, "Queue is not currently set up for mmap\n"); + dprintk(1, "queue is not currently set up for mmap\n"); return -EINVAL; } if (!q->mem_ops->get_dmabuf) { - dprintk(1, "Queue does not support DMA buffer exporting\n"); + dprintk(1, "queue does not support DMA buffer exporting\n"); return -EINVAL; } if (eb->flags & ~(O_CLOEXEC | O_ACCMODE)) { - dprintk(1, "Queue does support only O_CLOEXEC and access mode flags\n"); + dprintk(1, "queue does support only O_CLOEXEC and access mode flags\n"); return -EINVAL; } if (eb->type != q->type) { - dprintk(1, "qbuf: invalid buffer type\n"); + dprintk(1, "invalid buffer type\n"); return -EINVAL; } @@ -2242,13 +2322,17 @@ int vb2_expbuf(struct vb2_queue *q, struct v4l2_exportbuffer *eb) return -EINVAL; } + if (vb2_fileio_is_active(q)) { + dprintk(1, "expbuf: file io in progress\n"); + return -EBUSY; + } + vb_plane = &vb->planes[eb->plane]; - dbuf = call_memop(vb, get_dmabuf, vb_plane->mem_priv, eb->flags & O_ACCMODE); + dbuf = call_ptr_memop(vb, get_dmabuf, vb_plane->mem_priv, eb->flags & O_ACCMODE); if (IS_ERR_OR_NULL(dbuf)) { - dprintk(1, "Failed to export buffer %d, plane %d\n", + dprintk(1, "failed to export buffer %d, plane %d\n", eb->index, eb->plane); - fail_memop(vb, get_dmabuf); return -EINVAL; } @@ -2291,12 +2375,12 @@ int vb2_mmap(struct vb2_queue *q, struct vm_area_struct *vma) { unsigned long off = vma->vm_pgoff << PAGE_SHIFT; struct vb2_buffer *vb; - unsigned int buffer, plane; + unsigned int buffer = 0, plane = 0; int ret; unsigned long length; if (q->memory != V4L2_MEMORY_MMAP) { - dprintk(1, "Queue is not currently set up for mmap\n"); + dprintk(1, "queue is not currently set up for mmap\n"); return -EINVAL; } @@ -2304,20 +2388,24 @@ int vb2_mmap(struct vb2_queue *q, struct vm_area_struct *vma) * Check memory area access mode. */ if (!(vma->vm_flags & VM_SHARED)) { - dprintk(1, "Invalid vma flags, VM_SHARED needed\n"); + dprintk(1, "invalid vma flags, VM_SHARED needed\n"); return -EINVAL; } if (V4L2_TYPE_IS_OUTPUT(q->type)) { if (!(vma->vm_flags & VM_WRITE)) { - dprintk(1, "Invalid vma flags, VM_WRITE needed\n"); + dprintk(1, "invalid vma flags, VM_WRITE needed\n"); return -EINVAL; } } else { if (!(vma->vm_flags & VM_READ)) { - dprintk(1, "Invalid vma flags, VM_READ needed\n"); + dprintk(1, "invalid vma flags, VM_READ needed\n"); return -EINVAL; } } + if (vb2_fileio_is_active(q)) { + dprintk(1, "mmap: file io in progress\n"); + return -EBUSY; + } /* * Find the plane corresponding to the offset passed by userspace. @@ -2341,12 +2429,10 @@ int vb2_mmap(struct vb2_queue *q, struct vm_area_struct *vma) } ret = call_memop(vb, mmap, vb->planes[plane].mem_priv, vma); - if (ret) { - fail_memop(vb, mmap); + if (ret) return ret; - } - dprintk(3, "Buffer %d, plane %d successfully mapped\n", buffer, plane); + dprintk(3, "buffer %d, plane %d successfully mapped\n", buffer, plane); return 0; } EXPORT_SYMBOL_GPL(vb2_mmap); @@ -2364,7 +2450,7 @@ unsigned long vb2_get_unmapped_area(struct vb2_queue *q, int ret; if (q->memory != V4L2_MEMORY_MMAP) { - dprintk(1, "Queue is not currently set up for mmap\n"); + dprintk(1, "queue is not currently set up for mmap\n"); return -EINVAL; } @@ -2429,7 +2515,7 @@ unsigned int vb2_poll(struct vb2_queue *q, struct file *file, poll_table *wait) /* * Start file I/O emulator only if streaming API has not been used yet. */ - if (q->num_buffers == 0 && q->fileio == NULL) { + if (q->num_buffers == 0 && !vb2_fileio_is_active(q)) { if (!V4L2_TYPE_IS_OUTPUT(q->type) && (q->io_modes & VB2_READ) && (req_events & (POLLIN | POLLRDNORM))) { if (__vb2_init_fileio(q, 1)) @@ -2574,6 +2660,7 @@ struct vb2_fileio_buf { */ struct vb2_fileio_data { struct v4l2_requestbuffers req; + struct v4l2_plane p; struct v4l2_buffer b; struct vb2_fileio_buf bufs[VIDEO_MAX_FRAME]; unsigned int cur_index; @@ -2634,7 +2721,8 @@ static int __vb2_init_fileio(struct vb2_queue *q, int read) fileio->req.count = count; fileio->req.memory = V4L2_MEMORY_MMAP; fileio->req.type = q->type; - ret = vb2_reqbufs(q, &fileio->req); + q->fileio = fileio; + ret = __reqbufs(q, &fileio->req); if (ret) goto err_kfree; @@ -2663,16 +2751,24 @@ static int __vb2_init_fileio(struct vb2_queue *q, int read) * Read mode requires pre queuing of all buffers. */ if (read) { + bool is_multiplanar = V4L2_TYPE_IS_MULTIPLANAR(q->type); + /* * Queue all buffers. */ for (i = 0; i < q->num_buffers; i++) { struct v4l2_buffer *b = &fileio->b; + memset(b, 0, sizeof(*b)); b->type = q->type; + if (is_multiplanar) { + memset(&fileio->p, 0, sizeof(fileio->p)); + b->m.planes = &fileio->p; + b->length = 1; + } b->memory = q->memory; b->index = i; - ret = vb2_qbuf(q, b); + ret = vb2_internal_qbuf(q, b); if (ret) goto err_reqbufs; fileio->bufs[i].queued = 1; @@ -2688,19 +2784,18 @@ static int __vb2_init_fileio(struct vb2_queue *q, int read) /* * Start streaming. */ - ret = vb2_streamon(q, q->type); + ret = vb2_internal_streamon(q, q->type); if (ret) goto err_reqbufs; - q->fileio = fileio; - return ret; err_reqbufs: fileio->req.count = 0; - vb2_reqbufs(q, &fileio->req); + __reqbufs(q, &fileio->req); err_kfree: + q->fileio = NULL; kfree(fileio); return ret; } @@ -2738,9 +2833,18 @@ static size_t __vb2_perform_fileio(struct vb2_queue *q, char __user *data, size_ { struct vb2_fileio_data *fileio; struct vb2_fileio_buf *buf; + bool is_multiplanar = V4L2_TYPE_IS_MULTIPLANAR(q->type); + /* + * When using write() to write data to an output video node the vb2 core + * should set timestamps if V4L2_BUF_FLAG_TIMESTAMP_COPY is set. Nobody + * else is able to provide this information with the write() operation. + */ + bool set_timestamp = !read && + (q->timestamp_flags & V4L2_BUF_FLAG_TIMESTAMP_MASK) == + V4L2_BUF_FLAG_TIMESTAMP_COPY; int ret, index; - dprintk(3, "file io: mode %s, offset %ld, count %zd, %sblocking\n", + dprintk(3, "mode %s, offset %ld, count %zd, %sblocking\n", read ? "read" : "write", (long)*ppos, count, nonblock ? "non" : ""); @@ -2750,9 +2854,9 @@ static size_t __vb2_perform_fileio(struct vb2_queue *q, char __user *data, size_ /* * Initialize emulator on first call. */ - if (!q->fileio) { + if (!vb2_fileio_is_active(q)) { ret = __vb2_init_fileio(q, read); - dprintk(3, "file io: vb2_init_fileio result: %d\n", ret); + dprintk(3, "vb2_init_fileio result: %d\n", ret); if (ret) return ret; } @@ -2769,8 +2873,13 @@ static size_t __vb2_perform_fileio(struct vb2_queue *q, char __user *data, size_ memset(&fileio->b, 0, sizeof(fileio->b)); fileio->b.type = q->type; fileio->b.memory = q->memory; + if (is_multiplanar) { + memset(&fileio->p, 0, sizeof(fileio->p)); + fileio->b.m.planes = &fileio->p; + fileio->b.length = 1; + } ret = vb2_internal_dqbuf(q, &fileio->b, nonblock); - dprintk(5, "file io: vb2_dqbuf result: %d\n", ret); + dprintk(5, "vb2_dqbuf result: %d\n", ret); if (ret) return ret; fileio->dq_count += 1; @@ -2800,14 +2909,14 @@ static size_t __vb2_perform_fileio(struct vb2_queue *q, char __user *data, size_ /* * Transfer data to userspace. */ - dprintk(3, "file io: copying %zd bytes - buffer %d, offset %u\n", + dprintk(3, "copying %zd bytes - buffer %d, offset %u\n", count, index, buf->pos); if (read) ret = copy_to_user(data, buf->vaddr + buf->pos, count); else ret = copy_from_user(buf->vaddr + buf->pos, data, count); if (ret) { - dprintk(3, "file io: error copying data\n"); + dprintk(3, "error copying data\n"); return -EFAULT; } @@ -2827,7 +2936,7 @@ static size_t __vb2_perform_fileio(struct vb2_queue *q, char __user *data, size_ */ if (read && (fileio->flags & VB2_FILEIO_READ_ONCE) && fileio->dq_count == 1) { - dprintk(3, "file io: read limit reached\n"); + dprintk(3, "read limit reached\n"); return __vb2_cleanup_fileio(q); } @@ -2839,8 +2948,16 @@ static size_t __vb2_perform_fileio(struct vb2_queue *q, char __user *data, size_ fileio->b.memory = q->memory; fileio->b.index = index; fileio->b.bytesused = buf->pos; + if (is_multiplanar) { + memset(&fileio->p, 0, sizeof(fileio->p)); + fileio->p.bytesused = buf->pos; + fileio->b.m.planes = &fileio->p; + fileio->b.length = 1; + } + if (set_timestamp) + v4l2_get_timestamp(&fileio->b.timestamp); ret = vb2_internal_qbuf(q, &fileio->b); - dprintk(5, "file io: vb2_dbuf result: %d\n", ret); + dprintk(5, "vb2_dbuf result: %d\n", ret); if (ret) return ret; @@ -2890,6 +3007,147 @@ size_t vb2_write(struct vb2_queue *q, const char __user *data, size_t count, } EXPORT_SYMBOL_GPL(vb2_write); +struct vb2_threadio_data { + struct task_struct *thread; + vb2_thread_fnc fnc; + void *priv; + bool stop; +}; + +static int vb2_thread(void *data) +{ + struct vb2_queue *q = data; + struct vb2_threadio_data *threadio = q->threadio; + struct vb2_fileio_data *fileio = q->fileio; + bool set_timestamp = false; + int prequeue = 0; + int index = 0; + int ret = 0; + + if (V4L2_TYPE_IS_OUTPUT(q->type)) { + prequeue = q->num_buffers; + set_timestamp = + (q->timestamp_flags & V4L2_BUF_FLAG_TIMESTAMP_MASK) == + V4L2_BUF_FLAG_TIMESTAMP_COPY; + } + + set_freezable(); + + for (;;) { + struct vb2_buffer *vb; + + /* + * Call vb2_dqbuf to get buffer back. + */ + memset(&fileio->b, 0, sizeof(fileio->b)); + fileio->b.type = q->type; + fileio->b.memory = q->memory; + if (prequeue) { + fileio->b.index = index++; + prequeue--; + } else { + call_void_qop(q, wait_finish, q); + ret = vb2_internal_dqbuf(q, &fileio->b, 0); + call_void_qop(q, wait_prepare, q); + dprintk(5, "file io: vb2_dqbuf result: %d\n", ret); + } + if (threadio->stop) + break; + if (ret) + break; + try_to_freeze(); + + vb = q->bufs[fileio->b.index]; + if (!(fileio->b.flags & V4L2_BUF_FLAG_ERROR)) + ret = threadio->fnc(vb, threadio->priv); + if (ret) + break; + call_void_qop(q, wait_finish, q); + if (set_timestamp) + v4l2_get_timestamp(&fileio->b.timestamp); + ret = vb2_internal_qbuf(q, &fileio->b); + call_void_qop(q, wait_prepare, q); + if (ret) + break; + } + + /* Hmm, linux becomes *very* unhappy without this ... */ + while (!kthread_should_stop()) { + set_current_state(TASK_INTERRUPTIBLE); + schedule(); + } + return 0; +} + +/* + * This function should not be used for anything else but the videobuf2-dvb + * support. If you think you have another good use-case for this, then please + * contact the linux-media mailinglist first. + */ +int vb2_thread_start(struct vb2_queue *q, vb2_thread_fnc fnc, void *priv, + const char *thread_name) +{ + struct vb2_threadio_data *threadio; + int ret = 0; + + if (q->threadio) + return -EBUSY; + if (vb2_is_busy(q)) + return -EBUSY; + if (WARN_ON(q->fileio)) + return -EBUSY; + + threadio = kzalloc(sizeof(*threadio), GFP_KERNEL); + if (threadio == NULL) + return -ENOMEM; + threadio->fnc = fnc; + threadio->priv = priv; + + ret = __vb2_init_fileio(q, !V4L2_TYPE_IS_OUTPUT(q->type)); + dprintk(3, "file io: vb2_init_fileio result: %d\n", ret); + if (ret) + goto nomem; + q->threadio = threadio; + threadio->thread = kthread_run(vb2_thread, q, "vb2-%s", thread_name); + if (IS_ERR(threadio->thread)) { + ret = PTR_ERR(threadio->thread); + threadio->thread = NULL; + goto nothread; + } + return 0; + +nothread: + __vb2_cleanup_fileio(q); +nomem: + kfree(threadio); + return ret; +} +EXPORT_SYMBOL_GPL(vb2_thread_start); + +int vb2_thread_stop(struct vb2_queue *q) +{ + struct vb2_threadio_data *threadio = q->threadio; + struct vb2_fileio_data *fileio = q->fileio; + int err; + + if (threadio == NULL) + return 0; + call_void_qop(q, wait_finish, q); + threadio->stop = true; + vb2_internal_streamoff(q, q->type); + call_void_qop(q, wait_prepare, q); + q->fileio = NULL; + fileio->req.count = 0; + vb2_reqbufs(q, &fileio->req); + kfree(fileio); + err = kthread_stop(threadio->thread); + threadio->thread = NULL; + kfree(threadio); + q->fileio = NULL; + q->threadio = NULL; + return err; +} +EXPORT_SYMBOL_GPL(vb2_thread_stop); /* * The following functions are not part of the vb2 core API, but are helper @@ -3116,7 +3374,7 @@ unsigned int vb2_fop_poll(struct file *file, poll_table *wait) /* Try to be smart: only lock if polling might start fileio, otherwise locking will only introduce unwanted delays. */ - if (q->num_buffers == 0 && q->fileio == NULL) { + if (q->num_buffers == 0 && !vb2_fileio_is_active(q)) { if (!V4L2_TYPE_IS_OUTPUT(q->type) && (q->io_modes & VB2_READ) && (req_events & (POLLIN | POLLRDNORM))) must_lock = true; diff --git a/drivers/media/v4l2-core/videobuf2-dma-sg.c b/drivers/media/v4l2-core/videobuf2-dma-sg.c index c779f210d2c6..adefc31bb853 100644 --- a/drivers/media/v4l2-core/videobuf2-dma-sg.c +++ b/drivers/media/v4l2-core/videobuf2-dma-sg.c @@ -211,7 +211,7 @@ static void *vb2_dma_sg_get_userptr(void *alloc_ctx, unsigned long vaddr, ++num_pages_from_user, vaddr += PAGE_SIZE) { unsigned long pfn; - if (follow_pfn(buf->vma, vaddr, &pfn)) { + if (follow_pfn(vma, vaddr, &pfn)) { dprintk(1, "no page for address %lu\n", vaddr); break; } diff --git a/drivers/media/v4l2-core/videobuf2-dvb.c b/drivers/media/v4l2-core/videobuf2-dvb.c new file mode 100644 index 000000000000..d09269846b7e --- /dev/null +++ b/drivers/media/v4l2-core/videobuf2-dvb.c @@ -0,0 +1,336 @@ +/* + * + * some helper function for simple DVB cards which simply DMA the + * complete transport stream and let the computer sort everything else + * (i.e. we are using the software demux, ...). Also uses the + * video-buf to manage DMA buffers. + * + * (c) 2004 Gerd Knorr <kraxel@bytesex.org> [SUSE Labs] + * + * 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/module.h> +#include <linux/init.h> +#include <linux/device.h> +#include <linux/slab.h> + +#include <media/videobuf2-dvb.h> + +/* ------------------------------------------------------------------ */ + +MODULE_AUTHOR("Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]"); +MODULE_LICENSE("GPL"); + +/* ------------------------------------------------------------------ */ + +static int dvb_fnc(struct vb2_buffer *vb, void *priv) +{ + struct vb2_dvb *dvb = priv; + + dvb_dmx_swfilter(&dvb->demux, vb2_plane_vaddr(vb, 0), + vb2_get_plane_payload(vb, 0)); + return 0; +} + +static int vb2_dvb_start_feed(struct dvb_demux_feed *feed) +{ + struct dvb_demux *demux = feed->demux; + struct vb2_dvb *dvb = demux->priv; + int rc = 0; + + if (!demux->dmx.frontend) + return -EINVAL; + + mutex_lock(&dvb->lock); + dvb->nfeeds++; + + if (!dvb->dvbq.threadio) { + rc = vb2_thread_start(&dvb->dvbq, dvb_fnc, dvb, dvb->name); + if (rc) + dvb->nfeeds--; + } + if (!rc) + rc = dvb->nfeeds; + mutex_unlock(&dvb->lock); + return rc; +} + +static int vb2_dvb_stop_feed(struct dvb_demux_feed *feed) +{ + struct dvb_demux *demux = feed->demux; + struct vb2_dvb *dvb = demux->priv; + int err = 0; + + mutex_lock(&dvb->lock); + dvb->nfeeds--; + if (0 == dvb->nfeeds) + err = vb2_thread_stop(&dvb->dvbq); + mutex_unlock(&dvb->lock); + return err; +} + +static int vb2_dvb_register_adapter(struct vb2_dvb_frontends *fe, + struct module *module, + void *adapter_priv, + struct device *device, + char *adapter_name, + short *adapter_nr, + int mfe_shared) +{ + int result; + + mutex_init(&fe->lock); + + /* register adapter */ + result = dvb_register_adapter(&fe->adapter, adapter_name, module, + device, adapter_nr); + if (result < 0) { + pr_warn("%s: dvb_register_adapter failed (errno = %d)\n", + adapter_name, result); + } + fe->adapter.priv = adapter_priv; + fe->adapter.mfe_shared = mfe_shared; + + return result; +} + +static int vb2_dvb_register_frontend(struct dvb_adapter *adapter, + struct vb2_dvb *dvb) +{ + int result; + + /* register frontend */ + result = dvb_register_frontend(adapter, dvb->frontend); + if (result < 0) { + pr_warn("%s: dvb_register_frontend failed (errno = %d)\n", + dvb->name, result); + goto fail_frontend; + } + + /* register demux stuff */ + dvb->demux.dmx.capabilities = + DMX_TS_FILTERING | DMX_SECTION_FILTERING | + DMX_MEMORY_BASED_FILTERING; + dvb->demux.priv = dvb; + dvb->demux.filternum = 256; + dvb->demux.feednum = 256; + dvb->demux.start_feed = vb2_dvb_start_feed; + dvb->demux.stop_feed = vb2_dvb_stop_feed; + result = dvb_dmx_init(&dvb->demux); + if (result < 0) { + pr_warn("%s: dvb_dmx_init failed (errno = %d)\n", + dvb->name, result); + goto fail_dmx; + } + + dvb->dmxdev.filternum = 256; + dvb->dmxdev.demux = &dvb->demux.dmx; + dvb->dmxdev.capabilities = 0; + result = dvb_dmxdev_init(&dvb->dmxdev, adapter); + + if (result < 0) { + pr_warn("%s: dvb_dmxdev_init failed (errno = %d)\n", + dvb->name, result); + goto fail_dmxdev; + } + + dvb->fe_hw.source = DMX_FRONTEND_0; + result = dvb->demux.dmx.add_frontend(&dvb->demux.dmx, &dvb->fe_hw); + if (result < 0) { + pr_warn("%s: add_frontend failed (DMX_FRONTEND_0, errno = %d)\n", + dvb->name, result); + goto fail_fe_hw; + } + + dvb->fe_mem.source = DMX_MEMORY_FE; + result = dvb->demux.dmx.add_frontend(&dvb->demux.dmx, &dvb->fe_mem); + if (result < 0) { + pr_warn("%s: add_frontend failed (DMX_MEMORY_FE, errno = %d)\n", + dvb->name, result); + goto fail_fe_mem; + } + + result = dvb->demux.dmx.connect_frontend(&dvb->demux.dmx, &dvb->fe_hw); + if (result < 0) { + pr_warn("%s: connect_frontend failed (errno = %d)\n", + dvb->name, result); + goto fail_fe_conn; + } + + /* register network adapter */ + result = dvb_net_init(adapter, &dvb->net, &dvb->demux.dmx); + if (result < 0) { + pr_warn("%s: dvb_net_init failed (errno = %d)\n", + dvb->name, result); + goto fail_fe_conn; + } + return 0; + +fail_fe_conn: + dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_mem); +fail_fe_mem: + dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_hw); +fail_fe_hw: + dvb_dmxdev_release(&dvb->dmxdev); +fail_dmxdev: + dvb_dmx_release(&dvb->demux); +fail_dmx: + dvb_unregister_frontend(dvb->frontend); +fail_frontend: + dvb_frontend_detach(dvb->frontend); + dvb->frontend = NULL; + + return result; +} + +/* ------------------------------------------------------------------ */ +/* Register a single adapter and one or more frontends */ +int vb2_dvb_register_bus(struct vb2_dvb_frontends *f, + struct module *module, + void *adapter_priv, + struct device *device, + short *adapter_nr, + int mfe_shared) +{ + struct list_head *list, *q; + struct vb2_dvb_frontend *fe; + int res; + + fe = vb2_dvb_get_frontend(f, 1); + if (!fe) { + pr_warn("Unable to register the adapter which has no frontends\n"); + return -EINVAL; + } + + /* Bring up the adapter */ + res = vb2_dvb_register_adapter(f, module, adapter_priv, device, + fe->dvb.name, adapter_nr, mfe_shared); + if (res < 0) { + pr_warn("vb2_dvb_register_adapter failed (errno = %d)\n", res); + return res; + } + + /* Attach all of the frontends to the adapter */ + mutex_lock(&f->lock); + list_for_each_safe(list, q, &f->felist) { + fe = list_entry(list, struct vb2_dvb_frontend, felist); + res = vb2_dvb_register_frontend(&f->adapter, &fe->dvb); + if (res < 0) { + pr_warn("%s: vb2_dvb_register_frontend failed (errno = %d)\n", + fe->dvb.name, res); + goto err; + } + } + mutex_unlock(&f->lock); + return 0; + +err: + mutex_unlock(&f->lock); + vb2_dvb_unregister_bus(f); + return res; +} +EXPORT_SYMBOL(vb2_dvb_register_bus); + +void vb2_dvb_unregister_bus(struct vb2_dvb_frontends *f) +{ + vb2_dvb_dealloc_frontends(f); + + dvb_unregister_adapter(&f->adapter); +} +EXPORT_SYMBOL(vb2_dvb_unregister_bus); + +struct vb2_dvb_frontend *vb2_dvb_get_frontend( + struct vb2_dvb_frontends *f, int id) +{ + struct list_head *list, *q; + struct vb2_dvb_frontend *fe, *ret = NULL; + + mutex_lock(&f->lock); + + list_for_each_safe(list, q, &f->felist) { + fe = list_entry(list, struct vb2_dvb_frontend, felist); + if (fe->id == id) { + ret = fe; + break; + } + } + + mutex_unlock(&f->lock); + + return ret; +} +EXPORT_SYMBOL(vb2_dvb_get_frontend); + +int vb2_dvb_find_frontend(struct vb2_dvb_frontends *f, + struct dvb_frontend *p) +{ + struct list_head *list, *q; + struct vb2_dvb_frontend *fe = NULL; + int ret = 0; + + mutex_lock(&f->lock); + + list_for_each_safe(list, q, &f->felist) { + fe = list_entry(list, struct vb2_dvb_frontend, felist); + if (fe->dvb.frontend == p) { + ret = fe->id; + break; + } + } + + mutex_unlock(&f->lock); + + return ret; +} +EXPORT_SYMBOL(vb2_dvb_find_frontend); + +struct vb2_dvb_frontend *vb2_dvb_alloc_frontend( + struct vb2_dvb_frontends *f, int id) +{ + struct vb2_dvb_frontend *fe; + + fe = kzalloc(sizeof(struct vb2_dvb_frontend), GFP_KERNEL); + if (fe == NULL) + return NULL; + + fe->id = id; + mutex_init(&fe->dvb.lock); + + mutex_lock(&f->lock); + list_add_tail(&fe->felist, &f->felist); + mutex_unlock(&f->lock); + return fe; +} +EXPORT_SYMBOL(vb2_dvb_alloc_frontend); + +void vb2_dvb_dealloc_frontends(struct vb2_dvb_frontends *f) +{ + struct list_head *list, *q; + struct vb2_dvb_frontend *fe; + + mutex_lock(&f->lock); + list_for_each_safe(list, q, &f->felist) { + fe = list_entry(list, struct vb2_dvb_frontend, felist); + if (fe->dvb.net.dvbdev) { + dvb_net_release(&fe->dvb.net); + fe->dvb.demux.dmx.remove_frontend(&fe->dvb.demux.dmx, + &fe->dvb.fe_mem); + fe->dvb.demux.dmx.remove_frontend(&fe->dvb.demux.dmx, + &fe->dvb.fe_hw); + dvb_dmxdev_release(&fe->dvb.dmxdev); + dvb_dmx_release(&fe->dvb.demux); + dvb_unregister_frontend(fe->dvb.frontend); + } + if (fe->dvb.frontend) + /* always allocated, may have been reset */ + dvb_frontend_detach(fe->dvb.frontend); + list_del(list); /* remove list entry */ + kfree(fe); /* free frontend allocation */ + } + mutex_unlock(&f->lock); +} +EXPORT_SYMBOL(vb2_dvb_dealloc_frontends); |