aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHans Verkuil <hans.verkuil@cisco.com>2012-03-02 17:09:35 +0100
committerHans Verkuil <hans.verkuil@cisco.com>2012-03-02 17:09:35 +0100
commita38f7d94af725f651795e9a3728444490508196c (patch)
treee6822c0cc5ba04aa1600dba397538332573cd570
parent72f2a120ecbca55dc8f6506b4bfdcb78a71f554b (diff)
parentbb5b2c83cb86c34bb345c40685c135e9a3e31195 (diff)
Merge branch 'ivtv-fix' into debugfsdebugfs
# #total: 0 errors, 0 warnings, 98 lines checked # #Your patch has no obvious style problems and is ready for submission.
-rw-r--r--arch/arm/mach-imx/clock-imx27.c2
-rw-r--r--arch/arm/mach-imx/devices-imx27.h2
-rw-r--r--arch/arm/plat-mxc/devices/platform-mx2-camera.c18
-rw-r--r--arch/arm/plat-mxc/include/mach/devices-common.h2
-rw-r--r--drivers/media/common/tuners/mt2063.h4
-rw-r--r--drivers/media/dvb/dvb-usb/Kconfig10
-rw-r--r--drivers/media/dvb/dvb-usb/Makefile3
-rw-r--r--drivers/media/dvb/dvb-usb/dvb-usb-ids.h4
-rw-r--r--drivers/media/dvb/dvb-usb/rtl28xxu.c978
-rw-r--r--drivers/media/dvb/dvb-usb/rtl28xxu.h264
-rw-r--r--drivers/media/dvb/frontends/Kconfig7
-rw-r--r--drivers/media/dvb/frontends/Makefile1
-rw-r--r--drivers/media/dvb/frontends/rtl2830.c562
-rw-r--r--drivers/media/dvb/frontends/rtl2830.h97
-rw-r--r--drivers/media/dvb/frontends/rtl2830_priv.h57
-rw-r--r--drivers/media/dvb/mantis/mantis_hif.c2
-rw-r--r--drivers/media/radio/wl128x/Kconfig4
-rw-r--r--drivers/media/video/Kconfig10
-rw-r--r--drivers/media/video/Makefile2
-rw-r--r--drivers/media/video/hdpvr/hdpvr-core.c18
-rw-r--r--drivers/media/video/hdpvr/hdpvr-video.c43
-rw-r--r--drivers/media/video/hdpvr/hdpvr.h1
-rw-r--r--drivers/media/video/ivtv/ivtv-controls.c4
-rw-r--r--drivers/media/video/ivtv/ivtv-driver.c20
-rw-r--r--drivers/media/video/ivtv/ivtv-driver.h1
-rw-r--r--drivers/media/video/ivtv/ivtv-ioctl.c5
-rw-r--r--drivers/media/video/ivtv/ivtv-streams.c5
-rw-r--r--drivers/media/video/mx2_emmaprp.c1008
-rw-r--r--drivers/media/video/s5p-tv/Kconfig10
-rw-r--r--drivers/media/video/s5p-tv/Makefile2
-rw-r--r--drivers/media/video/s5p-tv/hdmi_drv.c116
-rw-r--r--drivers/media/video/s5p-tv/sdo_drv.c26
-rw-r--r--drivers/media/video/s5p-tv/sii9234_drv.c432
-rw-r--r--drivers/media/video/tveeprom.c8
-rw-r--r--drivers/media/video/v4l2-ctrls.c6
-rw-r--r--include/media/s5p_hdmi.h35
-rw-r--r--include/media/sii9234.h24
37 files changed, 3682 insertions, 111 deletions
diff --git a/arch/arm/mach-imx/clock-imx27.c b/arch/arm/mach-imx/clock-imx27.c
index 88fe00a146e3..dc2d7a511d9b 100644
--- a/arch/arm/mach-imx/clock-imx27.c
+++ b/arch/arm/mach-imx/clock-imx27.c
@@ -661,7 +661,7 @@ static struct clk_lookup lookups[] = {
_REGISTER_CLOCK(NULL, "dma", dma_clk)
_REGISTER_CLOCK(NULL, "rtic", rtic_clk)
_REGISTER_CLOCK(NULL, "brom", brom_clk)
- _REGISTER_CLOCK(NULL, "emma", emma_clk)
+ _REGISTER_CLOCK("m2m-emmaprp.0", NULL, emma_clk)
_REGISTER_CLOCK(NULL, "slcdc", slcdc_clk)
_REGISTER_CLOCK("imx27-fec.0", NULL, fec_clk)
_REGISTER_CLOCK(NULL, "emi", emi_clk)
diff --git a/arch/arm/mach-imx/devices-imx27.h b/arch/arm/mach-imx/devices-imx27.h
index 2f727d7c380c..28537a5d9048 100644
--- a/arch/arm/mach-imx/devices-imx27.h
+++ b/arch/arm/mach-imx/devices-imx27.h
@@ -50,6 +50,8 @@ extern const struct imx_imx_uart_1irq_data imx27_imx_uart_data[];
extern const struct imx_mx2_camera_data imx27_mx2_camera_data;
#define imx27_add_mx2_camera(pdata) \
imx_add_mx2_camera(&imx27_mx2_camera_data, pdata)
+#define imx27_add_mx2_emmaprp(pdata) \
+ imx_add_mx2_emmaprp(&imx27_mx2_camera_data)
extern const struct imx_mxc_ehci_data imx27_mxc_ehci_otg_data;
#define imx27_add_mxc_ehci_otg(pdata) \
diff --git a/arch/arm/plat-mxc/devices/platform-mx2-camera.c b/arch/arm/plat-mxc/devices/platform-mx2-camera.c
index b3f4828dc447..11eace953a09 100644
--- a/arch/arm/plat-mxc/devices/platform-mx2-camera.c
+++ b/arch/arm/plat-mxc/devices/platform-mx2-camera.c
@@ -62,3 +62,21 @@ struct platform_device *__init imx_add_mx2_camera(
res, data->iobaseemmaprp ? 4 : 2,
pdata, sizeof(*pdata), DMA_BIT_MASK(32));
}
+
+struct platform_device *__init imx_add_mx2_emmaprp(
+ const struct imx_mx2_camera_data *data)
+{
+ struct resource res[] = {
+ {
+ .start = data->iobaseemmaprp,
+ .end = data->iobaseemmaprp + data->iosizeemmaprp - 1,
+ .flags = IORESOURCE_MEM,
+ }, {
+ .start = data->irqemmaprp,
+ .end = data->irqemmaprp,
+ .flags = IORESOURCE_IRQ,
+ },
+ };
+ return imx_add_platform_device_dmamask("m2m-emmaprp", 0,
+ res, 2, NULL, 0, DMA_BIT_MASK(32));
+}
diff --git a/arch/arm/plat-mxc/include/mach/devices-common.h b/arch/arm/plat-mxc/include/mach/devices-common.h
index def9ba53e23a..1b2258daa05b 100644
--- a/arch/arm/plat-mxc/include/mach/devices-common.h
+++ b/arch/arm/plat-mxc/include/mach/devices-common.h
@@ -223,6 +223,8 @@ struct imx_mx2_camera_data {
struct platform_device *__init imx_add_mx2_camera(
const struct imx_mx2_camera_data *data,
const struct mx2_camera_platform_data *pdata);
+struct platform_device *__init imx_add_mx2_emmaprp(
+ const struct imx_mx2_camera_data *data);
#include <mach/mxc_ehci.h>
struct imx_mxc_ehci_data {
diff --git a/drivers/media/common/tuners/mt2063.h b/drivers/media/common/tuners/mt2063.h
index 62d0e8ec4e99..3f5cfd93713f 100644
--- a/drivers/media/common/tuners/mt2063.h
+++ b/drivers/media/common/tuners/mt2063.h
@@ -23,10 +23,6 @@ static inline struct dvb_frontend *mt2063_attach(struct dvb_frontend *fe,
return NULL;
}
-int mt2063_setTune(struct dvb_frontend *fe, u32 f_in,
- u32 bw_in,
- enum MTTune_atv_standard tv_type);
-
/* FIXME: Should use the standard DVB attachment interfaces */
unsigned int tuner_MT2063_SoftwareShutdown(struct dvb_frontend *fe);
unsigned int tuner_MT2063_ClearPowerMaskBits(struct dvb_frontend *fe);
diff --git a/drivers/media/dvb/dvb-usb/Kconfig b/drivers/media/dvb/dvb-usb/Kconfig
index e894bad6538e..615429274516 100644
--- a/drivers/media/dvb/dvb-usb/Kconfig
+++ b/drivers/media/dvb/dvb-usb/Kconfig
@@ -411,3 +411,13 @@ config DVB_USB_MXL111SF
select VIDEO_TVEEPROM
help
Say Y here to support the MxL111SF USB2.0 DTV receiver.
+
+config DVB_USB_RTL28XXU
+ tristate "Realtek RTL28xxU DVB USB support"
+ depends on DVB_USB && EXPERIMENTAL
+ select DVB_RTL2830
+ select MEDIA_TUNER_QT1010 if !MEDIA_TUNER_CUSTOMISE
+ select MEDIA_TUNER_MT2060 if !MEDIA_TUNER_CUSTOMISE
+ select MEDIA_TUNER_MXL5005S if !MEDIA_TUNER_CUSTOMISE
+ help
+ Say Y here to support the Realtek RTL28xxU DVB USB receiver.
diff --git a/drivers/media/dvb/dvb-usb/Makefile b/drivers/media/dvb/dvb-usb/Makefile
index d9549cb27698..7e476cb49334 100644
--- a/drivers/media/dvb/dvb-usb/Makefile
+++ b/drivers/media/dvb/dvb-usb/Makefile
@@ -107,6 +107,9 @@ obj-$(CONFIG_DVB_USB_MXL111SF) += dvb-usb-mxl111sf.o
obj-$(CONFIG_DVB_USB_MXL111SF) += mxl111sf-demod.o
obj-$(CONFIG_DVB_USB_MXL111SF) += mxl111sf-tuner.o
+dvb-usb-rtl28xxu-objs = rtl28xxu.o
+obj-$(CONFIG_DVB_USB_RTL28XXU) += dvb-usb-rtl28xxu.o
+
ccflags-y += -Idrivers/media/dvb/dvb-core/ -Idrivers/media/dvb/frontends/
# due to tuner-xc3028
ccflags-y += -Idrivers/media/common/tuners
diff --git a/drivers/media/dvb/dvb-usb/dvb-usb-ids.h b/drivers/media/dvb/dvb-usb/dvb-usb-ids.h
index 9c3dae100160..bf2826fa591d 100644
--- a/drivers/media/dvb/dvb-usb/dvb-usb-ids.h
+++ b/drivers/media/dvb/dvb-usb/dvb-usb-ids.h
@@ -51,6 +51,7 @@
#define USB_VID_PINNACLE 0x2304
#define USB_VID_PCTV 0x2013
#define USB_VID_PIXELVIEW 0x1554
+#define USB_VID_REALTEK 0x0bda
#define USB_VID_TECHNOTREND 0x0b48
#define USB_VID_TERRATEC 0x0ccd
#define USB_VID_TELESTAR 0x10b9
@@ -126,6 +127,7 @@
#define USB_PID_E3C_EC168_3 0xfffb
#define USB_PID_E3C_EC168_4 0x1001
#define USB_PID_E3C_EC168_5 0x1002
+#define USB_PID_FREECOM_DVBT 0x0160
#define USB_PID_UNIWILL_STK7700P 0x6003
#define USB_PID_GENIUS_TVGO_DVB_T03 0x4012
#define USB_PID_GRANDTEC_DVBT_USB_COLD 0x0fa0
@@ -252,6 +254,8 @@
#define USB_PID_PCTV_400E 0x020f
#define USB_PID_PCTV_450E 0x0222
#define USB_PID_PCTV_452E 0x021f
+#define USB_PID_REALTEK_RTL2831U 0x2831
+#define USB_PID_REALTEK_RTL2832U 0x2832
#define USB_PID_TECHNOTREND_CONNECT_S2_3600 0x3007
#define USB_PID_TECHNOTREND_CONNECT_S2_3650_CI 0x300a
#define USB_PID_NEBULA_DIGITV 0x0201
diff --git a/drivers/media/dvb/dvb-usb/rtl28xxu.c b/drivers/media/dvb/dvb-usb/rtl28xxu.c
new file mode 100644
index 000000000000..07597c53d07b
--- /dev/null
+++ b/drivers/media/dvb/dvb-usb/rtl28xxu.c
@@ -0,0 +1,978 @@
+/*
+ * Realtek RTL28xxU DVB USB driver
+ *
+ * Copyright (C) 2009 Antti Palosaari <crope@iki.fi>
+ * Copyright (C) 2011 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "rtl28xxu.h"
+
+#include "rtl2830.h"
+
+#include "qt1010.h"
+#include "mt2060.h"
+#include "mxl5005s.h"
+
+/* debug */
+static int dvb_usb_rtl28xxu_debug;
+module_param_named(debug, dvb_usb_rtl28xxu_debug, int, 0644);
+MODULE_PARM_DESC(debug, "set debugging level" DVB_USB_DEBUG_STATUS);
+DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
+
+static int rtl28xxu_ctrl_msg(struct dvb_usb_device *d, struct rtl28xxu_req *req)
+{
+ int ret;
+ unsigned int pipe;
+ u8 requesttype;
+ u8 *buf;
+
+ buf = kmalloc(req->size, GFP_KERNEL);
+ if (!buf) {
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ if (req->index & CMD_WR_FLAG) {
+ /* write */
+ memcpy(buf, req->data, req->size);
+ requesttype = (USB_TYPE_VENDOR | USB_DIR_OUT);
+ pipe = usb_sndctrlpipe(d->udev, 0);
+ } else {
+ /* read */
+ requesttype = (USB_TYPE_VENDOR | USB_DIR_IN);
+ pipe = usb_rcvctrlpipe(d->udev, 0);
+ }
+
+ ret = usb_control_msg(d->udev, pipe, 0, requesttype, req->value,
+ req->index, buf, req->size, 1000);
+ if (ret > 0)
+ ret = 0;
+
+ deb_dump(0, requesttype, req->value, req->index, buf, req->size,
+ deb_xfer);
+
+ /* read request, copy returned data to return buf */
+ if (!ret && requesttype == (USB_TYPE_VENDOR | USB_DIR_IN))
+ memcpy(req->data, buf, req->size);
+
+ kfree(buf);
+
+ if (ret)
+ goto err;
+
+ return ret;
+err:
+ deb_info("%s: failed=%d\n", __func__, ret);
+ return ret;
+}
+
+static int rtl2831_wr_regs(struct dvb_usb_device *d, u16 reg, u8 *val, int len)
+{
+ struct rtl28xxu_req req;
+
+ if (reg < 0x3000)
+ req.index = CMD_USB_WR;
+ else if (reg < 0x4000)
+ req.index = CMD_SYS_WR;
+ else
+ req.index = CMD_IR_WR;
+
+ req.value = reg;
+ req.size = len;
+ req.data = val;
+
+ return rtl28xxu_ctrl_msg(d, &req);
+}
+
+static int rtl2831_rd_regs(struct dvb_usb_device *d, u16 reg, u8 *val, int len)
+{
+ struct rtl28xxu_req req;
+
+ if (reg < 0x3000)
+ req.index = CMD_USB_RD;
+ else if (reg < 0x4000)
+ req.index = CMD_SYS_RD;
+ else
+ req.index = CMD_IR_RD;
+
+ req.value = reg;
+ req.size = len;
+ req.data = val;
+
+ return rtl28xxu_ctrl_msg(d, &req);
+}
+
+static int rtl2831_wr_reg(struct dvb_usb_device *d, u16 reg, u8 val)
+{
+ return rtl2831_wr_regs(d, reg, &val, 1);
+}
+
+static int rtl2831_rd_reg(struct dvb_usb_device *d, u16 reg, u8 *val)
+{
+ return rtl2831_rd_regs(d, reg, val, 1);
+}
+
+/* I2C */
+static int rtl28xxu_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[],
+ int num)
+{
+ int ret;
+ struct dvb_usb_device *d = i2c_get_adapdata(adap);
+ struct rtl28xxu_priv *priv = d->priv;
+ struct rtl28xxu_req req;
+
+ /*
+ * It is not known which are real I2C bus xfer limits, but testing
+ * with RTL2831U + MT2060 gives max RD 24 and max WR 22 bytes.
+ * TODO: find out RTL2832U lens
+ */
+
+ /*
+ * I2C adapter logic looks rather complicated due to fact it handles
+ * three different access methods. Those methods are;
+ * 1) integrated demod access
+ * 2) old I2C access
+ * 3) new I2C access
+ *
+ * Used method is selected in order 1, 2, 3. Method 3 can handle all
+ * requests but there is two reasons why not use it always;
+ * 1) It is most expensive, usually two USB messages are needed
+ * 2) At least RTL2831U does not support it
+ *
+ * Method 3 is needed in case of I2C write+read (typical register read)
+ * where write is more than one byte.
+ */
+
+ if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
+ return -EAGAIN;
+
+ if (num == 2 && !(msg[0].flags & I2C_M_RD) &&
+ (msg[1].flags & I2C_M_RD)) {
+ if (msg[0].len > 24 || msg[1].len > 24) {
+ /* TODO: check msg[0].len max */
+ ret = -EOPNOTSUPP;
+ goto err_mutex_unlock;
+ } else if (msg[0].addr == 0x10) {
+ /* method 1 - integrated demod */
+ req.value = (msg[0].buf[0] << 8) | (msg[0].addr << 1);
+ req.index = CMD_DEMOD_RD | priv->page;
+ req.size = msg[1].len;
+ req.data = &msg[1].buf[0];
+ ret = rtl28xxu_ctrl_msg(d, &req);
+ } else if (msg[0].len < 2) {
+ /* method 2 - old I2C */
+ req.value = (msg[0].buf[0] << 8) | (msg[0].addr << 1);
+ req.index = CMD_I2C_RD;
+ req.size = msg[1].len;
+ req.data = &msg[1].buf[0];
+ ret = rtl28xxu_ctrl_msg(d, &req);
+ } else {
+ /* method 3 - new I2C */
+ req.value = (msg[0].addr << 1);
+ req.index = CMD_I2C_DA_WR;
+ req.size = msg[0].len;
+ req.data = msg[0].buf;
+ ret = rtl28xxu_ctrl_msg(d, &req);
+ if (ret)
+ goto err_mutex_unlock;
+
+ req.value = (msg[0].addr << 1);
+ req.index = CMD_I2C_DA_RD;
+ req.size = msg[1].len;
+ req.data = msg[1].buf;
+ ret = rtl28xxu_ctrl_msg(d, &req);
+ }
+ } else if (num == 1 && !(msg[0].flags & I2C_M_RD)) {
+ if (msg[0].len > 22) {
+ /* TODO: check msg[0].len max */
+ ret = -EOPNOTSUPP;
+ goto err_mutex_unlock;
+ } else if (msg[0].addr == 0x10) {
+ /* method 1 - integrated demod */
+ if (msg[0].buf[0] == 0x00) {
+ /* save demod page for later demod access */
+ priv->page = msg[0].buf[1];
+ ret = 0;
+ } else {
+ req.value = (msg[0].buf[0] << 8) |
+ (msg[0].addr << 1);
+ req.index = CMD_DEMOD_WR | priv->page;
+ req.size = msg[0].len-1;
+ req.data = &msg[0].buf[1];
+ ret = rtl28xxu_ctrl_msg(d, &req);
+ }
+ } else if (msg[0].len < 23) {
+ /* method 2 - old I2C */
+ req.value = (msg[0].buf[0] << 8) | (msg[0].addr << 1);
+ req.index = CMD_I2C_WR;
+ req.size = msg[0].len-1;
+ req.data = &msg[0].buf[1];
+ ret = rtl28xxu_ctrl_msg(d, &req);
+ } else {
+ /* method 3 - new I2C */
+ req.value = (msg[0].addr << 1);
+ req.index = CMD_I2C_DA_WR;
+ req.size = msg[0].len;
+ req.data = msg[0].buf;
+ ret = rtl28xxu_ctrl_msg(d, &req);
+ }
+ } else {
+ ret = -EINVAL;
+ }
+
+err_mutex_unlock:
+ mutex_unlock(&d->i2c_mutex);
+
+ return ret ? ret : num;
+}
+
+static u32 rtl28xxu_i2c_func(struct i2c_adapter *adapter)
+{
+ return I2C_FUNC_I2C;
+}
+
+static struct i2c_algorithm rtl28xxu_i2c_algo = {
+ .master_xfer = rtl28xxu_i2c_xfer,
+ .functionality = rtl28xxu_i2c_func,
+};
+
+static struct rtl2830_config rtl28xxu_rtl2830_mt2060_config = {
+ .i2c_addr = 0x10, /* 0x20 */
+ .xtal = 28800000,
+ .ts_mode = 0,
+ .spec_inv = 1,
+ .if_dvbt = 36150000,
+ .vtop = 0x20,
+ .krf = 0x04,
+ .agc_targ_val = 0x2d,
+
+};
+
+static struct rtl2830_config rtl28xxu_rtl2830_qt1010_config = {
+ .i2c_addr = 0x10, /* 0x20 */
+ .xtal = 28800000,
+ .ts_mode = 0,
+ .spec_inv = 1,
+ .if_dvbt = 36125000,
+ .vtop = 0x20,
+ .krf = 0x04,
+ .agc_targ_val = 0x2d,
+};
+
+static struct rtl2830_config rtl28xxu_rtl2830_mxl5005s_config = {
+ .i2c_addr = 0x10, /* 0x20 */
+ .xtal = 28800000,
+ .ts_mode = 0,
+ .spec_inv = 0,
+ .if_dvbt = 4570000,
+ .vtop = 0x3f,
+ .krf = 0x04,
+ .agc_targ_val = 0x3e,
+};
+
+static int rtl2831u_frontend_attach(struct dvb_usb_adapter *adap)
+{
+ int ret;
+ struct rtl28xxu_priv *priv = adap->dev->priv;
+ u8 buf[1];
+ struct rtl2830_config *rtl2830_config;
+ /* open RTL2831U/RTL2830 I2C gate */
+ struct rtl28xxu_req req_gate = { 0x0120, 0x0011, 0x0001, "\x08" };
+ /* for MT2060 tuner probe */
+ struct rtl28xxu_req req_mt2060 = { 0x00c0, CMD_I2C_RD, 1, buf };
+ /* for QT1010 tuner probe */
+ struct rtl28xxu_req req_qt1010 = { 0x0fc4, CMD_I2C_RD, 1, buf };
+
+ deb_info("%s:\n", __func__);
+
+ /*
+ * RTL2831U GPIOs
+ * =========================================================
+ * GPIO0 | tuner#0 | 0 off | 1 on | MXL5005S (?)
+ * GPIO2 | LED | 0 off | 1 on |
+ * GPIO4 | tuner#1 | 0 on | 1 off | MT2060
+ */
+
+ /* GPIO direction */
+ ret = rtl2831_wr_reg(adap->dev, SYS_GPIO_DIR, 0x0a);
+ if (ret)
+ goto err;
+
+ /* enable as output GPIO0, GPIO2, GPIO4 */
+ ret = rtl2831_wr_reg(adap->dev, SYS_GPIO_OUT_EN, 0x15);
+ if (ret)
+ goto err;
+
+ /*
+ * Probe used tuner. We need to know used tuner before demod attach
+ * since there is some demod params needed to set according to tuner.
+ */
+
+ /* open demod I2C gate */
+ ret = rtl28xxu_ctrl_msg(adap->dev, &req_gate);
+ if (ret)
+ goto err;
+
+ /* check QT1010 ID(?) register; reg=0f val=2c */
+ ret = rtl28xxu_ctrl_msg(adap->dev, &req_qt1010);
+ if (ret == 0 && buf[0] == 0x2c) {
+ priv->tuner = TUNER_RTL2830_QT1010;
+ rtl2830_config = &rtl28xxu_rtl2830_qt1010_config;
+ deb_info("%s: QT1010\n", __func__);
+ goto found;
+ } else {
+ deb_info("%s: QT1010 probe failed=%d - %02x\n",
+ __func__, ret, buf[0]);
+ }
+
+ /* open demod I2C gate */
+ ret = rtl28xxu_ctrl_msg(adap->dev, &req_gate);
+ if (ret)
+ goto err;
+
+ /* check MT2060 ID register; reg=00 val=63 */
+ ret = rtl28xxu_ctrl_msg(adap->dev, &req_mt2060);
+ if (ret == 0 && buf[0] == 0x63) {
+ priv->tuner = TUNER_RTL2830_MT2060;
+ rtl2830_config = &rtl28xxu_rtl2830_mt2060_config;
+ deb_info("%s: MT2060\n", __func__);
+ goto found;
+ } else {
+ deb_info("%s: MT2060 probe failed=%d - %02x\n",
+ __func__, ret, buf[0]);
+ }
+
+ /* assume MXL5005S */
+ ret = 0;
+ priv->tuner = TUNER_RTL2830_MXL5005S;
+ rtl2830_config = &rtl28xxu_rtl2830_mxl5005s_config;
+ deb_info("%s: MXL5005S\n", __func__);
+ goto found;
+
+found:
+ /* attach demodulator */
+ adap->fe_adap[0].fe = dvb_attach(rtl2830_attach, rtl2830_config,
+ &adap->dev->i2c_adap);
+ if (adap->fe_adap[0].fe == NULL) {
+ ret = -ENODEV;
+ goto err;
+ }
+
+ return ret;
+err:
+ deb_info("%s: failed=%d\n", __func__, ret);
+ return ret;
+}
+
+static int rtl2832u_frontend_attach(struct dvb_usb_adapter *adap)
+{
+ int ret;
+ struct rtl28xxu_priv *priv = adap->dev->priv;
+ u8 buf[1];
+ /* open RTL2832U/RTL2832 I2C gate */
+ struct rtl28xxu_req req_gate_open = {0x0120, 0x0011, 0x0001, "\x18"};
+ /* close RTL2832U/RTL2832 I2C gate */
+ struct rtl28xxu_req req_gate_close = {0x0120, 0x0011, 0x0001, "\x10"};
+ /* for FC2580 tuner probe */
+ struct rtl28xxu_req req_fc2580 = {0x01ac, CMD_I2C_RD, 1, buf};
+
+ deb_info("%s:\n", __func__);
+
+ /* GPIO direction */
+ ret = rtl2831_wr_reg(adap->dev, SYS_GPIO_DIR, 0x0a);
+ if (ret)
+ goto err;
+
+ /* enable as output GPIO0, GPIO2, GPIO4 */
+ ret = rtl2831_wr_reg(adap->dev, SYS_GPIO_OUT_EN, 0x15);
+ if (ret)
+ goto err;
+
+ ret = rtl2831_wr_reg(adap->dev, SYS_DEMOD_CTL, 0xe8);
+ if (ret)
+ goto err;
+
+ /*
+ * Probe used tuner. We need to know used tuner before demod attach
+ * since there is some demod params needed to set according to tuner.
+ */
+
+ /* open demod I2C gate */
+ ret = rtl28xxu_ctrl_msg(adap->dev, &req_gate_open);
+ if (ret)
+ goto err;
+
+ /* check FC2580 ID register; reg=01 val=56 */
+ ret = rtl28xxu_ctrl_msg(adap->dev, &req_fc2580);
+ if (ret == 0 && buf[0] == 0x56) {
+ priv->tuner = TUNER_RTL2832_FC2580;
+ deb_info("%s: FC2580\n", __func__);
+ goto found;
+ } else {
+ deb_info("%s: FC2580 probe failed=%d - %02x\n",
+ __func__, ret, buf[0]);
+ }
+
+ /* close demod I2C gate */
+ ret = rtl28xxu_ctrl_msg(adap->dev, &req_gate_close);
+ if (ret)
+ goto err;
+
+ /* tuner not found */
+ ret = -ENODEV;
+ goto err;
+
+found:
+ /* close demod I2C gate */
+ ret = rtl28xxu_ctrl_msg(adap->dev, &req_gate_close);
+ if (ret)
+ goto err;
+
+ /* attach demodulator */
+ /* TODO: */
+
+ return ret;
+err:
+ deb_info("%s: failed=%d\n", __func__, ret);
+ return ret;
+}
+
+static struct qt1010_config rtl28xxu_qt1010_config = {
+ .i2c_address = 0x62, /* 0xc4 */
+};
+
+static struct mt2060_config rtl28xxu_mt2060_config = {
+ .i2c_address = 0x60, /* 0xc0 */
+ .clock_out = 0,
+};
+
+static struct mxl5005s_config rtl28xxu_mxl5005s_config = {
+ .i2c_address = 0x63, /* 0xc6 */
+ .if_freq = IF_FREQ_4570000HZ,
+ .xtal_freq = CRYSTAL_FREQ_16000000HZ,
+ .agc_mode = MXL_SINGLE_AGC,
+ .tracking_filter = MXL_TF_C_H,
+ .rssi_enable = MXL_RSSI_ENABLE,
+ .cap_select = MXL_CAP_SEL_ENABLE,
+ .div_out = MXL_DIV_OUT_4,
+ .clock_out = MXL_CLOCK_OUT_DISABLE,
+ .output_load = MXL5005S_IF_OUTPUT_LOAD_200_OHM,
+ .top = MXL5005S_TOP_25P2,
+ .mod_mode = MXL_DIGITAL_MODE,
+ .if_mode = MXL_ZERO_IF,
+ .AgcMasterByte = 0x00,
+};
+
+static int rtl2831u_tuner_attach(struct dvb_usb_adapter *adap)
+{
+ int ret;
+ struct rtl28xxu_priv *priv = adap->dev->priv;
+ struct i2c_adapter *rtl2830_tuner_i2c;
+ struct dvb_frontend *fe;
+
+ deb_info("%s:\n", __func__);
+
+ /* use rtl2830 driver I2C adapter, for more info see rtl2830 driver */
+ rtl2830_tuner_i2c = rtl2830_get_tuner_i2c_adapter(adap->fe_adap[0].fe);
+
+ switch (priv->tuner) {
+ case TUNER_RTL2830_QT1010:
+ fe = dvb_attach(qt1010_attach, adap->fe_adap[0].fe,
+ rtl2830_tuner_i2c, &rtl28xxu_qt1010_config);
+ break;
+ case TUNER_RTL2830_MT2060:
+ fe = dvb_attach(mt2060_attach, adap->fe_adap[0].fe,
+ rtl2830_tuner_i2c, &rtl28xxu_mt2060_config,
+ 1220);
+ break;
+ case TUNER_RTL2830_MXL5005S:
+ fe = dvb_attach(mxl5005s_attach, adap->fe_adap[0].fe,
+ rtl2830_tuner_i2c, &rtl28xxu_mxl5005s_config);
+ break;
+ default:
+ fe = NULL;
+ err("unknown tuner=%d", priv->tuner);
+ }
+
+ if (fe == NULL) {
+ ret = -ENODEV;
+ goto err;
+ }
+
+ return 0;
+err:
+ deb_info("%s: failed=%d\n", __func__, ret);
+ return ret;
+}
+
+static int rtl2832u_tuner_attach(struct dvb_usb_adapter *adap)
+{
+ int ret;
+ struct rtl28xxu_priv *priv = adap->dev->priv;
+ struct dvb_frontend *fe;
+
+ deb_info("%s:\n", __func__);
+
+ switch (priv->tuner) {
+ case TUNER_RTL2832_FC2580:
+ /* TODO: */
+ fe = NULL;
+ break;
+ default:
+ fe = NULL;
+ err("unknown tuner=%d", priv->tuner);
+ }
+
+ if (fe == NULL) {
+ ret = -ENODEV;
+ goto err;
+ }
+
+ return 0;
+err:
+ deb_info("%s: failed=%d\n", __func__, ret);
+ return ret;
+}
+
+static int rtl28xxu_streaming_ctrl(struct dvb_usb_adapter *adap , int onoff)
+{
+ int ret;
+ u8 buf[2], gpio;
+
+ deb_info("%s: onoff=%d\n", __func__, onoff);
+
+ ret = rtl2831_rd_reg(adap->dev, SYS_GPIO_OUT_VAL, &gpio);
+ if (ret)
+ goto err;
+
+ if (onoff) {
+ buf[0] = 0x00;
+ buf[1] = 0x00;
+ gpio |= 0x04; /* LED on */
+ } else {
+ buf[0] = 0x10; /* stall EPA */
+ buf[1] = 0x02; /* reset EPA */
+ gpio &= (~0x04); /* LED off */
+ }
+
+ ret = rtl2831_wr_reg(adap->dev, SYS_GPIO_OUT_VAL, gpio);
+ if (ret)
+ goto err;
+
+ ret = rtl2831_wr_regs(adap->dev, USB_EPA_CTL, buf, 2);
+ if (ret)
+ goto err;
+
+ return ret;
+err:
+ deb_info("%s: failed=%d\n", __func__, ret);
+ return ret;
+}
+
+static int rtl28xxu_power_ctrl(struct dvb_usb_device *d, int onoff)
+{
+ int ret;
+ u8 gpio, sys0;
+
+ deb_info("%s: onoff=%d\n", __func__, onoff);
+
+ /* demod adc */
+ ret = rtl2831_rd_reg(d, SYS_SYS0, &sys0);
+ if (ret)
+ goto err;
+
+ /* tuner power, read GPIOs */
+ ret = rtl2831_rd_reg(d, SYS_GPIO_OUT_VAL, &gpio);
+ if (ret)
+ goto err;
+
+ deb_info("%s: RD SYS0=%02x GPIO_OUT_VAL=%02x\n", __func__, sys0, gpio);
+
+ if (onoff) {
+ gpio |= 0x01; /* GPIO0 = 1 */
+ gpio &= (~0x10); /* GPIO4 = 0 */
+ sys0 = sys0 & 0x0f;
+ sys0 |= 0xe0;
+ } else {
+ gpio &= (~0x01); /* GPIO0 = 0 */
+ gpio |= 0x10; /* GPIO4 = 1 */
+ sys0 = sys0 & (~0xc0);
+ }
+
+ deb_info("%s: WR SYS0=%02x GPIO_OUT_VAL=%02x\n", __func__, sys0, gpio);
+
+ /* demod adc */
+ ret = rtl2831_wr_reg(d, SYS_SYS0, sys0);
+ if (ret)
+ goto err;
+
+ /* tuner power, write GPIOs */
+ ret = rtl2831_wr_reg(d, SYS_GPIO_OUT_VAL, gpio);
+ if (ret)
+ goto err;
+
+ return ret;
+err:
+ deb_info("%s: failed=%d\n", __func__, ret);
+ return ret;
+}
+
+static int rtl2831u_rc_query(struct dvb_usb_device *d)
+{
+ int ret, i;
+ struct rtl28xxu_priv *priv = d->priv;
+ u8 buf[5];
+ u32 rc_code;
+ struct rtl28xxu_reg_val rc_nec_tab[] = {
+ { 0x3033, 0x80 },
+ { 0x3020, 0x43 },
+ { 0x3021, 0x16 },
+ { 0x3022, 0x16 },
+ { 0x3023, 0x5a },
+ { 0x3024, 0x2d },
+ { 0x3025, 0x16 },
+ { 0x3026, 0x01 },
+ { 0x3028, 0xb0 },
+ { 0x3029, 0x04 },
+ { 0x302c, 0x88 },
+ { 0x302e, 0x13 },
+ { 0x3030, 0xdf },
+ { 0x3031, 0x05 },
+ };
+
+ /* init remote controller */
+ if (!priv->rc_active) {
+ for (i = 0; i < ARRAY_SIZE(rc_nec_tab); i++) {
+ ret = rtl2831_wr_reg(d, rc_nec_tab[i].reg,
+ rc_nec_tab[i].val);
+ if (ret)
+ goto err;
+ }
+ priv->rc_active = true;
+ }
+
+ ret = rtl2831_rd_regs(d, SYS_IRRC_RP, buf, 5);
+ if (ret)
+ goto err;
+
+ if (buf[4] & 0x01) {
+ if (buf[2] == (u8) ~buf[3]) {
+ if (buf[0] == (u8) ~buf[1]) {
+ /* NEC standard (16 bit) */
+ rc_code = buf[0] << 8 | buf[2];
+ } else {
+ /* NEC extended (24 bit) */
+ rc_code = buf[0] << 16 |
+ buf[1] << 8 | buf[2];
+ }
+ } else {
+ /* NEC full (32 bit) */
+ rc_code = buf[0] << 24 | buf[1] << 16 |
+ buf[2] << 8 | buf[3];
+ }
+
+ rc_keydown(d->rc_dev, rc_code, 0);
+
+ ret = rtl2831_wr_reg(d, SYS_IRRC_SR, 1);
+ if (ret)
+ goto err;
+
+ /* repeated intentionally to avoid extra keypress */
+ ret = rtl2831_wr_reg(d, SYS_IRRC_SR, 1);
+ if (ret)
+ goto err;
+ }
+
+ return ret;
+err:
+ deb_info("%s: failed=%d\n", __func__, ret);
+ return ret;
+}
+
+static int rtl2832u_rc_query(struct dvb_usb_device *d)
+{
+ int ret, i;
+ struct rtl28xxu_priv *priv = d->priv;
+ u8 buf[128];
+ int len;
+ struct rtl28xxu_reg_val rc_nec_tab[] = {
+ { IR_RX_CTRL, 0x20 },
+ { IR_RX_BUF_CTRL, 0x80 },
+ { IR_RX_IF, 0xff },
+ { IR_RX_IE, 0xff },
+ { IR_MAX_DURATION0, 0xd0 },
+ { IR_MAX_DURATION1, 0x07 },
+ { IR_IDLE_LEN0, 0xc0 },
+ { IR_IDLE_LEN1, 0x00 },
+ { IR_GLITCH_LEN, 0x03 },
+ { IR_RX_CLK, 0x09 },
+ { IR_RX_CFG, 0x1c },
+ { IR_MAX_H_TOL_LEN, 0x1e },
+ { IR_MAX_L_TOL_LEN, 0x1e },
+ { IR_RX_CTRL, 0x80 },
+ };
+
+ /* init remote controller */
+ if (!priv->rc_active) {
+ for (i = 0; i < ARRAY_SIZE(rc_nec_tab); i++) {
+ ret = rtl2831_wr_reg(d, rc_nec_tab[i].reg,
+ rc_nec_tab[i].val);
+ if (ret)
+ goto err;
+ }
+ priv->rc_active = true;
+ }
+
+ ret = rtl2831_rd_reg(d, IR_RX_IF, &buf[0]);
+ if (ret)
+ goto err;
+
+ if (buf[0] != 0x83)
+ goto exit;
+
+ ret = rtl2831_rd_reg(d, IR_RX_BC, &buf[0]);
+ if (ret)
+ goto err;
+
+ len = buf[0];
+ ret = rtl2831_rd_regs(d, IR_RX_BUF, buf, len);
+
+ /* TODO: pass raw IR to Kernel IR decoder */
+
+ ret = rtl2831_wr_reg(d, IR_RX_IF, 0x03);
+ ret = rtl2831_wr_reg(d, IR_RX_BUF_CTRL, 0x80);
+ ret = rtl2831_wr_reg(d, IR_RX_CTRL, 0x80);
+
+exit:
+ return ret;
+err:
+ deb_info("%s: failed=%d\n", __func__, ret);
+ return ret;
+}
+
+enum rtl28xxu_usb_table_entry {
+ RTL2831U_0BDA_2831,
+ RTL2831U_14AA_0160,
+};
+
+static struct usb_device_id rtl28xxu_table[] = {
+ /* RTL2831U */
+ [RTL2831U_0BDA_2831] = {
+ USB_DEVICE(USB_VID_REALTEK, USB_PID_REALTEK_RTL2831U)},
+ [RTL2831U_14AA_0160] = {
+ USB_DEVICE(USB_VID_WIDEVIEW, USB_PID_FREECOM_DVBT)},
+
+ /* RTL2832U */
+ {} /* terminating entry */
+};
+
+MODULE_DEVICE_TABLE(usb, rtl28xxu_table);
+
+static struct dvb_usb_device_properties rtl28xxu_properties[] = {
+ {
+ .caps = DVB_USB_IS_AN_I2C_ADAPTER,
+
+ .usb_ctrl = DEVICE_SPECIFIC,
+ .no_reconnect = 1,
+
+ .size_of_priv = sizeof(struct rtl28xxu_priv),
+
+ .num_adapters = 1,
+ .adapter = {
+ {
+ .num_frontends = 1,
+ .fe = {
+ {
+ .frontend_attach = rtl2831u_frontend_attach,
+ .tuner_attach = rtl2831u_tuner_attach,
+ .streaming_ctrl = rtl28xxu_streaming_ctrl,
+ .stream = {
+ .type = USB_BULK,
+ .count = 6,
+ .endpoint = 0x81,
+ .u = {
+ .bulk = {
+ .buffersize = 8*512,
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+
+ .power_ctrl = rtl28xxu_power_ctrl,
+
+ .rc.core = {
+ .protocol = RC_TYPE_NEC,
+ .module_name = "rtl28xxu",
+ .rc_query = rtl2831u_rc_query,
+ .rc_interval = 400,
+ .allowed_protos = RC_TYPE_NEC,
+ .rc_codes = RC_MAP_EMPTY,
+ },
+
+ .i2c_algo = &rtl28xxu_i2c_algo,
+
+ .num_device_descs = 2,
+ .devices = {
+ {
+ .name = "Realtek RTL2831U reference design",
+ .warm_ids = {
+ &rtl28xxu_table[RTL2831U_0BDA_2831],
+ },
+ },
+ {
+ .name = "Freecom USB2.0 DVB-T",
+ .warm_ids = {
+ &rtl28xxu_table[RTL2831U_14AA_0160],
+ },
+ },
+ }
+ },
+ {
+ .caps = DVB_USB_IS_AN_I2C_ADAPTER,
+
+ .usb_ctrl = DEVICE_SPECIFIC,
+ .no_reconnect = 1,
+
+ .size_of_priv = sizeof(struct rtl28xxu_priv),
+
+ .num_adapters = 1,
+ .adapter = {
+ {
+ .num_frontends = 1,
+ .fe = {
+ {
+ .frontend_attach = rtl2832u_frontend_attach,
+ .tuner_attach = rtl2832u_tuner_attach,
+ .streaming_ctrl = rtl28xxu_streaming_ctrl,
+ .stream = {
+ .type = USB_BULK,
+ .count = 6,
+ .endpoint = 0x81,
+ .u = {
+ .bulk = {
+ .buffersize = 8*512,
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+
+ .power_ctrl = rtl28xxu_power_ctrl,
+
+ .rc.core = {
+ .protocol = RC_TYPE_NEC,
+ .module_name = "rtl28xxu",
+ .rc_query = rtl2832u_rc_query,
+ .rc_interval = 400,
+ .allowed_protos = RC_TYPE_NEC,
+ .rc_codes = RC_MAP_EMPTY,
+ },
+
+ .i2c_algo = &rtl28xxu_i2c_algo,
+
+ .num_device_descs = 0, /* disabled as no support for RTL2832 */
+ .devices = {
+ {
+ .name = "Realtek RTL2832U reference design",
+ },
+ }
+ },
+
+};
+
+static int rtl28xxu_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ int ret, i;
+ int properties_count = ARRAY_SIZE(rtl28xxu_properties);
+ struct dvb_usb_device *d;
+
+ deb_info("%s: interface=%d\n", __func__,
+ intf->cur_altsetting->desc.bInterfaceNumber);
+
+ if (intf->cur_altsetting->desc.bInterfaceNumber != 0)
+ return 0;
+
+ for (i = 0; i < properties_count; i++) {
+ ret = dvb_usb_device_init(intf, &rtl28xxu_properties[i],
+ THIS_MODULE, &d, adapter_nr);
+ if (ret == 0 || ret != -ENODEV)
+ break;
+ }
+
+ if (ret)
+ goto err;
+
+ /* init USB endpoints */
+ ret = rtl2831_wr_reg(d, USB_SYSCTL_0, 0x09);
+ if (ret)
+ goto err;
+
+ ret = rtl2831_wr_regs(d, USB_EPA_MAXPKT, "\x00\x02\x00\x00", 4);
+ if (ret)
+ goto err;
+
+ ret = rtl2831_wr_regs(d, USB_EPA_FIFO_CFG, "\x14\x00\x00\x00", 4);
+ if (ret)
+ goto err;
+
+ return ret;
+err:
+ deb_info("%s: failed=%d\n", __func__, ret);
+ return ret;
+}
+
+static struct usb_driver rtl28xxu_driver = {
+ .name = "dvb_usb_rtl28xxu",
+ .probe = rtl28xxu_probe,
+ .disconnect = dvb_usb_device_exit,
+ .id_table = rtl28xxu_table,
+};
+
+/* module stuff */
+static int __init rtl28xxu_module_init(void)
+{
+ int ret;
+
+ deb_info("%s:\n", __func__);
+
+ ret = usb_register(&rtl28xxu_driver);
+ if (ret)
+ err("usb_register failed=%d", ret);
+
+ return ret;
+}
+
+static void __exit rtl28xxu_module_exit(void)
+{
+ deb_info("%s:\n", __func__);
+
+ /* deregister this driver from the USB subsystem */
+ usb_deregister(&rtl28xxu_driver);
+}
+
+module_init(rtl28xxu_module_init);
+module_exit(rtl28xxu_module_exit);
+
+MODULE_DESCRIPTION("Realtek RTL28xxU DVB USB driver");
+MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/dvb-usb/rtl28xxu.h b/drivers/media/dvb/dvb-usb/rtl28xxu.h
new file mode 100644
index 000000000000..90f3bb4f4c0e
--- /dev/null
+++ b/drivers/media/dvb/dvb-usb/rtl28xxu.h
@@ -0,0 +1,264 @@
+/*
+ * Realtek RTL28xxU DVB USB driver
+ *
+ * Copyright (C) 2009 Antti Palosaari <crope@iki.fi>
+ * Copyright (C) 2011 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef RTL28XXU_H
+#define RTL28XXU_H
+
+#define DVB_USB_LOG_PREFIX "rtl28xxu"
+#include "dvb-usb.h"
+
+#define deb_info(args...) dprintk(dvb_usb_rtl28xxu_debug, 0x01, args)
+#define deb_rc(args...) dprintk(dvb_usb_rtl28xxu_debug, 0x02, args)
+#define deb_xfer(args...) dprintk(dvb_usb_rtl28xxu_debug, 0x04, args)
+#define deb_reg(args...) dprintk(dvb_usb_rtl28xxu_debug, 0x08, args)
+#define deb_i2c(args...) dprintk(dvb_usb_rtl28xxu_debug, 0x10, args)
+#define deb_fw(args...) dprintk(dvb_usb_rtl28xxu_debug, 0x20, args)
+
+#define deb_dump(r, t, v, i, b, l, func) { \
+ int loop_; \
+ func("%02x %02x %02x %02x %02x %02x %02x %02x", \
+ t, r, v & 0xff, v >> 8, i & 0xff, i >> 8, l & 0xff, l >> 8); \
+ if (t == (USB_TYPE_VENDOR | USB_DIR_OUT)) \
+ func(" >>> "); \
+ else \
+ func(" <<< "); \
+ for (loop_ = 0; loop_ < l; loop_++) \
+ func("%02x ", b[loop_]); \
+ func("\n");\
+}
+
+/*
+ * USB commands
+ * (usb_control_msg() index parameter)
+ */
+
+#define DEMOD 0x0000
+#define USB 0x0100
+#define SYS 0x0200
+#define I2C 0x0300
+#define I2C_DA 0x0600
+
+#define CMD_WR_FLAG 0x0010
+#define CMD_DEMOD_RD 0x0000
+#define CMD_DEMOD_WR 0x0010
+#define CMD_USB_RD 0x0100
+#define CMD_USB_WR 0x0110
+#define CMD_SYS_RD 0x0200
+#define CMD_IR_RD 0x0201
+#define CMD_IR_WR 0x0211
+#define CMD_SYS_WR 0x0210
+#define CMD_I2C_RD 0x0300
+#define CMD_I2C_WR 0x0310
+#define CMD_I2C_DA_RD 0x0600
+#define CMD_I2C_DA_WR 0x0610
+
+
+struct rtl28xxu_priv {
+ u8 chip_id;
+ u8 tuner;
+ u8 page; /* integrated demod active register page */
+ bool rc_active;
+};
+
+enum rtl28xxu_chip_id {
+ CHIP_ID_NONE,
+ CHIP_ID_RTL2831U,
+ CHIP_ID_RTL2832U,
+};
+
+enum rtl28xxu_tuner {
+ TUNER_NONE,
+
+ TUNER_RTL2830_QT1010,
+ TUNER_RTL2830_MT2060,
+ TUNER_RTL2830_MXL5005S,
+
+ TUNER_RTL2832_MT2266,
+ TUNER_RTL2832_FC2580,
+ TUNER_RTL2832_MT2063,
+ TUNER_RTL2832_MAX3543,
+ TUNER_RTL2832_TUA9001,
+ TUNER_RTL2832_MXL5007T,
+ TUNER_RTL2832_FC0012,
+ TUNER_RTL2832_E4000,
+ TUNER_RTL2832_TDA18272,
+ TUNER_RTL2832_FC0013,
+};
+
+struct rtl28xxu_req {
+ u16 value;
+ u16 index;
+ u16 size;
+ u8 *data;
+};
+
+struct rtl28xxu_reg_val {
+ u16 reg;
+ u8 val;
+};
+
+/*
+ * memory map
+ *
+ * 0x0000 DEMOD : demodulator
+ * 0x2000 USB : SIE, USB endpoint, debug, DMA
+ * 0x3000 SYS : system
+ * 0xfc00 RC : remote controller (not RTL2831U)
+ */
+
+/*
+ * USB registers
+ */
+/* SIE Control Registers */
+#define USB_SYSCTL 0x2000 /* USB system control */
+#define USB_SYSCTL_0 0x2000 /* USB system control */
+#define USB_SYSCTL_1 0x2001 /* USB system control */
+#define USB_SYSCTL_2 0x2002 /* USB system control */
+#define USB_SYSCTL_3 0x2003 /* USB system control */
+#define USB_IRQSTAT 0x2008 /* SIE interrupt status */
+#define USB_IRQEN 0x200C /* SIE interrupt enable */
+#define USB_CTRL 0x2010 /* USB control */
+#define USB_STAT 0x2014 /* USB status */
+#define USB_DEVADDR 0x2018 /* USB device address */
+#define USB_TEST 0x201C /* USB test mode */
+#define USB_FRAME_NUMBER 0x2020 /* frame number */
+#define USB_FIFO_ADDR 0x2028 /* address of SIE FIFO RAM */
+#define USB_FIFO_CMD 0x202A /* SIE FIFO RAM access command */
+#define USB_FIFO_DATA 0x2030 /* SIE FIFO RAM data */
+/* Endpoint Registers */
+#define EP0_SETUPA 0x20F8 /* EP 0 setup packet lower byte */
+#define EP0_SETUPB 0x20FC /* EP 0 setup packet higher byte */
+#define USB_EP0_CFG 0x2104 /* EP 0 configure */
+#define USB_EP0_CTL 0x2108 /* EP 0 control */
+#define USB_EP0_STAT 0x210C /* EP 0 status */
+#define USB_EP0_IRQSTAT 0x2110 /* EP 0 interrupt status */
+#define USB_EP0_IRQEN 0x2114 /* EP 0 interrupt enable */
+#define USB_EP0_MAXPKT 0x2118 /* EP 0 max packet size */
+#define USB_EP0_BC 0x2120 /* EP 0 FIFO byte counter */
+#define USB_EPA_CFG 0x2144 /* EP A configure */
+#define USB_EPA_CFG_0 0x2144 /* EP A configure */
+#define USB_EPA_CFG_1 0x2145 /* EP A configure */
+#define USB_EPA_CFG_2 0x2146 /* EP A configure */
+#define USB_EPA_CFG_3 0x2147 /* EP A configure */
+#define USB_EPA_CTL 0x2148 /* EP A control */
+#define USB_EPA_CTL_0 0x2148 /* EP A control */
+#define USB_EPA_CTL_1 0x2149 /* EP A control */
+#define USB_EPA_CTL_2 0x214A /* EP A control */
+#define USB_EPA_CTL_3 0x214B /* EP A control */
+#define USB_EPA_STAT 0x214C /* EP A status */
+#define USB_EPA_IRQSTAT 0x2150 /* EP A interrupt status */
+#define USB_EPA_IRQEN 0x2154 /* EP A interrupt enable */
+#define USB_EPA_MAXPKT 0x2158 /* EP A max packet size */
+#define USB_EPA_MAXPKT_0 0x2158 /* EP A max packet size */
+#define USB_EPA_MAXPKT_1 0x2159 /* EP A max packet size */
+#define USB_EPA_MAXPKT_2 0x215A /* EP A max packet size */
+#define USB_EPA_MAXPKT_3 0x215B /* EP A max packet size */
+#define USB_EPA_FIFO_CFG 0x2160 /* EP A FIFO configure */
+#define USB_EPA_FIFO_CFG_0 0x2160 /* EP A FIFO configure */
+#define USB_EPA_FIFO_CFG_1 0x2161 /* EP A FIFO configure */
+#define USB_EPA_FIFO_CFG_2 0x2162 /* EP A FIFO configure */
+#define USB_EPA_FIFO_CFG_3 0x2163 /* EP A FIFO configure */
+/* Debug Registers */
+#define USB_PHYTSTDIS 0x2F04 /* PHY test disable */
+#define USB_TOUT_VAL 0x2F08 /* USB time-out time */
+#define USB_VDRCTRL 0x2F10 /* UTMI vendor signal control */
+#define USB_VSTAIN 0x2F14 /* UTMI vendor signal status in */
+#define USB_VLOADM 0x2F18 /* UTMI load vendor signal status in */
+#define USB_VSTAOUT 0x2F1C /* UTMI vendor signal status out */
+#define USB_UTMI_TST 0x2F80 /* UTMI test */
+#define USB_UTMI_STATUS 0x2F84 /* UTMI status */
+#define USB_TSTCTL 0x2F88 /* test control */
+#define USB_TSTCTL2 0x2F8C /* test control 2 */
+#define USB_PID_FORCE 0x2F90 /* force PID */
+#define USB_PKTERR_CNT 0x2F94 /* packet error counter */
+#define USB_RXERR_CNT 0x2F98 /* RX error counter */
+#define USB_MEM_BIST 0x2F9C /* MEM BIST test */
+#define USB_SLBBIST 0x2FA0 /* self-loop-back BIST */
+#define USB_CNTTEST 0x2FA4 /* counter test */
+#define USB_PHYTST 0x2FC0 /* USB PHY test */
+#define USB_DBGIDX 0x2FF0 /* select individual block debug signal */
+#define USB_DBGMUX 0x2FF4 /* debug signal module mux */
+
+/*
+ * SYS registers
+ */
+/* demod control registers */
+#define SYS_SYS0 0x3000 /* include DEMOD_CTL, GPO, GPI, GPOE */
+#define SYS_DEMOD_CTL 0x3000 /* control register for DVB-T demodulator */
+/* GPIO registers */
+#define SYS_GPIO_OUT_VAL 0x3001 /* output value of GPIO */
+#define SYS_GPIO_IN_VAL 0x3002 /* input value of GPIO */
+#define SYS_GPIO_OUT_EN 0x3003 /* output enable of GPIO */
+#define SYS_SYS1 0x3004 /* include GPD, SYSINTE, SYSINTS, GP_CFG0 */
+#define SYS_GPIO_DIR 0x3004 /* direction control for GPIO */
+#define SYS_SYSINTE 0x3005 /* system interrupt enable */
+#define SYS_SYSINTS 0x3006 /* system interrupt status */
+#define SYS_GPIO_CFG0 0x3007 /* PAD configuration for GPIO0-GPIO3 */
+#define SYS_SYS2 0x3008 /* include GP_CFG1 and 3 reserved bytes */
+#define SYS_GPIO_CFG1 0x3008 /* PAD configuration for GPIO4 */
+#define SYS_DEMOD_CTL1 0x300B
+
+/* IrDA registers */
+#define SYS_IRRC_PSR 0x3020 /* IR protocol selection */
+#define SYS_IRRC_PER 0x3024 /* IR protocol extension */
+#define SYS_IRRC_SF 0x3028 /* IR sampling frequency */
+#define SYS_IRRC_DPIR 0x302C /* IR data package interval */
+#define SYS_IRRC_CR 0x3030 /* IR control */
+#define SYS_IRRC_RP 0x3034 /* IR read port */
+#define SYS_IRRC_SR 0x3038 /* IR status */
+/* I2C master registers */
+#define SYS_I2CCR 0x3040 /* I2C clock */
+#define SYS_I2CMCR 0x3044 /* I2C master control */
+#define SYS_I2CMSTR 0x3048 /* I2C master SCL timing */
+#define SYS_I2CMSR 0x304C /* I2C master status */
+#define SYS_I2CMFR 0x3050 /* I2C master FIFO */
+
+/*
+ * IR registers
+ */
+#define IR_RX_BUF 0xFC00
+#define IR_RX_IE 0xFD00
+#define IR_RX_IF 0xFD01
+#define IR_RX_CTRL 0xFD02
+#define IR_RX_CFG 0xFD03
+#define IR_MAX_DURATION0 0xFD04
+#define IR_MAX_DURATION1 0xFD05
+#define IR_IDLE_LEN0 0xFD06
+#define IR_IDLE_LEN1 0xFD07
+#define IR_GLITCH_LEN 0xFD08
+#define IR_RX_BUF_CTRL 0xFD09
+#define IR_RX_BUF_DATA 0xFD0A
+#define IR_RX_BC 0xFD0B
+#define IR_RX_CLK 0xFD0C
+#define IR_RX_C_COUNT_L 0xFD0D
+#define IR_RX_C_COUNT_H 0xFD0E
+#define IR_SUSPEND_CTRL 0xFD10
+#define IR_ERR_TOL_CTRL 0xFD11
+#define IR_UNIT_LEN 0xFD12
+#define IR_ERR_TOL_LEN 0xFD13
+#define IR_MAX_H_TOL_LEN 0xFD14
+#define IR_MAX_L_TOL_LEN 0xFD15
+#define IR_MASK_CTRL 0xFD16
+#define IR_MASK_DATA 0xFD17
+#define IR_RES_MASK_ADDR 0xFD18
+#define IR_RES_MASK_T_LEN 0xFD19
+
+#endif
diff --git a/drivers/media/dvb/frontends/Kconfig b/drivers/media/dvb/frontends/Kconfig
index ebb5ed7a7783..2995204c3355 100644
--- a/drivers/media/dvb/frontends/Kconfig
+++ b/drivers/media/dvb/frontends/Kconfig
@@ -425,6 +425,13 @@ config DVB_CXD2820R
help
Say Y when you want to support this frontend.
+config DVB_RTL2830
+ tristate "Realtek RTL2830 DVB-T"
+ depends on DVB_CORE && I2C
+ default m if DVB_FE_CUSTOMISE
+ 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 00a20636df62..7f85e64cdcb1 100644
--- a/drivers/media/dvb/frontends/Makefile
+++ b/drivers/media/dvb/frontends/Makefile
@@ -96,4 +96,5 @@ obj-$(CONFIG_DVB_TDA18271C2DD) += tda18271c2dd.o
obj-$(CONFIG_DVB_IT913X_FE) += it913x-fe.o
obj-$(CONFIG_DVB_A8293) += a8293.o
obj-$(CONFIG_DVB_TDA10071) += tda10071.o
+obj-$(CONFIG_DVB_RTL2830) += rtl2830.o
diff --git a/drivers/media/dvb/frontends/rtl2830.c b/drivers/media/dvb/frontends/rtl2830.c
new file mode 100644
index 000000000000..f971d949b2af
--- /dev/null
+++ b/drivers/media/dvb/frontends/rtl2830.c
@@ -0,0 +1,562 @@
+/*
+ * Realtek RTL2830 DVB-T demodulator driver
+ *
+ * Copyright (C) 2011 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+
+/*
+ * Driver implements own I2C-adapter for tuner I2C access. That's since chip
+ * have unusual I2C-gate control which closes gate automatically after each
+ * I2C transfer. Using own I2C adapter we can workaround that.
+ */
+
+#include "rtl2830_priv.h"
+
+int rtl2830_debug;
+module_param_named(debug, rtl2830_debug, int, 0644);
+MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
+
+/* write multiple hardware registers */
+static int rtl2830_wr(struct rtl2830_priv *priv, u8 reg, u8 *val, int len)
+{
+ int ret;
+ u8 buf[1+len];
+ struct i2c_msg msg[1] = {
+ {
+ .addr = priv->cfg.i2c_addr,
+ .flags = 0,
+ .len = 1+len,
+ .buf = buf,
+ }
+ };
+
+ buf[0] = reg;
+ memcpy(&buf[1], val, len);
+
+ ret = i2c_transfer(priv->i2c, msg, 1);
+ if (ret == 1) {
+ ret = 0;
+ } else {
+ warn("i2c wr failed=%d reg=%02x len=%d", ret, reg, len);
+ ret = -EREMOTEIO;
+ }
+ return ret;
+}
+
+/* read multiple hardware registers */
+static int rtl2830_rd(struct rtl2830_priv *priv, u8 reg, u8 *val, int len)
+{
+ int ret;
+ struct i2c_msg msg[2] = {
+ {
+ .addr = priv->cfg.i2c_addr,
+ .flags = 0,
+ .len = 1,
+ .buf = &reg,
+ }, {
+ .addr = priv->cfg.i2c_addr,
+ .flags = I2C_M_RD,
+ .len = len,
+ .buf = val,
+ }
+ };
+
+ ret = i2c_transfer(priv->i2c, msg, 2);
+ if (ret == 2) {
+ ret = 0;
+ } else {
+ warn("i2c rd failed=%d reg=%02x len=%d", ret, reg, len);
+ ret = -EREMOTEIO;
+ }
+ return ret;
+}
+
+/* write multiple registers */
+static int rtl2830_wr_regs(struct rtl2830_priv *priv, u16 reg, u8 *val, int len)
+{
+ int ret;
+ u8 reg2 = (reg >> 0) & 0xff;
+ u8 page = (reg >> 8) & 0xff;
+
+ /* switch bank if needed */
+ if (page != priv->page) {
+ ret = rtl2830_wr(priv, 0x00, &page, 1);
+ if (ret)
+ return ret;
+
+ priv->page = page;
+ }
+
+ return rtl2830_wr(priv, reg2, val, len);
+}
+
+/* read multiple registers */
+static int rtl2830_rd_regs(struct rtl2830_priv *priv, u16 reg, u8 *val, int len)
+{
+ int ret;
+ u8 reg2 = (reg >> 0) & 0xff;
+ u8 page = (reg >> 8) & 0xff;
+
+ /* switch bank if needed */
+ if (page != priv->page) {
+ ret = rtl2830_wr(priv, 0x00, &page, 1);
+ if (ret)
+ return ret;
+
+ priv->page = page;
+ }
+
+ return rtl2830_rd(priv, reg2, val, len);
+}
+
+#if 0 /* currently not used */
+/* write single register */
+static int rtl2830_wr_reg(struct rtl2830_priv *priv, u16 reg, u8 val)
+{
+ return rtl2830_wr_regs(priv, reg, &val, 1);
+}
+#endif
+
+/* read single register */
+static int rtl2830_rd_reg(struct rtl2830_priv *priv, u16 reg, u8 *val)
+{
+ return rtl2830_rd_regs(priv, reg, val, 1);
+}
+
+/* write single register with mask */
+int rtl2830_wr_reg_mask(struct rtl2830_priv *priv, u16 reg, u8 val, u8 mask)
+{
+ int ret;
+ u8 tmp;
+
+ /* no need for read if whole reg is written */
+ if (mask != 0xff) {
+ ret = rtl2830_rd_regs(priv, reg, &tmp, 1);
+ if (ret)
+ return ret;
+
+ val &= mask;
+ tmp &= ~mask;
+ val |= tmp;
+ }
+
+ return rtl2830_wr_regs(priv, reg, &val, 1);
+}
+
+/* read single register with mask */
+int rtl2830_rd_reg_mask(struct rtl2830_priv *priv, u16 reg, u8 *val, u8 mask)
+{
+ int ret, i;
+ u8 tmp;
+
+ ret = rtl2830_rd_regs(priv, reg, &tmp, 1);
+ if (ret)
+ return ret;
+
+ tmp &= mask;
+
+ /* find position of the first bit */
+ for (i = 0; i < 8; i++) {
+ if ((mask >> i) & 0x01)
+ break;
+ }
+ *val = tmp >> i;
+
+ return 0;
+}
+
+static int rtl2830_init(struct dvb_frontend *fe)
+{
+ struct rtl2830_priv *priv = fe->demodulator_priv;
+ int ret, i;
+ u64 num;
+ u8 buf[3], tmp;
+ u32 if_ctl;
+ struct rtl2830_reg_val_mask tab[] = {
+ { 0x00d, 0x01, 0x03 },
+ { 0x00d, 0x10, 0x10 },
+ { 0x104, 0x00, 0x1e },
+ { 0x105, 0x80, 0x80 },
+ { 0x110, 0x02, 0x03 },
+ { 0x110, 0x08, 0x0c },
+ { 0x17b, 0x00, 0x40 },
+ { 0x17d, 0x05, 0x0f },
+ { 0x17d, 0x50, 0xf0 },
+ { 0x18c, 0x08, 0x0f },
+ { 0x18d, 0x00, 0xc0 },
+ { 0x188, 0x05, 0x0f },
+ { 0x189, 0x00, 0xfc },
+ { 0x2d5, 0x02, 0x02 },
+ { 0x2f1, 0x02, 0x06 },
+ { 0x2f1, 0x20, 0xf8 },
+ { 0x16d, 0x00, 0x01 },
+ { 0x1a6, 0x00, 0x80 },
+ { 0x106, priv->cfg.vtop, 0x3f },
+ { 0x107, priv->cfg.krf, 0x3f },
+ { 0x112, 0x28, 0xff },
+ { 0x103, priv->cfg.agc_targ_val, 0xff },
+ { 0x00a, 0x02, 0x07 },
+ { 0x140, 0x0c, 0x3c },
+ { 0x140, 0x40, 0xc0 },
+ { 0x15b, 0x05, 0x07 },
+ { 0x15b, 0x28, 0x38 },
+ { 0x15c, 0x05, 0x07 },
+ { 0x15c, 0x28, 0x38 },
+ { 0x115, priv->cfg.spec_inv, 0x01 },
+ { 0x16f, 0x01, 0x07 },
+ { 0x170, 0x18, 0x38 },
+ { 0x172, 0x0f, 0x0f },
+ { 0x173, 0x08, 0x38 },
+ { 0x175, 0x01, 0x07 },
+ { 0x176, 0x00, 0xc0 },
+ };
+
+ for (i = 0; i < ARRAY_SIZE(tab); i++) {
+ ret = rtl2830_wr_reg_mask(priv, tab[i].reg, tab[i].val,
+ tab[i].mask);
+ if (ret)
+ goto err;
+ }
+
+ ret = rtl2830_wr_regs(priv, 0x18f, "\x28\x00", 2);
+ if (ret)
+ goto err;
+
+ ret = rtl2830_wr_regs(priv, 0x195,
+ "\x04\x06\x0a\x12\x0a\x12\x1e\x28", 8);
+ if (ret)
+ goto err;
+
+ num = priv->cfg.if_dvbt % priv->cfg.xtal;
+ num *= 0x400000;
+ num /= priv->cfg.xtal;
+ num = -num;
+ if_ctl = num & 0x3fffff;
+ dbg("%s: if_ctl=%08x", __func__, if_ctl);
+
+ ret = rtl2830_rd_reg_mask(priv, 0x119, &tmp, 0xc0); /* b[7:6] */
+ if (ret)
+ goto err;
+
+ buf[0] = tmp << 6;
+ buf[0] = (if_ctl >> 16) & 0x3f;
+ buf[1] = (if_ctl >> 8) & 0xff;
+ buf[2] = (if_ctl >> 0) & 0xff;
+
+ ret = rtl2830_wr_regs(priv, 0x119, buf, 3);
+ if (ret)
+ goto err;
+
+ /* TODO: spec init */
+
+ /* soft reset */
+ ret = rtl2830_wr_reg_mask(priv, 0x101, 0x04, 0x04);
+ if (ret)
+ goto err;
+
+ ret = rtl2830_wr_reg_mask(priv, 0x101, 0x00, 0x04);
+ if (ret)
+ goto err;
+
+ priv->sleeping = false;
+
+ return ret;
+err:
+ dbg("%s: failed=%d", __func__, ret);
+ return ret;
+}
+
+static int rtl2830_sleep(struct dvb_frontend *fe)
+{
+ struct rtl2830_priv *priv = fe->demodulator_priv;
+ priv->sleeping = true;
+ return 0;
+}
+
+int rtl2830_get_tune_settings(struct dvb_frontend *fe,
+ struct dvb_frontend_tune_settings *s)
+{
+ s->min_delay_ms = 500;
+ s->step_size = fe->ops.info.frequency_stepsize * 2;
+ s->max_drift = (fe->ops.info.frequency_stepsize * 2) + 1;
+
+ return 0;
+}
+
+static int rtl2830_set_frontend(struct dvb_frontend *fe)
+{
+ struct rtl2830_priv *priv = fe->demodulator_priv;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ int ret, i;
+ static u8 bw_params1[3][34] = {
+ {
+ 0x1f, 0xf0, 0x1f, 0xf0, 0x1f, 0xfa, 0x00, 0x17, 0x00, 0x41,
+ 0x00, 0x64, 0x00, 0x67, 0x00, 0x38, 0x1f, 0xde, 0x1f, 0x7a,
+ 0x1f, 0x47, 0x1f, 0x7c, 0x00, 0x30, 0x01, 0x4b, 0x02, 0x82,
+ 0x03, 0x73, 0x03, 0xcf, /* 6 MHz */
+ }, {
+ 0x1f, 0xfa, 0x1f, 0xda, 0x1f, 0xc1, 0x1f, 0xb3, 0x1f, 0xca,
+ 0x00, 0x07, 0x00, 0x4d, 0x00, 0x6d, 0x00, 0x40, 0x1f, 0xca,
+ 0x1f, 0x4d, 0x1f, 0x2a, 0x1f, 0xb2, 0x00, 0xec, 0x02, 0x7e,
+ 0x03, 0xd0, 0x04, 0x53, /* 7 MHz */
+ }, {
+ 0x00, 0x10, 0x00, 0x0e, 0x1f, 0xf7, 0x1f, 0xc9, 0x1f, 0xa0,
+ 0x1f, 0xa6, 0x1f, 0xec, 0x00, 0x4e, 0x00, 0x7d, 0x00, 0x3a,
+ 0x1f, 0x98, 0x1f, 0x10, 0x1f, 0x40, 0x00, 0x75, 0x02, 0x5f,
+ 0x04, 0x24, 0x04, 0xdb, /* 8 MHz */
+ },
+ };
+ static u8 bw_params2[3][6] = {
+ {0xc3, 0x0c, 0x44, 0x33, 0x33, 0x30,}, /* 6 MHz */
+ {0xb8, 0xe3, 0x93, 0x99, 0x99, 0x98,}, /* 7 MHz */
+ {0xae, 0xba, 0xf3, 0x26, 0x66, 0x64,}, /* 8 MHz */
+ };
+
+
+ dbg("%s: frequency=%d bandwidth_hz=%d inversion=%d", __func__,
+ c->frequency, c->bandwidth_hz, c->inversion);
+
+ /* program tuner */
+ if (fe->ops.tuner_ops.set_params)
+ fe->ops.tuner_ops.set_params(fe);
+
+ switch (c->bandwidth_hz) {
+ case 6000000:
+ i = 0;
+ break;
+ case 7000000:
+ i = 1;
+ break;
+ case 8000000:
+ i = 2;
+ break;
+ default:
+ dbg("invalid bandwidth");
+ return -EINVAL;
+ }
+
+ ret = rtl2830_wr_reg_mask(priv, 0x008, i << 1, 0x06);
+ if (ret)
+ goto err;
+
+ /* 1/2 split I2C write */
+ ret = rtl2830_wr_regs(priv, 0x11c, &bw_params1[i][0], 17);
+ if (ret)
+ goto err;
+
+ /* 2/2 split I2C write */
+ ret = rtl2830_wr_regs(priv, 0x12d, &bw_params1[i][17], 17);
+ if (ret)
+ goto err;
+
+ ret = rtl2830_wr_regs(priv, 0x19d, bw_params2[i], 6);
+ if (ret)
+ goto err;
+
+ return ret;
+err:
+ dbg("%s: failed=%d", __func__, ret);
+ return ret;
+}
+
+static int rtl2830_read_status(struct dvb_frontend *fe, fe_status_t *status)
+{
+ struct rtl2830_priv *priv = fe->demodulator_priv;
+ int ret;
+ u8 tmp;
+ *status = 0;
+
+ if (priv->sleeping)
+ return 0;
+
+ ret = rtl2830_rd_reg_mask(priv, 0x351, &tmp, 0x78); /* [6:3] */
+ if (ret)
+ goto err;
+
+ if (tmp == 11) {
+ *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER |
+ FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK;
+ } else if (tmp == 10) {
+ *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER |
+ FE_HAS_VITERBI;
+ }
+
+ return ret;
+err:
+ dbg("%s: failed=%d", __func__, ret);
+ return ret;
+}
+
+static int rtl2830_read_snr(struct dvb_frontend *fe, u16 *snr)
+{
+ *snr = 0;
+ return 0;
+}
+
+static int rtl2830_read_ber(struct dvb_frontend *fe, u32 *ber)
+{
+ *ber = 0;
+ return 0;
+}
+
+static int rtl2830_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
+{
+ *ucblocks = 0;
+ return 0;
+}
+
+static int rtl2830_read_signal_strength(struct dvb_frontend *fe, u16 *strength)
+{
+ *strength = 0;
+ return 0;
+}
+
+static struct dvb_frontend_ops rtl2830_ops;
+
+static u32 rtl2830_tuner_i2c_func(struct i2c_adapter *adapter)
+{
+ return I2C_FUNC_I2C;
+}
+
+static int rtl2830_tuner_i2c_xfer(struct i2c_adapter *i2c_adap,
+ struct i2c_msg msg[], int num)
+{
+ struct rtl2830_priv *priv = i2c_get_adapdata(i2c_adap);
+ int ret;
+
+ /* open i2c-gate */
+ ret = rtl2830_wr_reg_mask(priv, 0x101, 0x08, 0x08);
+ if (ret)
+ goto err;
+
+ ret = i2c_transfer(priv->i2c, msg, num);
+ if (ret < 0)
+ warn("tuner i2c failed=%d", ret);
+
+ return ret;
+err:
+ dbg("%s: failed=%d", __func__, ret);
+ return ret;
+}
+
+static struct i2c_algorithm rtl2830_tuner_i2c_algo = {
+ .master_xfer = rtl2830_tuner_i2c_xfer,
+ .functionality = rtl2830_tuner_i2c_func,
+};
+
+struct i2c_adapter *rtl2830_get_tuner_i2c_adapter(struct dvb_frontend *fe)
+{
+ struct rtl2830_priv *priv = fe->demodulator_priv;
+ return &priv->tuner_i2c_adapter;
+}
+EXPORT_SYMBOL(rtl2830_get_tuner_i2c_adapter);
+
+static void rtl2830_release(struct dvb_frontend *fe)
+{
+ struct rtl2830_priv *priv = fe->demodulator_priv;
+
+ i2c_del_adapter(&priv->tuner_i2c_adapter);
+ kfree(priv);
+}
+
+struct dvb_frontend *rtl2830_attach(const struct rtl2830_config *cfg,
+ struct i2c_adapter *i2c)
+{
+ struct rtl2830_priv *priv = NULL;
+ int ret = 0;
+ u8 tmp;
+
+ /* allocate memory for the internal state */
+ priv = kzalloc(sizeof(struct rtl2830_priv), GFP_KERNEL);
+ if (priv == NULL)
+ goto err;
+
+ /* setup the priv */
+ priv->i2c = i2c;
+ memcpy(&priv->cfg, cfg, sizeof(struct rtl2830_config));
+
+ /* check if the demod is there */
+ ret = rtl2830_rd_reg(priv, 0x000, &tmp);
+ if (ret)
+ goto err;
+
+ /* create dvb_frontend */
+ memcpy(&priv->fe.ops, &rtl2830_ops, sizeof(struct dvb_frontend_ops));
+ priv->fe.demodulator_priv = priv;
+
+ /* create tuner i2c adapter */
+ strlcpy(priv->tuner_i2c_adapter.name, "RTL2830 tuner I2C adapter",
+ sizeof(priv->tuner_i2c_adapter.name));
+ priv->tuner_i2c_adapter.algo = &rtl2830_tuner_i2c_algo;
+ priv->tuner_i2c_adapter.algo_data = NULL;
+ i2c_set_adapdata(&priv->tuner_i2c_adapter, priv);
+ if (i2c_add_adapter(&priv->tuner_i2c_adapter) < 0) {
+ err("tuner I2C bus could not be initialized");
+ goto err;
+ }
+
+ priv->sleeping = true;
+
+ return &priv->fe;
+err:
+ dbg("%s: failed=%d", __func__, ret);
+ kfree(priv);
+ return NULL;
+}
+EXPORT_SYMBOL(rtl2830_attach);
+
+static struct dvb_frontend_ops rtl2830_ops = {
+ .delsys = { SYS_DVBT },
+ .info = {
+ .name = "Realtek RTL2830 (DVB-T)",
+ .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_64 |
+ FE_CAN_QAM_AUTO |
+ FE_CAN_TRANSMISSION_MODE_AUTO |
+ FE_CAN_GUARD_INTERVAL_AUTO |
+ FE_CAN_HIERARCHY_AUTO |
+ FE_CAN_RECOVER |
+ FE_CAN_MUTE_TS
+ },
+
+ .release = rtl2830_release,
+
+ .init = rtl2830_init,
+ .sleep = rtl2830_sleep,
+
+ .get_tune_settings = rtl2830_get_tune_settings,
+
+ .set_frontend = rtl2830_set_frontend,
+
+ .read_status = rtl2830_read_status,
+ .read_snr = rtl2830_read_snr,
+ .read_ber = rtl2830_read_ber,
+ .read_ucblocks = rtl2830_read_ucblocks,
+ .read_signal_strength = rtl2830_read_signal_strength,
+};
+
+MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
+MODULE_DESCRIPTION("Realtek RTL2830 DVB-T demodulator driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/frontends/rtl2830.h b/drivers/media/dvb/frontends/rtl2830.h
new file mode 100644
index 000000000000..1c6ee91749c2
--- /dev/null
+++ b/drivers/media/dvb/frontends/rtl2830.h
@@ -0,0 +1,97 @@
+/*
+ * Realtek RTL2830 DVB-T demodulator driver
+ *
+ * Copyright (C) 2011 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef RTL2830_H
+#define RTL2830_H
+
+#include <linux/dvb/frontend.h>
+
+struct rtl2830_config {
+ /*
+ * Demodulator I2C address.
+ */
+ u8 i2c_addr;
+
+ /*
+ * Xtal frequency.
+ * Hz
+ * 4000000, 16000000, 25000000, 28800000
+ */
+ u32 xtal;
+
+ /*
+ * TS output mode.
+ */
+ u8 ts_mode;
+
+ /*
+ * Spectrum inversion.
+ */
+ bool spec_inv;
+
+ /*
+ * IFs for all used modes.
+ * Hz
+ * 4570000, 4571429, 36000000, 36125000, 36166667, 44000000
+ */
+ u32 if_dvbt;
+
+ /*
+ */
+ u8 vtop;
+
+ /*
+ */
+ u8 krf;
+
+ /*
+ */
+ u8 agc_targ_val;
+};
+
+#if defined(CONFIG_DVB_RTL2830) || \
+ (defined(CONFIG_DVB_RTL2830_MODULE) && defined(MODULE))
+extern struct dvb_frontend *rtl2830_attach(
+ const struct rtl2830_config *config,
+ struct i2c_adapter *i2c
+);
+
+extern struct i2c_adapter *rtl2830_get_tuner_i2c_adapter(
+ struct dvb_frontend *fe
+);
+#else
+static inline struct dvb_frontend *rtl2830_attach(
+ const struct rtl2830_config *config,
+ struct i2c_adapter *i2c
+)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return NULL;
+}
+
+static inline struct i2c_adapter *rtl2830_get_tuner_i2c_adapter(
+ struct dvb_frontend *fe
+)
+{
+ return NULL;
+}
+#endif
+
+#endif /* RTL2830_H */
diff --git a/drivers/media/dvb/frontends/rtl2830_priv.h b/drivers/media/dvb/frontends/rtl2830_priv.h
new file mode 100644
index 000000000000..4a464761b5b8
--- /dev/null
+++ b/drivers/media/dvb/frontends/rtl2830_priv.h
@@ -0,0 +1,57 @@
+/*
+ * Realtek RTL2830 DVB-T demodulator driver
+ *
+ * Copyright (C) 2011 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef RTL2830_PRIV_H
+#define RTL2830_PRIV_H
+
+#include "dvb_frontend.h"
+#include "rtl2830.h"
+
+#define LOG_PREFIX "rtl2830"
+
+#undef dbg
+#define dbg(f, arg...) \
+ if (rtl2830_debug) \
+ printk(KERN_INFO LOG_PREFIX": " f "\n" , ## arg)
+#undef err
+#define err(f, arg...) printk(KERN_ERR LOG_PREFIX": " f "\n" , ## arg)
+#undef info
+#define info(f, arg...) printk(KERN_INFO LOG_PREFIX": " f "\n" , ## arg)
+#undef warn
+#define warn(f, arg...) printk(KERN_WARNING LOG_PREFIX": " f "\n" , ## arg)
+
+struct rtl2830_priv {
+ struct i2c_adapter *i2c;
+ struct dvb_frontend fe;
+ struct rtl2830_config cfg;
+ struct i2c_adapter tuner_i2c_adapter;
+
+ bool sleeping;
+
+ u8 page; /* active register page */
+};
+
+struct rtl2830_reg_val_mask {
+ u16 reg;
+ u8 val;
+ u8 mask;
+};
+
+#endif /* RTL2830_PRIV_H */
diff --git a/drivers/media/dvb/mantis/mantis_hif.c b/drivers/media/dvb/mantis/mantis_hif.c
index 672cf4d2462d..10c68df7e16f 100644
--- a/drivers/media/dvb/mantis/mantis_hif.c
+++ b/drivers/media/dvb/mantis/mantis_hif.c
@@ -76,7 +76,7 @@ static int mantis_hif_write_wait(struct mantis_ca *ca)
udelay(500);
timeout++;
if (timeout > 100) {
- dprintk(MANTIS_ERROR, 1, "Adater(%d) Slot(0): Write operation timed out!", mantis->num);
+ dprintk(MANTIS_ERROR, 1, "Adapter(%d) Slot(0): Write operation timed out!", mantis->num);
rc = -ETIMEDOUT;
break;
}
diff --git a/drivers/media/radio/wl128x/Kconfig b/drivers/media/radio/wl128x/Kconfig
index 86b28579f0c7..ea1e6545df36 100644
--- a/drivers/media/radio/wl128x/Kconfig
+++ b/drivers/media/radio/wl128x/Kconfig
@@ -4,8 +4,8 @@
menu "Texas Instruments WL128x FM driver (ST based)"
config RADIO_WL128X
tristate "Texas Instruments WL128x FM Radio"
- depends on VIDEO_V4L2 && RFKILL
- select TI_ST if NET && GPIOLIB
+ depends on VIDEO_V4L2 && RFKILL && GPIOLIB
+ select TI_ST if NET
help
Choose Y here if you have this FM radio chip.
diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig
index ca7c75862374..8527fc63e42b 100644
--- a/drivers/media/video/Kconfig
+++ b/drivers/media/video/Kconfig
@@ -1188,4 +1188,14 @@ config VIDEO_SAMSUNG_S5P_MFC
help
MFC 5.1 driver for V4L2.
+config VIDEO_MX2_EMMAPRP
+ tristate "MX2 eMMa-PrP support"
+ depends on VIDEO_DEV && VIDEO_V4L2 && SOC_IMX27
+ select VIDEOBUF2_DMA_CONTIG
+ select V4L2_MEM2MEM_DEV
+ help
+ MX2X chips have a PrP that can be used to process buffers from
+ memory to memory. Operations include resizing and format
+ conversion.
+
endif # V4L_MEM2MEM_DRIVERS
diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile
index 354138804cda..563443c6b635 100644
--- a/drivers/media/video/Makefile
+++ b/drivers/media/video/Makefile
@@ -177,6 +177,8 @@ obj-$(CONFIG_VIDEO_SH_MOBILE_CEU) += sh_mobile_ceu_camera.o
obj-$(CONFIG_VIDEO_OMAP1) += omap1_camera.o
obj-$(CONFIG_VIDEO_ATMEL_ISI) += atmel-isi.o
+obj-$(CONFIG_VIDEO_MX2_EMMAPRP) += mx2_emmaprp.o
+
obj-$(CONFIG_VIDEO_SAMSUNG_S5P_FIMC) += s5p-fimc/
obj-$(CONFIG_VIDEO_SAMSUNG_S5P_JPEG) += s5p-jpeg/
obj-$(CONFIG_VIDEO_SAMSUNG_S5P_MFC) += s5p-mfc/
diff --git a/drivers/media/video/hdpvr/hdpvr-core.c b/drivers/media/video/hdpvr/hdpvr-core.c
index e5eb56a5b618..6510110f53d0 100644
--- a/drivers/media/video/hdpvr/hdpvr-core.c
+++ b/drivers/media/video/hdpvr/hdpvr-core.c
@@ -154,10 +154,20 @@ static int device_authorization(struct hdpvr_device *dev)
}
#endif
+ dev->fw_ver = dev->usbc_buf[1];
+
v4l2_info(&dev->v4l2_dev, "firmware version 0x%x dated %s\n",
- dev->usbc_buf[1], &dev->usbc_buf[2]);
+ dev->fw_ver, &dev->usbc_buf[2]);
+
+ if (dev->fw_ver > 0x15) {
+ dev->options.brightness = 0x80;
+ dev->options.contrast = 0x40;
+ dev->options.hue = 0xf;
+ dev->options.saturation = 0x40;
+ dev->options.sharpness = 0x80;
+ }
- switch (dev->usbc_buf[1]) {
+ switch (dev->fw_ver) {
case HDPVR_FIRMWARE_VERSION:
dev->flags &= ~HDPVR_FLAG_AC3_CAP;
break;
@@ -169,7 +179,7 @@ static int device_authorization(struct hdpvr_device *dev)
default:
v4l2_info(&dev->v4l2_dev, "untested firmware, the driver might"
" not work.\n");
- if (dev->usbc_buf[1] >= HDPVR_FIRMWARE_VERSION_AC3)
+ if (dev->fw_ver >= HDPVR_FIRMWARE_VERSION_AC3)
dev->flags |= HDPVR_FLAG_AC3_CAP;
else
dev->flags &= ~HDPVR_FLAG_AC3_CAP;
@@ -270,6 +280,8 @@ static const struct hdpvr_options hdpvr_default_options = {
.bitrate_mode = HDPVR_CONSTANT,
.gop_mode = HDPVR_SIMPLE_IDR_GOP,
.audio_codec = V4L2_MPEG_AUDIO_ENCODING_AAC,
+ /* original picture controls for firmware version <= 0x15 */
+ /* updated in device_authorization() for newer firmware */
.brightness = 0x86,
.contrast = 0x80,
.hue = 0x80,
diff --git a/drivers/media/video/hdpvr/hdpvr-video.c b/drivers/media/video/hdpvr/hdpvr-video.c
index 087f7c08cb85..36bb057e3023 100644
--- a/drivers/media/video/hdpvr/hdpvr-video.c
+++ b/drivers/media/video/hdpvr/hdpvr-video.c
@@ -722,21 +722,39 @@ static const s32 supported_v4l2_ctrls[] = {
};
static int fill_queryctrl(struct hdpvr_options *opt, struct v4l2_queryctrl *qc,
- int ac3)
+ int ac3, int fw_ver)
{
int err;
+ if (fw_ver > 0x15) {
+ switch (qc->id) {
+ case V4L2_CID_BRIGHTNESS:
+ return v4l2_ctrl_query_fill(qc, 0x0, 0xff, 1, 0x80);
+ case V4L2_CID_CONTRAST:
+ return v4l2_ctrl_query_fill(qc, 0x0, 0xff, 1, 0x40);
+ case V4L2_CID_SATURATION:
+ return v4l2_ctrl_query_fill(qc, 0x0, 0xff, 1, 0x40);
+ case V4L2_CID_HUE:
+ return v4l2_ctrl_query_fill(qc, 0x0, 0x1e, 1, 0xf);
+ case V4L2_CID_SHARPNESS:
+ return v4l2_ctrl_query_fill(qc, 0x0, 0xff, 1, 0x80);
+ }
+ } else {
+ switch (qc->id) {
+ case V4L2_CID_BRIGHTNESS:
+ return v4l2_ctrl_query_fill(qc, 0x0, 0xff, 1, 0x86);
+ case V4L2_CID_CONTRAST:
+ return v4l2_ctrl_query_fill(qc, 0x0, 0xff, 1, 0x80);
+ case V4L2_CID_SATURATION:
+ return v4l2_ctrl_query_fill(qc, 0x0, 0xff, 1, 0x80);
+ case V4L2_CID_HUE:
+ return v4l2_ctrl_query_fill(qc, 0x0, 0xff, 1, 0x80);
+ case V4L2_CID_SHARPNESS:
+ return v4l2_ctrl_query_fill(qc, 0x0, 0xff, 1, 0x80);
+ }
+ }
+
switch (qc->id) {
- case V4L2_CID_BRIGHTNESS:
- return v4l2_ctrl_query_fill(qc, 0x0, 0xff, 1, 0x86);
- case V4L2_CID_CONTRAST:
- return v4l2_ctrl_query_fill(qc, 0x0, 0xff, 1, 0x80);
- case V4L2_CID_SATURATION:
- return v4l2_ctrl_query_fill(qc, 0x0, 0xff, 1, 0x80);
- case V4L2_CID_HUE:
- return v4l2_ctrl_query_fill(qc, 0x0, 0xff, 1, 0x80);
- case V4L2_CID_SHARPNESS:
- return v4l2_ctrl_query_fill(qc, 0x0, 0xff, 1, 0x80);
case V4L2_CID_MPEG_AUDIO_ENCODING:
return v4l2_ctrl_query_fill(
qc, V4L2_MPEG_AUDIO_ENCODING_AAC,
@@ -794,7 +812,8 @@ static int vidioc_queryctrl(struct file *file, void *private_data,
if (qc->id == supported_v4l2_ctrls[i])
return fill_queryctrl(&dev->options, qc,
- dev->flags & HDPVR_FLAG_AC3_CAP);
+ dev->flags & HDPVR_FLAG_AC3_CAP,
+ dev->fw_ver);
if (qc->id < supported_v4l2_ctrls[i])
break;
diff --git a/drivers/media/video/hdpvr/hdpvr.h b/drivers/media/video/hdpvr/hdpvr.h
index d6439db1d18b..fea3c6926997 100644
--- a/drivers/media/video/hdpvr/hdpvr.h
+++ b/drivers/media/video/hdpvr/hdpvr.h
@@ -113,6 +113,7 @@ struct hdpvr_device {
/* usb control transfer buffer and lock */
struct mutex usbc_mutex;
u8 *usbc_buf;
+ u8 fw_ver;
};
static inline struct hdpvr_device *to_hdpvr_dev(struct v4l2_device *v4l2_dev)
diff --git a/drivers/media/video/ivtv/ivtv-controls.c b/drivers/media/video/ivtv/ivtv-controls.c
index e2901184db09..c60424601cb9 100644
--- a/drivers/media/video/ivtv/ivtv-controls.c
+++ b/drivers/media/video/ivtv/ivtv-controls.c
@@ -129,7 +129,7 @@ int ivtv_g_pts_frame(struct ivtv *itv, s64 *pts, s64 *frame)
static int ivtv_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
{
- struct ivtv *itv = container_of(ctrl->handler, struct ivtv, hdl_out);
+ struct ivtv *itv = container_of(ctrl->handler, struct ivtv, cxhdl.hdl);
switch (ctrl->id) {
/* V4L2_CID_MPEG_VIDEO_DEC_PTS and V4L2_CID_MPEG_VIDEO_DEC_FRAME
@@ -143,7 +143,7 @@ static int ivtv_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
static int ivtv_s_ctrl(struct v4l2_ctrl *ctrl)
{
- struct ivtv *itv = container_of(ctrl->handler, struct ivtv, hdl_out);
+ struct ivtv *itv = container_of(ctrl->handler, struct ivtv, cxhdl.hdl);
switch (ctrl->id) {
/* V4L2_CID_MPEG_AUDIO_DEC_PLAYBACK and MULTILINGUAL_PLAYBACK
diff --git a/drivers/media/video/ivtv/ivtv-driver.c b/drivers/media/video/ivtv/ivtv-driver.c
index 107e9e6d6a52..cfc4276d907e 100644
--- a/drivers/media/video/ivtv/ivtv-driver.c
+++ b/drivers/media/video/ivtv/ivtv-driver.c
@@ -1198,28 +1198,28 @@ static int __devinit ivtv_probe(struct pci_dev *pdev,
itv->tuner_std = itv->std;
if (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT) {
- v4l2_ctrl_handler_init(&itv->hdl_out, 50);
- itv->ctrl_pts = v4l2_ctrl_new_std(&itv->hdl_out, &ivtv_hdl_out_ops,
+ struct v4l2_ctrl_handler *hdl = itv->v4l2_dev.ctrl_handler;
+
+ itv->ctrl_pts = v4l2_ctrl_new_std(hdl, &ivtv_hdl_out_ops,
V4L2_CID_MPEG_VIDEO_DEC_PTS, 0, 0, 1, 0);
- itv->ctrl_frame = v4l2_ctrl_new_std(&itv->hdl_out, &ivtv_hdl_out_ops,
+ itv->ctrl_frame = v4l2_ctrl_new_std(hdl, &ivtv_hdl_out_ops,
V4L2_CID_MPEG_VIDEO_DEC_FRAME, 0, 0x7fffffff, 1, 0);
/* Note: V4L2_MPEG_AUDIO_DEC_PLAYBACK_AUTO is not supported,
mask that menu item. */
itv->ctrl_audio_playback =
- v4l2_ctrl_new_std_menu(&itv->hdl_out, &ivtv_hdl_out_ops,
+ v4l2_ctrl_new_std_menu(hdl, &ivtv_hdl_out_ops,
V4L2_CID_MPEG_AUDIO_DEC_PLAYBACK,
V4L2_MPEG_AUDIO_DEC_PLAYBACK_SWAPPED_STEREO,
1 << V4L2_MPEG_AUDIO_DEC_PLAYBACK_AUTO,
V4L2_MPEG_AUDIO_DEC_PLAYBACK_STEREO);
itv->ctrl_audio_multilingual_playback =
- v4l2_ctrl_new_std_menu(&itv->hdl_out, &ivtv_hdl_out_ops,
+ v4l2_ctrl_new_std_menu(hdl, &ivtv_hdl_out_ops,
V4L2_CID_MPEG_AUDIO_DEC_MULTILINGUAL_PLAYBACK,
V4L2_MPEG_AUDIO_DEC_PLAYBACK_SWAPPED_STEREO,
1 << V4L2_MPEG_AUDIO_DEC_PLAYBACK_AUTO,
V4L2_MPEG_AUDIO_DEC_PLAYBACK_LEFT);
- v4l2_ctrl_add_handler(&itv->hdl_out, &itv->cxhdl.hdl);
- if (itv->hdl_out.error) {
- retval = itv->hdl_out.error;
+ if (hdl->error) {
+ retval = hdl->error;
goto free_i2c;
}
v4l2_ctrl_cluster(2, &itv->ctrl_pts);
@@ -1260,8 +1260,6 @@ free_streams:
free_irq:
free_irq(itv->pdev->irq, (void *)itv);
free_i2c:
- if (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)
- v4l2_ctrl_handler_free(&itv->hdl_out);
v4l2_ctrl_handler_free(&itv->cxhdl.hdl);
exit_ivtv_i2c(itv);
free_io:
@@ -1418,8 +1416,6 @@ static void ivtv_remove(struct pci_dev *pdev)
ivtv_streams_cleanup(itv, 1);
ivtv_udma_free(itv);
- if (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)
- v4l2_ctrl_handler_free(&itv->hdl_out);
v4l2_ctrl_handler_free(&itv->cxhdl.hdl);
exit_ivtv_i2c(itv);
diff --git a/drivers/media/video/ivtv/ivtv-driver.h b/drivers/media/video/ivtv/ivtv-driver.h
index f83857aedbd1..f767df943954 100644
--- a/drivers/media/video/ivtv/ivtv-driver.h
+++ b/drivers/media/video/ivtv/ivtv-driver.h
@@ -641,7 +641,6 @@ struct ivtv {
struct v4l2_ctrl *ctrl_audio_playback;
struct v4l2_ctrl *ctrl_audio_multilingual_playback;
};
- struct v4l2_ctrl_handler hdl_out;
struct v4l2_ctrl_handler hdl_gpio;
struct v4l2_subdev sd_gpio; /* GPIO sub-device */
u16 instance;
diff --git a/drivers/media/video/ivtv/ivtv-ioctl.c b/drivers/media/video/ivtv/ivtv-ioctl.c
index 385e003fcfbb..5452beef8e11 100644
--- a/drivers/media/video/ivtv/ivtv-ioctl.c
+++ b/drivers/media/video/ivtv/ivtv-ioctl.c
@@ -1548,10 +1548,7 @@ static int ivtv_log_status(struct file *file, void *fh)
}
IVTV_INFO("Tuner: %s\n",
test_bit(IVTV_F_I_RADIO_USER, &itv->i_flags) ? "Radio" : "TV");
- if (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)
- v4l2_ctrl_handler_log_status(&itv->hdl_out, itv->v4l2_dev.name);
- else
- v4l2_ctrl_handler_log_status(&itv->cxhdl.hdl, itv->v4l2_dev.name);
+ v4l2_ctrl_handler_log_status(&itv->cxhdl.hdl, itv->v4l2_dev.name);
IVTV_INFO("Status flags: 0x%08lx\n", itv->i_flags);
for (i = 0; i < IVTV_MAX_STREAMS; i++) {
struct ivtv_stream *s = &itv->streams[i];
diff --git a/drivers/media/video/ivtv/ivtv-streams.c b/drivers/media/video/ivtv/ivtv-streams.c
index 9cc037c12609..7ea5ca7f012b 100644
--- a/drivers/media/video/ivtv/ivtv-streams.c
+++ b/drivers/media/video/ivtv/ivtv-streams.c
@@ -223,11 +223,8 @@ static int ivtv_prep_dev(struct ivtv *itv, int type)
s->vdev->num = num;
s->vdev->v4l2_dev = &itv->v4l2_dev;
- if (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)
- s->vdev->ctrl_handler = &itv->hdl_out;
- else
- s->vdev->ctrl_handler = itv->v4l2_dev.ctrl_handler;
s->vdev->fops = ivtv_stream_info[type].fops;
+ s->vdev->ctrl_handler = itv->v4l2_dev.ctrl_handler;
s->vdev->release = video_device_release;
s->vdev->tvnorms = V4L2_STD_ALL;
s->vdev->lock = &itv->serialize_lock;
diff --git a/drivers/media/video/mx2_emmaprp.c b/drivers/media/video/mx2_emmaprp.c
new file mode 100644
index 000000000000..ba89a7401c8c
--- /dev/null
+++ b/drivers/media/video/mx2_emmaprp.c
@@ -0,0 +1,1008 @@
+/*
+ * Support eMMa-PrP through mem2mem framework.
+ *
+ * eMMa-PrP is a piece of HW that allows fetching buffers
+ * from one memory location and do several operations on
+ * them such as scaling or format conversion giving, as a result
+ * a new processed buffer in another memory location.
+ *
+ * Based on mem2mem_testdev.c by Pawel Osciak.
+ *
+ * Copyright (c) 2011 Vista Silicon S.L.
+ * Javier Martin <javier.martin@vista-silicon.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/module.h>
+#include <linux/clk.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+
+#include <linux/platform_device.h>
+#include <media/v4l2-mem2mem.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ioctl.h>
+#include <media/videobuf2-dma-contig.h>
+#include <asm/sizes.h>
+
+#define EMMAPRP_MODULE_NAME "mem2mem-emmaprp"
+
+MODULE_DESCRIPTION("Mem-to-mem device which supports eMMa-PrP present in mx2 SoCs");
+MODULE_AUTHOR("Javier Martin <javier.martin@vista-silicon.com");
+MODULE_LICENSE("GPL");
+MODULE_VERSION("0.0.1");
+
+static bool debug;
+module_param(debug, bool, 0644);
+
+#define MIN_W 32
+#define MIN_H 32
+#define MAX_W 2040
+#define MAX_H 2046
+
+#define S_ALIGN 1 /* multiple of 2 */
+#define W_ALIGN_YUV420 3 /* multiple of 8 */
+#define W_ALIGN_OTHERS 2 /* multiple of 4 */
+#define H_ALIGN 1 /* multiple of 2 */
+
+/* Flags that indicate a format can be used for capture/output */
+#define MEM2MEM_CAPTURE (1 << 0)
+#define MEM2MEM_OUTPUT (1 << 1)
+
+#define MEM2MEM_NAME "m2m-emmaprp"
+
+/* In bytes, per queue */
+#define MEM2MEM_VID_MEM_LIMIT SZ_16M
+
+#define dprintk(dev, fmt, arg...) \
+ v4l2_dbg(1, debug, &dev->v4l2_dev, "%s: " fmt, __func__, ## arg)
+
+/* EMMA PrP */
+#define PRP_CNTL 0x00
+#define PRP_INTR_CNTL 0x04
+#define PRP_INTRSTATUS 0x08
+#define PRP_SOURCE_Y_PTR 0x0c
+#define PRP_SOURCE_CB_PTR 0x10
+#define PRP_SOURCE_CR_PTR 0x14
+#define PRP_DEST_RGB1_PTR 0x18
+#define PRP_DEST_RGB2_PTR 0x1c
+#define PRP_DEST_Y_PTR 0x20
+#define PRP_DEST_CB_PTR 0x24
+#define PRP_DEST_CR_PTR 0x28
+#define PRP_SRC_FRAME_SIZE 0x2c
+#define PRP_DEST_CH1_LINE_STRIDE 0x30
+#define PRP_SRC_PIXEL_FORMAT_CNTL 0x34
+#define PRP_CH1_PIXEL_FORMAT_CNTL 0x38
+#define PRP_CH1_OUT_IMAGE_SIZE 0x3c
+#define PRP_CH2_OUT_IMAGE_SIZE 0x40
+#define PRP_SRC_LINE_STRIDE 0x44
+#define PRP_CSC_COEF_012 0x48
+#define PRP_CSC_COEF_345 0x4c
+#define PRP_CSC_COEF_678 0x50
+#define PRP_CH1_RZ_HORI_COEF1 0x54
+#define PRP_CH1_RZ_HORI_COEF2 0x58
+#define PRP_CH1_RZ_HORI_VALID 0x5c
+#define PRP_CH1_RZ_VERT_COEF1 0x60
+#define PRP_CH1_RZ_VERT_COEF2 0x64
+#define PRP_CH1_RZ_VERT_VALID 0x68
+#define PRP_CH2_RZ_HORI_COEF1 0x6c
+#define PRP_CH2_RZ_HORI_COEF2 0x70
+#define PRP_CH2_RZ_HORI_VALID 0x74
+#define PRP_CH2_RZ_VERT_COEF1 0x78
+#define PRP_CH2_RZ_VERT_COEF2 0x7c
+#define PRP_CH2_RZ_VERT_VALID 0x80
+
+#define PRP_CNTL_CH1EN (1 << 0)
+#define PRP_CNTL_CH2EN (1 << 1)
+#define PRP_CNTL_CSIEN (1 << 2)
+#define PRP_CNTL_DATA_IN_YUV420 (0 << 3)
+#define PRP_CNTL_DATA_IN_YUV422 (1 << 3)
+#define PRP_CNTL_DATA_IN_RGB16 (2 << 3)
+#define PRP_CNTL_DATA_IN_RGB32 (3 << 3)
+#define PRP_CNTL_CH1_OUT_RGB8 (0 << 5)
+#define PRP_CNTL_CH1_OUT_RGB16 (1 << 5)
+#define PRP_CNTL_CH1_OUT_RGB32 (2 << 5)
+#define PRP_CNTL_CH1_OUT_YUV422 (3 << 5)
+#define PRP_CNTL_CH2_OUT_YUV420 (0 << 7)
+#define PRP_CNTL_CH2_OUT_YUV422 (1 << 7)
+#define PRP_CNTL_CH2_OUT_YUV444 (2 << 7)
+#define PRP_CNTL_CH1_LEN (1 << 9)
+#define PRP_CNTL_CH2_LEN (1 << 10)
+#define PRP_CNTL_SKIP_FRAME (1 << 11)
+#define PRP_CNTL_SWRST (1 << 12)
+#define PRP_CNTL_CLKEN (1 << 13)
+#define PRP_CNTL_WEN (1 << 14)
+#define PRP_CNTL_CH1BYP (1 << 15)
+#define PRP_CNTL_IN_TSKIP(x) ((x) << 16)
+#define PRP_CNTL_CH1_TSKIP(x) ((x) << 19)
+#define PRP_CNTL_CH2_TSKIP(x) ((x) << 22)
+#define PRP_CNTL_INPUT_FIFO_LEVEL(x) ((x) << 25)
+#define PRP_CNTL_RZ_FIFO_LEVEL(x) ((x) << 27)
+#define PRP_CNTL_CH2B1EN (1 << 29)
+#define PRP_CNTL_CH2B2EN (1 << 30)
+#define PRP_CNTL_CH2FEN (1 << 31)
+
+#define PRP_SIZE_HEIGHT(x) (x)
+#define PRP_SIZE_WIDTH(x) ((x) << 16)
+
+/* IRQ Enable and status register */
+#define PRP_INTR_RDERR (1 << 0)
+#define PRP_INTR_CH1WERR (1 << 1)
+#define PRP_INTR_CH2WERR (1 << 2)
+#define PRP_INTR_CH1FC (1 << 3)
+#define PRP_INTR_CH2FC (1 << 5)
+#define PRP_INTR_LBOVF (1 << 7)
+#define PRP_INTR_CH2OVF (1 << 8)
+
+#define PRP_INTR_ST_RDERR (1 << 0)
+#define PRP_INTR_ST_CH1WERR (1 << 1)
+#define PRP_INTR_ST_CH2WERR (1 << 2)
+#define PRP_INTR_ST_CH2B2CI (1 << 3)
+#define PRP_INTR_ST_CH2B1CI (1 << 4)
+#define PRP_INTR_ST_CH1B2CI (1 << 5)
+#define PRP_INTR_ST_CH1B1CI (1 << 6)
+#define PRP_INTR_ST_LBOVF (1 << 7)
+#define PRP_INTR_ST_CH2OVF (1 << 8)
+
+struct emmaprp_fmt {
+ char *name;
+ u32 fourcc;
+ /* Types the format can be used for */
+ u32 types;
+};
+
+static struct emmaprp_fmt formats[] = {
+ {
+ .name = "YUV 4:2:0 Planar",
+ .fourcc = V4L2_PIX_FMT_YUV420,
+ .types = MEM2MEM_CAPTURE,
+ },
+ {
+ .name = "4:2:2, packed, YUYV",
+ .fourcc = V4L2_PIX_FMT_YUYV,
+ .types = MEM2MEM_OUTPUT,
+ },
+};
+
+/* Per-queue, driver-specific private data */
+struct emmaprp_q_data {
+ unsigned int width;
+ unsigned int height;
+ unsigned int sizeimage;
+ struct emmaprp_fmt *fmt;
+};
+
+enum {
+ V4L2_M2M_SRC = 0,
+ V4L2_M2M_DST = 1,
+};
+
+#define NUM_FORMATS ARRAY_SIZE(formats)
+
+static struct emmaprp_fmt *find_format(struct v4l2_format *f)
+{
+ struct emmaprp_fmt *fmt;
+ unsigned int k;
+
+ for (k = 0; k < NUM_FORMATS; k++) {
+ fmt = &formats[k];
+ if (fmt->fourcc == f->fmt.pix.pixelformat)
+ break;
+ }
+
+ if (k == NUM_FORMATS)
+ return NULL;
+
+ return &formats[k];
+}
+
+struct emmaprp_dev {
+ struct v4l2_device v4l2_dev;
+ struct video_device *vfd;
+
+ struct mutex dev_mutex;
+ spinlock_t irqlock;
+
+ int irq_emma;
+ void __iomem *base_emma;
+ struct clk *clk_emma;
+ struct resource *res_emma;
+
+ struct v4l2_m2m_dev *m2m_dev;
+ struct vb2_alloc_ctx *alloc_ctx;
+};
+
+struct emmaprp_ctx {
+ struct emmaprp_dev *dev;
+ /* Abort requested by m2m */
+ int aborting;
+ struct emmaprp_q_data q_data[2];
+ struct v4l2_m2m_ctx *m2m_ctx;
+};
+
+static struct emmaprp_q_data *get_q_data(struct emmaprp_ctx *ctx,
+ enum v4l2_buf_type type)
+{
+ switch (type) {
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT:
+ return &(ctx->q_data[V4L2_M2M_SRC]);
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ return &(ctx->q_data[V4L2_M2M_DST]);
+ default:
+ BUG();
+ }
+ return NULL;
+}
+
+/*
+ * mem2mem callbacks
+ */
+static void emmaprp_job_abort(void *priv)
+{
+ struct emmaprp_ctx *ctx = priv;
+ struct emmaprp_dev *pcdev = ctx->dev;
+
+ ctx->aborting = 1;
+
+ dprintk(pcdev, "Aborting task\n");
+
+ v4l2_m2m_job_finish(pcdev->m2m_dev, ctx->m2m_ctx);
+}
+
+static void emmaprp_lock(void *priv)
+{
+ struct emmaprp_ctx *ctx = priv;
+ struct emmaprp_dev *pcdev = ctx->dev;
+ mutex_lock(&pcdev->dev_mutex);
+}
+
+static void emmaprp_unlock(void *priv)
+{
+ struct emmaprp_ctx *ctx = priv;
+ struct emmaprp_dev *pcdev = ctx->dev;
+ mutex_unlock(&pcdev->dev_mutex);
+}
+
+static inline void emmaprp_dump_regs(struct emmaprp_dev *pcdev)
+{
+ dprintk(pcdev,
+ "eMMa-PrP Registers:\n"
+ " SOURCE_Y_PTR = 0x%08X\n"
+ " SRC_FRAME_SIZE = 0x%08X\n"
+ " DEST_Y_PTR = 0x%08X\n"
+ " DEST_CR_PTR = 0x%08X\n"
+ " DEST_CB_PTR = 0x%08X\n"
+ " CH2_OUT_IMAGE_SIZE = 0x%08X\n"
+ " CNTL = 0x%08X\n",
+ readl(pcdev->base_emma + PRP_SOURCE_Y_PTR),
+ readl(pcdev->base_emma + PRP_SRC_FRAME_SIZE),
+ readl(pcdev->base_emma + PRP_DEST_Y_PTR),
+ readl(pcdev->base_emma + PRP_DEST_CR_PTR),
+ readl(pcdev->base_emma + PRP_DEST_CB_PTR),
+ readl(pcdev->base_emma + PRP_CH2_OUT_IMAGE_SIZE),
+ readl(pcdev->base_emma + PRP_CNTL));
+}
+
+static void emmaprp_device_run(void *priv)
+{
+ struct emmaprp_ctx *ctx = priv;
+ struct emmaprp_q_data *s_q_data, *d_q_data;
+ struct vb2_buffer *src_buf, *dst_buf;
+ struct emmaprp_dev *pcdev = ctx->dev;
+ unsigned int s_width, s_height;
+ unsigned int d_width, d_height;
+ unsigned int d_size;
+ dma_addr_t p_in, p_out;
+ u32 tmp;
+
+ src_buf = v4l2_m2m_next_src_buf(ctx->m2m_ctx);
+ dst_buf = v4l2_m2m_next_dst_buf(ctx->m2m_ctx);
+
+ s_q_data = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT);
+ s_width = s_q_data->width;
+ s_height = s_q_data->height;
+
+ d_q_data = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE);
+ d_width = d_q_data->width;
+ d_height = d_q_data->height;
+ d_size = d_width * d_height;
+
+ p_in = vb2_dma_contig_plane_dma_addr(src_buf, 0);
+ p_out = vb2_dma_contig_plane_dma_addr(dst_buf, 0);
+ if (!p_in || !p_out) {
+ v4l2_err(&pcdev->v4l2_dev,
+ "Acquiring kernel pointers to buffers failed\n");
+ return;
+ }
+
+ /* Input frame parameters */
+ writel(p_in, pcdev->base_emma + PRP_SOURCE_Y_PTR);
+ writel(PRP_SIZE_WIDTH(s_width) | PRP_SIZE_HEIGHT(s_height),
+ pcdev->base_emma + PRP_SRC_FRAME_SIZE);
+
+ /* Output frame parameters */
+ writel(p_out, pcdev->base_emma + PRP_DEST_Y_PTR);
+ writel(p_out + d_size, pcdev->base_emma + PRP_DEST_CB_PTR);
+ writel(p_out + d_size + (d_size >> 2),
+ pcdev->base_emma + PRP_DEST_CR_PTR);
+ writel(PRP_SIZE_WIDTH(d_width) | PRP_SIZE_HEIGHT(d_height),
+ pcdev->base_emma + PRP_CH2_OUT_IMAGE_SIZE);
+
+ /* IRQ configuration */
+ tmp = readl(pcdev->base_emma + PRP_INTR_CNTL);
+ writel(tmp | PRP_INTR_RDERR |
+ PRP_INTR_CH2WERR |
+ PRP_INTR_CH2FC,
+ pcdev->base_emma + PRP_INTR_CNTL);
+
+ emmaprp_dump_regs(pcdev);
+
+ /* Enable transfer */
+ tmp = readl(pcdev->base_emma + PRP_CNTL);
+ writel(tmp | PRP_CNTL_CH2_OUT_YUV420 |
+ PRP_CNTL_DATA_IN_YUV422 |
+ PRP_CNTL_CH2EN,
+ pcdev->base_emma + PRP_CNTL);
+}
+
+static irqreturn_t emmaprp_irq(int irq_emma, void *data)
+{
+ struct emmaprp_dev *pcdev = data;
+ struct emmaprp_ctx *curr_ctx;
+ struct vb2_buffer *src_vb, *dst_vb;
+ unsigned long flags;
+ u32 irqst;
+
+ /* Check irq flags and clear irq */
+ irqst = readl(pcdev->base_emma + PRP_INTRSTATUS);
+ writel(irqst, pcdev->base_emma + PRP_INTRSTATUS);
+ dprintk(pcdev, "irqst = 0x%08x\n", irqst);
+
+ curr_ctx = v4l2_m2m_get_curr_priv(pcdev->m2m_dev);
+ if (curr_ctx == NULL) {
+ pr_err("Instance released before the end of transaction\n");
+ return IRQ_HANDLED;
+ }
+
+ if (!curr_ctx->aborting) {
+ if ((irqst & PRP_INTR_ST_RDERR) ||
+ (irqst & PRP_INTR_ST_CH2WERR)) {
+ pr_err("PrP bus error ocurred, this transfer is probably corrupted\n");
+ writel(PRP_CNTL_SWRST, pcdev->base_emma + PRP_CNTL);
+ } else if (irqst & PRP_INTR_ST_CH2B1CI) { /* buffer ready */
+ src_vb = v4l2_m2m_src_buf_remove(curr_ctx->m2m_ctx);
+ dst_vb = v4l2_m2m_dst_buf_remove(curr_ctx->m2m_ctx);
+
+ spin_lock_irqsave(&pcdev->irqlock, flags);
+ v4l2_m2m_buf_done(src_vb, VB2_BUF_STATE_DONE);
+ v4l2_m2m_buf_done(dst_vb, VB2_BUF_STATE_DONE);
+ spin_unlock_irqrestore(&pcdev->irqlock, flags);
+ }
+ }
+
+ v4l2_m2m_job_finish(pcdev->m2m_dev, curr_ctx->m2m_ctx);
+ return IRQ_HANDLED;
+}
+
+/*
+ * video ioctls
+ */
+static int vidioc_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ strncpy(cap->driver, MEM2MEM_NAME, sizeof(cap->driver) - 1);
+ strncpy(cap->card, MEM2MEM_NAME, sizeof(cap->card) - 1);
+ cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OUTPUT
+ | V4L2_CAP_STREAMING;
+
+ return 0;
+}
+
+static int enum_fmt(struct v4l2_fmtdesc *f, u32 type)
+{
+ int i, num;
+ struct emmaprp_fmt *fmt;
+
+ num = 0;
+
+ for (i = 0; i < NUM_FORMATS; ++i) {
+ if (formats[i].types & type) {
+ /* index-th format of type type found ? */
+ if (num == f->index)
+ break;
+ /* Correct type but haven't reached our index yet,
+ * just increment per-type index */
+ ++num;
+ }
+ }
+
+ if (i < NUM_FORMATS) {
+ /* Format found */
+ fmt = &formats[i];
+ strlcpy(f->description, fmt->name, sizeof(f->description) - 1);
+ f->pixelformat = fmt->fourcc;
+ return 0;
+ }
+
+ /* Format not found */
+ return -EINVAL;
+}
+
+static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ return enum_fmt(f, MEM2MEM_CAPTURE);
+}
+
+static int vidioc_enum_fmt_vid_out(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ return enum_fmt(f, MEM2MEM_OUTPUT);
+}
+
+static int vidioc_g_fmt(struct emmaprp_ctx *ctx, struct v4l2_format *f)
+{
+ struct vb2_queue *vq;
+ struct emmaprp_q_data *q_data;
+
+ vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type);
+ if (!vq)
+ return -EINVAL;
+
+ q_data = get_q_data(ctx, f->type);
+
+ f->fmt.pix.width = q_data->width;
+ f->fmt.pix.height = q_data->height;
+ f->fmt.pix.field = V4L2_FIELD_NONE;
+ f->fmt.pix.pixelformat = q_data->fmt->fourcc;
+ if (f->fmt.pix.pixelformat == V4L2_PIX_FMT_YUV420)
+ f->fmt.pix.bytesperline = q_data->width * 3 / 2;
+ else /* YUYV */
+ f->fmt.pix.bytesperline = q_data->width * 2;
+ f->fmt.pix.sizeimage = q_data->sizeimage;
+
+ return 0;
+}
+
+static int vidioc_g_fmt_vid_out(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ return vidioc_g_fmt(priv, f);
+}
+
+static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ return vidioc_g_fmt(priv, f);
+}
+
+static int vidioc_try_fmt(struct v4l2_format *f)
+{
+ enum v4l2_field field;
+
+
+ if (!find_format(f))
+ return -EINVAL;
+
+ field = f->fmt.pix.field;
+ if (field == V4L2_FIELD_ANY)
+ field = V4L2_FIELD_NONE;
+ else if (V4L2_FIELD_NONE != field)
+ return -EINVAL;
+
+ /* V4L2 specification suggests the driver corrects the format struct
+ * if any of the dimensions is unsupported */
+ f->fmt.pix.field = field;
+
+ if (f->fmt.pix.pixelformat == V4L2_PIX_FMT_YUV420) {
+ v4l_bound_align_image(&f->fmt.pix.width, MIN_W, MAX_W,
+ W_ALIGN_YUV420, &f->fmt.pix.height,
+ MIN_H, MAX_H, H_ALIGN, S_ALIGN);
+ f->fmt.pix.bytesperline = f->fmt.pix.width * 3 / 2;
+ } else {
+ v4l_bound_align_image(&f->fmt.pix.width, MIN_W, MAX_W,
+ W_ALIGN_OTHERS, &f->fmt.pix.height,
+ MIN_H, MAX_H, H_ALIGN, S_ALIGN);
+ f->fmt.pix.bytesperline = f->fmt.pix.width * 2;
+ }
+ f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline;
+
+ return 0;
+}
+
+static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct emmaprp_fmt *fmt;
+ struct emmaprp_ctx *ctx = priv;
+
+ fmt = find_format(f);
+ if (!fmt || !(fmt->types & MEM2MEM_CAPTURE)) {
+ v4l2_err(&ctx->dev->v4l2_dev,
+ "Fourcc format (0x%08x) invalid.\n",
+ f->fmt.pix.pixelformat);
+ return -EINVAL;
+ }
+
+ return vidioc_try_fmt(f);
+}
+
+static int vidioc_try_fmt_vid_out(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct emmaprp_fmt *fmt;
+ struct emmaprp_ctx *ctx = priv;
+
+ fmt = find_format(f);
+ if (!fmt || !(fmt->types & MEM2MEM_OUTPUT)) {
+ v4l2_err(&ctx->dev->v4l2_dev,
+ "Fourcc format (0x%08x) invalid.\n",
+ f->fmt.pix.pixelformat);
+ return -EINVAL;
+ }
+
+ return vidioc_try_fmt(f);
+}
+
+static int vidioc_s_fmt(struct emmaprp_ctx *ctx, struct v4l2_format *f)
+{
+ struct emmaprp_q_data *q_data;
+ struct vb2_queue *vq;
+ int ret;
+
+ vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type);
+ if (!vq)
+ return -EINVAL;
+
+ q_data = get_q_data(ctx, f->type);
+ if (!q_data)
+ return -EINVAL;
+
+ if (vb2_is_busy(vq)) {
+ v4l2_err(&ctx->dev->v4l2_dev, "%s queue busy\n", __func__);
+ return -EBUSY;
+ }
+
+ ret = vidioc_try_fmt(f);
+ if (ret)
+ return ret;
+
+ q_data->fmt = find_format(f);
+ q_data->width = f->fmt.pix.width;
+ q_data->height = f->fmt.pix.height;
+ if (q_data->fmt->fourcc == V4L2_PIX_FMT_YUV420)
+ q_data->sizeimage = q_data->width * q_data->height * 3 / 2;
+ else /* YUYV */
+ q_data->sizeimage = q_data->width * q_data->height * 2;
+
+ dprintk(ctx->dev,
+ "Setting format for type %d, wxh: %dx%d, fmt: %d\n",
+ f->type, q_data->width, q_data->height, q_data->fmt->fourcc);
+
+ return 0;
+}
+
+static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ int ret;
+
+ ret = vidioc_try_fmt_vid_cap(file, priv, f);
+ if (ret)
+ return ret;
+
+ return vidioc_s_fmt(priv, f);
+}
+
+static int vidioc_s_fmt_vid_out(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ int ret;
+
+ ret = vidioc_try_fmt_vid_out(file, priv, f);
+ if (ret)
+ return ret;
+
+ return vidioc_s_fmt(priv, f);
+}
+
+static int vidioc_reqbufs(struct file *file, void *priv,
+ struct v4l2_requestbuffers *reqbufs)
+{
+ struct emmaprp_ctx *ctx = priv;
+
+ return v4l2_m2m_reqbufs(file, ctx->m2m_ctx, reqbufs);
+}
+
+static int vidioc_querybuf(struct file *file, void *priv,
+ struct v4l2_buffer *buf)
+{
+ struct emmaprp_ctx *ctx = priv;
+
+ return v4l2_m2m_querybuf(file, ctx->m2m_ctx, buf);
+}
+
+static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
+{
+ struct emmaprp_ctx *ctx = priv;
+
+ return v4l2_m2m_qbuf(file, ctx->m2m_ctx, buf);
+}
+
+static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
+{
+ struct emmaprp_ctx *ctx = priv;
+
+ return v4l2_m2m_dqbuf(file, ctx->m2m_ctx, buf);
+}
+
+static int vidioc_streamon(struct file *file, void *priv,
+ enum v4l2_buf_type type)
+{
+ struct emmaprp_ctx *ctx = priv;
+
+ return v4l2_m2m_streamon(file, ctx->m2m_ctx, type);
+}
+
+static int vidioc_streamoff(struct file *file, void *priv,
+ enum v4l2_buf_type type)
+{
+ struct emmaprp_ctx *ctx = priv;
+
+ return v4l2_m2m_streamoff(file, ctx->m2m_ctx, type);
+}
+
+static const struct v4l2_ioctl_ops emmaprp_ioctl_ops = {
+ .vidioc_querycap = vidioc_querycap,
+
+ .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
+ .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap,
+ .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap,
+ .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,
+
+ .vidioc_enum_fmt_vid_out = vidioc_enum_fmt_vid_out,
+ .vidioc_g_fmt_vid_out = vidioc_g_fmt_vid_out,
+ .vidioc_try_fmt_vid_out = vidioc_try_fmt_vid_out,
+ .vidioc_s_fmt_vid_out = vidioc_s_fmt_vid_out,
+
+ .vidioc_reqbufs = vidioc_reqbufs,
+ .vidioc_querybuf = vidioc_querybuf,
+
+ .vidioc_qbuf = vidioc_qbuf,
+ .vidioc_dqbuf = vidioc_dqbuf,
+
+ .vidioc_streamon = vidioc_streamon,
+ .vidioc_streamoff = vidioc_streamoff,
+};
+
+
+/*
+ * Queue operations
+ */
+static int emmaprp_queue_setup(struct vb2_queue *vq,
+ const struct v4l2_format *fmt,
+ unsigned int *nbuffers, unsigned int *nplanes,
+ unsigned int sizes[], void *alloc_ctxs[])
+{
+ struct emmaprp_ctx *ctx = vb2_get_drv_priv(vq);
+ struct emmaprp_q_data *q_data;
+ unsigned int size, count = *nbuffers;
+
+ q_data = get_q_data(ctx, vq->type);
+
+ if (q_data->fmt->fourcc == V4L2_PIX_FMT_YUV420)
+ size = q_data->width * q_data->height * 3 / 2;
+ else
+ size = q_data->width * q_data->height * 2;
+
+ while (size * count > MEM2MEM_VID_MEM_LIMIT)
+ (count)--;
+
+ *nplanes = 1;
+ *nbuffers = count;
+ sizes[0] = size;
+
+ alloc_ctxs[0] = ctx->dev->alloc_ctx;
+
+ dprintk(ctx->dev, "get %d buffer(s) of size %d each.\n", count, size);
+
+ return 0;
+}
+
+static int emmaprp_buf_prepare(struct vb2_buffer *vb)
+{
+ struct emmaprp_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
+ struct emmaprp_q_data *q_data;
+
+ dprintk(ctx->dev, "type: %d\n", vb->vb2_queue->type);
+
+ q_data = get_q_data(ctx, vb->vb2_queue->type);
+
+ if (vb2_plane_size(vb, 0) < q_data->sizeimage) {
+ dprintk(ctx->dev, "%s data will not fit into plane"
+ "(%lu < %lu)\n", __func__,
+ vb2_plane_size(vb, 0),
+ (long)q_data->sizeimage);
+ return -EINVAL;
+ }
+
+ vb2_set_plane_payload(vb, 0, q_data->sizeimage);
+
+ return 0;
+}
+
+static void emmaprp_buf_queue(struct vb2_buffer *vb)
+{
+ struct emmaprp_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
+ v4l2_m2m_buf_queue(ctx->m2m_ctx, vb);
+}
+
+static struct vb2_ops emmaprp_qops = {
+ .queue_setup = emmaprp_queue_setup,
+ .buf_prepare = emmaprp_buf_prepare,
+ .buf_queue = emmaprp_buf_queue,
+};
+
+static int queue_init(void *priv, struct vb2_queue *src_vq,
+ struct vb2_queue *dst_vq)
+{
+ struct emmaprp_ctx *ctx = priv;
+ int ret;
+
+ memset(src_vq, 0, sizeof(*src_vq));
+ src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
+ src_vq->io_modes = VB2_MMAP;
+ src_vq->drv_priv = ctx;
+ src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
+ src_vq->ops = &emmaprp_qops;
+ src_vq->mem_ops = &vb2_dma_contig_memops;
+
+ ret = vb2_queue_init(src_vq);
+ if (ret)
+ return ret;
+
+ memset(dst_vq, 0, sizeof(*dst_vq));
+ dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ dst_vq->io_modes = VB2_MMAP;
+ dst_vq->drv_priv = ctx;
+ dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
+ dst_vq->ops = &emmaprp_qops;
+ dst_vq->mem_ops = &vb2_dma_contig_memops;
+
+ return vb2_queue_init(dst_vq);
+}
+
+/*
+ * File operations
+ */
+static int emmaprp_open(struct file *file)
+{
+ struct emmaprp_dev *pcdev = video_drvdata(file);
+ struct emmaprp_ctx *ctx;
+
+ ctx = kzalloc(sizeof *ctx, GFP_KERNEL);
+ if (!ctx)
+ return -ENOMEM;
+
+ file->private_data = ctx;
+ ctx->dev = pcdev;
+
+ ctx->m2m_ctx = v4l2_m2m_ctx_init(pcdev->m2m_dev, ctx, &queue_init);
+
+ if (IS_ERR(ctx->m2m_ctx)) {
+ int ret = PTR_ERR(ctx->m2m_ctx);
+
+ kfree(ctx);
+ return ret;
+ }
+
+ clk_enable(pcdev->clk_emma);
+ ctx->q_data[V4L2_M2M_SRC].fmt = &formats[1];
+ ctx->q_data[V4L2_M2M_DST].fmt = &formats[0];
+
+ dprintk(pcdev, "Created instance %p, m2m_ctx: %p\n", ctx, ctx->m2m_ctx);
+
+ return 0;
+}
+
+static int emmaprp_release(struct file *file)
+{
+ struct emmaprp_dev *pcdev = video_drvdata(file);
+ struct emmaprp_ctx *ctx = file->private_data;
+
+ dprintk(pcdev, "Releasing instance %p\n", ctx);
+
+ clk_disable(pcdev->clk_emma);
+ v4l2_m2m_ctx_release(ctx->m2m_ctx);
+ kfree(ctx);
+
+ return 0;
+}
+
+static unsigned int emmaprp_poll(struct file *file,
+ struct poll_table_struct *wait)
+{
+ struct emmaprp_ctx *ctx = file->private_data;
+
+ return v4l2_m2m_poll(file, ctx->m2m_ctx, wait);
+}
+
+static int emmaprp_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ struct emmaprp_ctx *ctx = file->private_data;
+
+ return v4l2_m2m_mmap(file, ctx->m2m_ctx, vma);
+}
+
+static const struct v4l2_file_operations emmaprp_fops = {
+ .owner = THIS_MODULE,
+ .open = emmaprp_open,
+ .release = emmaprp_release,
+ .poll = emmaprp_poll,
+ .unlocked_ioctl = video_ioctl2,
+ .mmap = emmaprp_mmap,
+};
+
+static struct video_device emmaprp_videodev = {
+ .name = MEM2MEM_NAME,
+ .fops = &emmaprp_fops,
+ .ioctl_ops = &emmaprp_ioctl_ops,
+ .minor = -1,
+ .release = video_device_release,
+};
+
+static struct v4l2_m2m_ops m2m_ops = {
+ .device_run = emmaprp_device_run,
+ .job_abort = emmaprp_job_abort,
+ .lock = emmaprp_lock,
+ .unlock = emmaprp_unlock,
+};
+
+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;
+
+ pcdev = kzalloc(sizeof *pcdev, GFP_KERNEL);
+ if (!pcdev)
+ return -ENOMEM;
+
+ spin_lock_init(&pcdev->irqlock);
+
+ pcdev->clk_emma = clk_get(&pdev->dev, NULL);
+ if (IS_ERR(pcdev->clk_emma)) {
+ ret = PTR_ERR(pcdev->clk_emma);
+ goto free_dev;
+ }
+
+ 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");
+ ret = -ENODEV;
+ goto free_clk;
+ }
+
+ ret = v4l2_device_register(&pdev->dev, &pcdev->v4l2_dev);
+ if (ret)
+ goto free_clk;
+
+ mutex_init(&pcdev->dev_mutex);
+
+ vfd = video_device_alloc();
+ if (!vfd) {
+ v4l2_err(&pcdev->v4l2_dev, "Failed to allocate video device\n");
+ ret = -ENOMEM;
+ goto unreg_dev;
+ }
+
+ *vfd = emmaprp_videodev;
+ vfd->lock = &pcdev->dev_mutex;
+
+ video_set_drvdata(vfd, pcdev);
+ snprintf(vfd->name, sizeof(vfd->name), "%s", emmaprp_videodev.name);
+ pcdev->vfd = vfd;
+ v4l2_info(&pcdev->v4l2_dev, EMMAPRP_MODULE_NAME
+ " Device registered as /dev/video%d\n", vfd->num);
+
+ platform_set_drvdata(pdev, pcdev);
+
+ if (devm_request_mem_region(&pdev->dev, res_emma->start,
+ resource_size(res_emma), MEM2MEM_NAME) == NULL)
+ goto rel_vdev;
+
+ pcdev->base_emma = devm_ioremap(&pdev->dev, res_emma->start,
+ resource_size(res_emma));
+ if (!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)
+ goto rel_vdev;
+
+ pcdev->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev);
+ if (IS_ERR(pcdev->alloc_ctx)) {
+ v4l2_err(&pcdev->v4l2_dev, "Failed to alloc vb2 context\n");
+ ret = PTR_ERR(pcdev->alloc_ctx);
+ goto rel_vdev;
+ }
+
+ pcdev->m2m_dev = v4l2_m2m_init(&m2m_ops);
+ if (IS_ERR(pcdev->m2m_dev)) {
+ v4l2_err(&pcdev->v4l2_dev, "Failed to init mem2mem device\n");
+ ret = PTR_ERR(pcdev->m2m_dev);
+ goto rel_ctx;
+ }
+
+ ret = video_register_device(vfd, VFL_TYPE_GRABBER, 0);
+ if (ret) {
+ v4l2_err(&pcdev->v4l2_dev, "Failed to register video device\n");
+ goto rel_m2m;
+ }
+
+ return 0;
+
+
+rel_m2m:
+ v4l2_m2m_release(pcdev->m2m_dev);
+rel_ctx:
+ vb2_dma_contig_cleanup_ctx(pcdev->alloc_ctx);
+rel_vdev:
+ video_device_release(vfd);
+unreg_dev:
+ v4l2_device_unregister(&pcdev->v4l2_dev);
+free_clk:
+ clk_put(pcdev->clk_emma);
+free_dev:
+ kfree(pcdev);
+
+ return ret;
+}
+
+static int emmaprp_remove(struct platform_device *pdev)
+{
+ struct emmaprp_dev *pcdev = platform_get_drvdata(pdev);
+
+ v4l2_info(&pcdev->v4l2_dev, "Removing " EMMAPRP_MODULE_NAME);
+
+ video_unregister_device(pcdev->vfd);
+ v4l2_m2m_release(pcdev->m2m_dev);
+ vb2_dma_contig_cleanup_ctx(pcdev->alloc_ctx);
+ v4l2_device_unregister(&pcdev->v4l2_dev);
+ clk_put(pcdev->clk_emma);
+ kfree(pcdev);
+
+ return 0;
+}
+
+static struct platform_driver emmaprp_pdrv = {
+ .probe = emmaprp_probe,
+ .remove = emmaprp_remove,
+ .driver = {
+ .name = MEM2MEM_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+static void __exit emmaprp_exit(void)
+{
+ platform_driver_unregister(&emmaprp_pdrv);
+}
+
+static int __init emmaprp_init(void)
+{
+ return platform_driver_register(&emmaprp_pdrv);
+}
+
+module_init(emmaprp_init);
+module_exit(emmaprp_exit);
diff --git a/drivers/media/video/s5p-tv/Kconfig b/drivers/media/video/s5p-tv/Kconfig
index f2a09779ec8f..f248b2856720 100644
--- a/drivers/media/video/s5p-tv/Kconfig
+++ b/drivers/media/video/s5p-tv/Kconfig
@@ -46,6 +46,16 @@ config VIDEO_SAMSUNG_S5P_HDMIPHY
as module. It is an I2C driver, that exposes a V4L2
subdev for use by other drivers.
+config VIDEO_SAMSUNG_S5P_SII9234
+ tristate "Samsung SII9234 Driver"
+ depends on VIDEO_DEV && VIDEO_V4L2 && I2C
+ depends on VIDEO_SAMSUNG_S5P_TV
+ help
+ Say Y here if you want support for the MHL interface
+ in S5P Samsung SoC. The driver can be compiled
+ as module. It is an I2C driver, that exposes a V4L2
+ subdev for use by other drivers.
+
config VIDEO_SAMSUNG_S5P_SDO
tristate "Samsung Analog TV Driver"
depends on VIDEO_DEV && VIDEO_V4L2
diff --git a/drivers/media/video/s5p-tv/Makefile b/drivers/media/video/s5p-tv/Makefile
index 37e4c17663b4..f49e756a2fde 100644
--- a/drivers/media/video/s5p-tv/Makefile
+++ b/drivers/media/video/s5p-tv/Makefile
@@ -8,6 +8,8 @@
obj-$(CONFIG_VIDEO_SAMSUNG_S5P_HDMIPHY) += s5p-hdmiphy.o
s5p-hdmiphy-y += hdmiphy_drv.o
+obj-$(CONFIG_VIDEO_SAMSUNG_S5P_SII9234) += s5p-sii9234.o
+s5p-sii9234-y += sii9234_drv.o
obj-$(CONFIG_VIDEO_SAMSUNG_S5P_HDMI) += s5p-hdmi.o
s5p-hdmi-y += hdmi_drv.o
obj-$(CONFIG_VIDEO_SAMSUNG_S5P_SDO) += s5p-sdo.o
diff --git a/drivers/media/video/s5p-tv/hdmi_drv.c b/drivers/media/video/s5p-tv/hdmi_drv.c
index 3e0dd099b2e0..4865d25a0e57 100644
--- a/drivers/media/video/s5p-tv/hdmi_drv.c
+++ b/drivers/media/video/s5p-tv/hdmi_drv.c
@@ -30,6 +30,7 @@
#include <linux/clk.h>
#include <linux/regulator/consumer.h>
+#include <media/s5p_hdmi.h>
#include <media/v4l2-common.h>
#include <media/v4l2-dev.h>
#include <media/v4l2-device.h>
@@ -66,6 +67,8 @@ struct hdmi_device {
struct v4l2_device v4l2_dev;
/** subdev of HDMIPHY interface */
struct v4l2_subdev *phy_sd;
+ /** subdev of MHL interface */
+ struct v4l2_subdev *mhl_sd;
/** configuration of current graphic mode */
const struct hdmi_preset_conf *cur_conf;
/** current preset */
@@ -74,10 +77,6 @@ struct hdmi_device {
struct hdmi_resources res;
};
-struct hdmi_driver_data {
- int hdmiphy_bus;
-};
-
struct hdmi_tg_regs {
u8 cmd;
u8 h_fsz_l;
@@ -129,23 +128,11 @@ struct hdmi_preset_conf {
struct v4l2_mbus_framefmt mbus_fmt;
};
-/* I2C module and id for HDMIPHY */
-static struct i2c_board_info hdmiphy_info = {
- I2C_BOARD_INFO("hdmiphy", 0x38),
-};
-
-static struct hdmi_driver_data hdmi_driver_data[] = {
- { .hdmiphy_bus = 3 },
- { .hdmiphy_bus = 8 },
-};
-
static struct platform_device_id hdmi_driver_types[] = {
{
.name = "s5pv210-hdmi",
- .driver_data = (unsigned long)&hdmi_driver_data[0],
}, {
.name = "exynos4-hdmi",
- .driver_data = (unsigned long)&hdmi_driver_data[1],
}, {
/* end node */
}
@@ -587,7 +574,15 @@ static int hdmi_streamon(struct hdmi_device *hdev)
if (tries == 0) {
dev_err(dev, "hdmiphy's pll could not reach steady state.\n");
v4l2_subdev_call(hdev->phy_sd, video, s_stream, 0);
- hdmi_dumpregs(hdev, "s_stream");
+ hdmi_dumpregs(hdev, "hdmiphy - s_stream");
+ return -EIO;
+ }
+
+ /* starting MHL */
+ ret = v4l2_subdev_call(hdev->mhl_sd, video, s_stream, 1);
+ if (hdev->mhl_sd && ret) {
+ v4l2_subdev_call(hdev->phy_sd, video, s_stream, 0);
+ hdmi_dumpregs(hdev, "mhl - s_stream");
return -EIO;
}
@@ -618,6 +613,7 @@ static int hdmi_streamoff(struct hdmi_device *hdev)
clk_set_parent(res->sclk_hdmi, res->sclk_pixel);
clk_enable(res->sclk_hdmi);
+ v4l2_subdev_call(hdev->mhl_sd, video, s_stream, 0);
v4l2_subdev_call(hdev->phy_sd, video, s_stream, 0);
hdmi_dumpregs(hdev, "streamoff");
@@ -739,6 +735,7 @@ static int hdmi_runtime_suspend(struct device *dev)
struct hdmi_device *hdev = sd_to_hdmi_dev(sd);
dev_dbg(dev, "%s\n", __func__);
+ v4l2_subdev_call(hdev->mhl_sd, core, s_power, 0);
hdmi_resource_poweroff(&hdev->res);
return 0;
}
@@ -757,6 +754,11 @@ static int hdmi_runtime_resume(struct device *dev)
if (ret)
goto fail;
+ /* starting MHL */
+ ret = v4l2_subdev_call(hdev->mhl_sd, core, s_power, 1);
+ if (hdev->mhl_sd && ret)
+ goto fail;
+
dev_dbg(dev, "poweron succeed\n");
return 0;
@@ -867,15 +869,21 @@ static int __devinit hdmi_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct resource *res;
- struct i2c_adapter *phy_adapter;
+ struct i2c_adapter *adapter;
struct v4l2_subdev *sd;
struct hdmi_device *hdmi_dev = NULL;
- struct hdmi_driver_data *drv_data;
+ struct s5p_hdmi_platform_data *pdata = dev->platform_data;
int ret;
dev_dbg(dev, "probe start\n");
- hdmi_dev = kzalloc(sizeof(*hdmi_dev), GFP_KERNEL);
+ if (!pdata) {
+ dev_err(dev, "platform data is missing\n");
+ ret = -ENODEV;
+ goto fail;
+ }
+
+ hdmi_dev = devm_kzalloc(&pdev->dev, sizeof(*hdmi_dev), GFP_KERNEL);
if (!hdmi_dev) {
dev_err(dev, "out of memory\n");
ret = -ENOMEM;
@@ -886,7 +894,7 @@ static int __devinit hdmi_probe(struct platform_device *pdev)
ret = hdmi_resources_init(hdmi_dev);
if (ret)
- goto fail_hdev;
+ goto fail;
/* mapping HDMI registers */
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
@@ -896,24 +904,26 @@ static int __devinit hdmi_probe(struct platform_device *pdev)
goto fail_init;
}
- hdmi_dev->regs = ioremap(res->start, resource_size(res));
+ hdmi_dev->regs = devm_ioremap(&pdev->dev, res->start,
+ resource_size(res));
if (hdmi_dev->regs == NULL) {
dev_err(dev, "register mapping failed.\n");
ret = -ENXIO;
- goto fail_hdev;
+ goto fail_init;
}
res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
if (res == NULL) {
dev_err(dev, "get interrupt resource failed.\n");
ret = -ENXIO;
- goto fail_regs;
+ goto fail_init;
}
- ret = request_irq(res->start, hdmi_irq_handler, 0, "hdmi", hdmi_dev);
+ ret = devm_request_irq(&pdev->dev, res->start, hdmi_irq_handler, 0,
+ "hdmi", hdmi_dev);
if (ret) {
dev_err(dev, "request interrupt failed.\n");
- goto fail_regs;
+ goto fail_init;
}
hdmi_dev->irq = res->start;
@@ -924,28 +934,54 @@ static int __devinit hdmi_probe(struct platform_device *pdev)
ret = v4l2_device_register(NULL, &hdmi_dev->v4l2_dev);
if (ret) {
dev_err(dev, "could not register v4l2 device.\n");
- goto fail_irq;
+ goto fail_init;
}
- drv_data = (struct hdmi_driver_data *)
- platform_get_device_id(pdev)->driver_data;
- phy_adapter = i2c_get_adapter(drv_data->hdmiphy_bus);
- if (phy_adapter == NULL) {
- dev_err(dev, "adapter request failed\n");
+ /* testing if hdmiphy info is present */
+ if (!pdata->hdmiphy_info) {
+ dev_err(dev, "hdmiphy info is missing in platform data\n");
+ ret = -ENXIO;
+ goto fail_vdev;
+ }
+
+ adapter = i2c_get_adapter(pdata->hdmiphy_bus);
+ if (adapter == NULL) {
+ dev_err(dev, "hdmiphy adapter request failed\n");
ret = -ENXIO;
goto fail_vdev;
}
hdmi_dev->phy_sd = v4l2_i2c_new_subdev_board(&hdmi_dev->v4l2_dev,
- phy_adapter, &hdmiphy_info, NULL);
+ adapter, pdata->hdmiphy_info, NULL);
/* on failure or not adapter is no longer useful */
- i2c_put_adapter(phy_adapter);
+ i2c_put_adapter(adapter);
if (hdmi_dev->phy_sd == NULL) {
dev_err(dev, "missing subdev for hdmiphy\n");
ret = -ENODEV;
goto fail_vdev;
}
+ /* initialization of MHL interface if present */
+ if (pdata->mhl_info) {
+ adapter = i2c_get_adapter(pdata->mhl_bus);
+ if (adapter == NULL) {
+ dev_err(dev, "MHL adapter request failed\n");
+ ret = -ENXIO;
+ goto fail_vdev;
+ }
+
+ hdmi_dev->mhl_sd = v4l2_i2c_new_subdev_board(
+ &hdmi_dev->v4l2_dev, adapter,
+ pdata->mhl_info, NULL);
+ /* on failure or not adapter is no longer useful */
+ i2c_put_adapter(adapter);
+ if (hdmi_dev->mhl_sd == NULL) {
+ dev_err(dev, "missing subdev for MHL\n");
+ ret = -ENODEV;
+ goto fail_vdev;
+ }
+ }
+
clk_enable(hdmi_dev->res.hdmi);
pm_runtime_enable(dev);
@@ -969,18 +1005,9 @@ static int __devinit hdmi_probe(struct platform_device *pdev)
fail_vdev:
v4l2_device_unregister(&hdmi_dev->v4l2_dev);
-fail_irq:
- free_irq(hdmi_dev->irq, hdmi_dev);
-
-fail_regs:
- iounmap(hdmi_dev->regs);
-
fail_init:
hdmi_resources_cleanup(hdmi_dev);
-fail_hdev:
- kfree(hdmi_dev);
-
fail:
dev_err(dev, "probe failed\n");
return ret;
@@ -996,10 +1023,7 @@ static int __devexit hdmi_remove(struct platform_device *pdev)
clk_disable(hdmi_dev->res.hdmi);
v4l2_device_unregister(&hdmi_dev->v4l2_dev);
disable_irq(hdmi_dev->irq);
- free_irq(hdmi_dev->irq, hdmi_dev);
- iounmap(hdmi_dev->regs);
hdmi_resources_cleanup(hdmi_dev);
- kfree(hdmi_dev);
dev_info(dev, "remove successful\n");
return 0;
diff --git a/drivers/media/video/s5p-tv/sdo_drv.c b/drivers/media/video/s5p-tv/sdo_drv.c
index 059e7749ce95..f6bca2c20e89 100644
--- a/drivers/media/video/s5p-tv/sdo_drv.c
+++ b/drivers/media/video/s5p-tv/sdo_drv.c
@@ -301,7 +301,7 @@ static int __devinit sdo_probe(struct platform_device *pdev)
struct clk *sclk_vpll;
dev_info(dev, "probe start\n");
- sdev = kzalloc(sizeof *sdev, GFP_KERNEL);
+ sdev = devm_kzalloc(&pdev->dev, sizeof *sdev, GFP_KERNEL);
if (!sdev) {
dev_err(dev, "not enough memory.\n");
ret = -ENOMEM;
@@ -314,14 +314,14 @@ static int __devinit sdo_probe(struct platform_device *pdev)
if (res == NULL) {
dev_err(dev, "get memory resource failed.\n");
ret = -ENXIO;
- goto fail_sdev;
+ goto fail;
}
- sdev->regs = ioremap(res->start, resource_size(res));
+ sdev->regs = devm_ioremap(&pdev->dev, res->start, resource_size(res));
if (sdev->regs == NULL) {
dev_err(dev, "register mapping failed.\n");
ret = -ENXIO;
- goto fail_sdev;
+ goto fail;
}
/* acquiring interrupt */
@@ -329,12 +329,13 @@ static int __devinit sdo_probe(struct platform_device *pdev)
if (res == NULL) {
dev_err(dev, "get interrupt resource failed.\n");
ret = -ENXIO;
- goto fail_regs;
+ goto fail;
}
- ret = request_irq(res->start, sdo_irq_handler, 0, "s5p-sdo", sdev);
+ ret = devm_request_irq(&pdev->dev, res->start, sdo_irq_handler, 0,
+ "s5p-sdo", sdev);
if (ret) {
dev_err(dev, "request interrupt failed.\n");
- goto fail_regs;
+ goto fail;
}
sdev->irq = res->start;
@@ -343,7 +344,7 @@ static int __devinit sdo_probe(struct platform_device *pdev)
if (IS_ERR_OR_NULL(sdev->sclk_dac)) {
dev_err(dev, "failed to get clock 'sclk_dac'\n");
ret = -ENXIO;
- goto fail_irq;
+ goto fail;
}
sdev->dac = clk_get(dev, "dac");
if (IS_ERR_OR_NULL(sdev->dac)) {
@@ -415,12 +416,6 @@ fail_dac:
clk_put(sdev->dac);
fail_sclk_dac:
clk_put(sdev->sclk_dac);
-fail_irq:
- free_irq(sdev->irq, sdev);
-fail_regs:
- iounmap(sdev->regs);
-fail_sdev:
- kfree(sdev);
fail:
dev_info(dev, "probe failed\n");
return ret;
@@ -439,9 +434,6 @@ static int __devexit sdo_remove(struct platform_device *pdev)
clk_put(sdev->dacphy);
clk_put(sdev->dac);
clk_put(sdev->sclk_dac);
- free_irq(sdev->irq, sdev);
- iounmap(sdev->regs);
- kfree(sdev);
dev_info(&pdev->dev, "remove successful\n");
return 0;
diff --git a/drivers/media/video/s5p-tv/sii9234_drv.c b/drivers/media/video/s5p-tv/sii9234_drv.c
new file mode 100644
index 000000000000..0f31eccd7b80
--- /dev/null
+++ b/drivers/media/video/s5p-tv/sii9234_drv.c
@@ -0,0 +1,432 @@
+/*
+ * Samsung MHL interface driver
+ *
+ * Copyright (C) 2011 Samsung Electronics Co.Ltd
+ * Author: Tomasz Stanislawski <t.stanislaws@samsung.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/delay.h>
+#include <linux/err.h>
+#include <linux/freezer.h>
+#include <linux/gpio.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/kthread.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/regulator/machine.h>
+#include <linux/slab.h>
+
+#include <mach/gpio.h>
+#include <plat/gpio-cfg.h>
+
+#include <media/sii9234.h>
+#include <media/v4l2-subdev.h>
+
+MODULE_AUTHOR("Tomasz Stanislawski <t.stanislaws@samsung.com>");
+MODULE_DESCRIPTION("Samsung MHL interface driver");
+MODULE_LICENSE("GPL");
+
+struct sii9234_context {
+ struct i2c_client *client;
+ struct regulator *power;
+ int gpio_n_reset;
+ struct v4l2_subdev sd;
+};
+
+static inline struct sii9234_context *sd_to_context(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct sii9234_context, sd);
+}
+
+static inline int sii9234_readb(struct i2c_client *client, int addr)
+{
+ return i2c_smbus_read_byte_data(client, addr);
+}
+
+static inline int sii9234_writeb(struct i2c_client *client, int addr, int value)
+{
+ return i2c_smbus_write_byte_data(client, addr, value);
+}
+
+static inline int sii9234_writeb_mask(struct i2c_client *client, int addr,
+ int value, int mask)
+{
+ int ret;
+
+ ret = i2c_smbus_read_byte_data(client, addr);
+ if (ret < 0)
+ return ret;
+ ret = (ret & ~mask) | (value & mask);
+ return i2c_smbus_write_byte_data(client, addr, ret);
+}
+
+static inline int sii9234_readb_idx(struct i2c_client *client, int addr)
+{
+ int ret;
+ ret = i2c_smbus_write_byte_data(client, 0xbc, addr >> 8);
+ if (ret < 0)
+ return ret;
+ ret = i2c_smbus_write_byte_data(client, 0xbd, addr & 0xff);
+ if (ret < 0)
+ return ret;
+ return i2c_smbus_read_byte_data(client, 0xbe);
+}
+
+static inline int sii9234_writeb_idx(struct i2c_client *client, int addr,
+ int value)
+{
+ int ret;
+ ret = i2c_smbus_write_byte_data(client, 0xbc, addr >> 8);
+ if (ret < 0)
+ return ret;
+ ret = i2c_smbus_write_byte_data(client, 0xbd, addr & 0xff);
+ if (ret < 0)
+ return ret;
+ ret = i2c_smbus_write_byte_data(client, 0xbe, value);
+ return ret;
+}
+
+static inline int sii9234_writeb_idx_mask(struct i2c_client *client, int addr,
+ int value, int mask)
+{
+ int ret;
+
+ ret = sii9234_readb_idx(client, addr);
+ if (ret < 0)
+ return ret;
+ ret = (ret & ~mask) | (value & mask);
+ return sii9234_writeb_idx(client, addr, ret);
+}
+
+static int sii9234_reset(struct sii9234_context *ctx)
+{
+ struct i2c_client *client = ctx->client;
+ struct device *dev = &client->dev;
+ int ret, tries;
+
+ gpio_direction_output(ctx->gpio_n_reset, 1);
+ mdelay(1);
+ gpio_direction_output(ctx->gpio_n_reset, 0);
+ mdelay(1);
+ gpio_direction_output(ctx->gpio_n_reset, 1);
+ mdelay(1);
+
+ /* going to TTPI mode */
+ ret = sii9234_writeb(client, 0xc7, 0);
+ if (ret < 0) {
+ dev_err(dev, "failed to set TTPI mode\n");
+ return ret;
+ }
+ for (tries = 0; tries < 100 ; ++tries) {
+ ret = sii9234_readb(client, 0x1b);
+ if (ret > 0)
+ break;
+ if (ret < 0) {
+ dev_err(dev, "failed to reset device\n");
+ return -EIO;
+ }
+ mdelay(1);
+ }
+ if (tries == 100) {
+ dev_err(dev, "maximal number of tries reached\n");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int sii9234_verify_version(struct i2c_client *client)
+{
+ struct device *dev = &client->dev;
+ int family, rev, tpi_rev, dev_id, sub_id, hdcp, id;
+
+ family = sii9234_readb(client, 0x1b);
+ rev = sii9234_readb(client, 0x1c) & 0x0f;
+ tpi_rev = sii9234_readb(client, 0x1d) & 0x7f;
+ dev_id = sii9234_readb_idx(client, 0x0103);
+ sub_id = sii9234_readb_idx(client, 0x0102);
+ hdcp = sii9234_readb(client, 0x30);
+
+ if (family < 0 || rev < 0 || tpi_rev < 0 || dev_id < 0 ||
+ sub_id < 0 || hdcp < 0) {
+ dev_err(dev, "failed to read chip's version\n");
+ return -EIO;
+ }
+
+ id = (dev_id << 8) | sub_id;
+
+ dev_info(dev, "chip: SiL%02x family: %02x, rev: %02x\n",
+ id, family, rev);
+ dev_info(dev, "tpi_rev:%02x, hdcp: %02x\n", tpi_rev, hdcp);
+ if (id != 0x9234) {
+ dev_err(dev, "not supported chip\n");
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static u8 data[][3] = {
+/* setup from driver created by doonsoo45.kim */
+ { 0x01, 0x05, 0x04 }, /* Enable Auto soft reset on SCDT = 0 */
+ { 0x01, 0x08, 0x35 }, /* Power Up TMDS Tx Core */
+ { 0x01, 0x0d, 0x1c }, /* HDMI Transcode mode enable */
+ { 0x01, 0x2b, 0x01 }, /* Enable HDCP Compliance workaround */
+ { 0x01, 0x79, 0x40 }, /* daniel test...MHL_INT */
+ { 0x01, 0x80, 0x34 }, /* Enable Rx PLL Clock Value */
+ { 0x01, 0x90, 0x27 }, /* Enable CBUS discovery */
+ { 0x01, 0x91, 0xe5 }, /* Skip RGND detection */
+ { 0x01, 0x92, 0x46 }, /* Force MHD mode */
+ { 0x01, 0x93, 0xdc }, /* Disable CBUS pull-up during RGND measurement */
+ { 0x01, 0x94, 0x66 }, /* 1.8V CBUS VTH & GND threshold */
+ { 0x01, 0x95, 0x31 }, /* RGND block & single discovery attempt */
+ { 0x01, 0x96, 0x22 }, /* use 1K and 2K setting */
+ { 0x01, 0xa0, 0x10 }, /* SIMG: Term mode */
+ { 0x01, 0xa1, 0xfc }, /* Disable internal Mobile HD driver */
+ { 0x01, 0xa3, 0xfa }, /* SIMG: Output Swing default EB, 3x Clk Mult */
+ { 0x01, 0xa5, 0x80 }, /* SIMG: RGND Hysterisis, 3x mode for Beast */
+ { 0x01, 0xa6, 0x0c }, /* SIMG: Swing Offset */
+ { 0x02, 0x3d, 0x3f }, /* Power up CVCC 1.2V core */
+ { 0x03, 0x00, 0x00 }, /* SIMG: correcting HW default */
+ { 0x03, 0x11, 0x01 }, /* Enable TxPLL Clock */
+ { 0x03, 0x12, 0x15 }, /* Enable Tx Clock Path & Equalizer */
+ { 0x03, 0x13, 0x60 }, /* SIMG: Set termination value */
+ { 0x03, 0x14, 0xf0 }, /* SIMG: Change CKDT level */
+ { 0x03, 0x17, 0x07 }, /* SIMG: PLL Calrefsel */
+ { 0x03, 0x1a, 0x20 }, /* VCO Cal */
+ { 0x03, 0x22, 0xe0 }, /* SIMG: Auto EQ */
+ { 0x03, 0x23, 0xc0 }, /* SIMG: Auto EQ */
+ { 0x03, 0x24, 0xa0 }, /* SIMG: Auto EQ */
+ { 0x03, 0x25, 0x80 }, /* SIMG: Auto EQ */
+ { 0x03, 0x26, 0x60 }, /* SIMG: Auto EQ */
+ { 0x03, 0x27, 0x40 }, /* SIMG: Auto EQ */
+ { 0x03, 0x28, 0x20 }, /* SIMG: Auto EQ */
+ { 0x03, 0x29, 0x00 }, /* SIMG: Auto EQ */
+ { 0x03, 0x31, 0x0b }, /* SIMG: Rx PLL BW value from I2C BW ~ 4MHz */
+ { 0x03, 0x45, 0x06 }, /* SIMG: DPLL Mode */
+ { 0x03, 0x4b, 0x06 }, /* SIMG: Correcting HW default */
+ { 0x03, 0x4c, 0xa0 }, /* Manual zone control */
+ { 0x03, 0x4d, 0x02 }, /* SIMG: PLL Mode Value (order is important) */
+};
+
+static int sii9234_set_internal(struct sii9234_context *ctx)
+{
+ struct i2c_client *client = ctx->client;
+ int i, ret;
+
+ for (i = 0; i < ARRAY_SIZE(data); ++i) {
+ int addr = (data[i][0] << 8) | data[i][1];
+ ret = sii9234_writeb_idx(client, addr, data[i][2]);
+ if (ret < 0)
+ return ret;
+ }
+ return 0;
+}
+
+static int sii9234_runtime_suspend(struct device *dev)
+{
+ struct v4l2_subdev *sd = dev_get_drvdata(dev);
+ struct sii9234_context *ctx = sd_to_context(sd);
+ struct i2c_client *client = ctx->client;
+
+ dev_info(dev, "suspend start\n");
+
+ sii9234_writeb_mask(client, 0x1e, 3, 3);
+ regulator_disable(ctx->power);
+
+ return 0;
+}
+
+static int sii9234_runtime_resume(struct device *dev)
+{
+ struct v4l2_subdev *sd = dev_get_drvdata(dev);
+ struct sii9234_context *ctx = sd_to_context(sd);
+ struct i2c_client *client = ctx->client;
+ int ret;
+
+ dev_info(dev, "resume start\n");
+ regulator_enable(ctx->power);
+
+ ret = sii9234_reset(ctx);
+ if (ret)
+ goto fail;
+
+ /* enable tpi */
+ ret = sii9234_writeb_mask(client, 0x1e, 1, 0);
+ if (ret < 0)
+ goto fail;
+ ret = sii9234_set_internal(ctx);
+ if (ret < 0)
+ goto fail;
+
+ return 0;
+
+fail:
+ dev_err(dev, "failed to resume\n");
+ regulator_disable(ctx->power);
+
+ return ret;
+}
+
+static const struct dev_pm_ops sii9234_pm_ops = {
+ .runtime_suspend = sii9234_runtime_suspend,
+ .runtime_resume = sii9234_runtime_resume,
+};
+
+static int sii9234_s_power(struct v4l2_subdev *sd, int on)
+{
+ struct sii9234_context *ctx = sd_to_context(sd);
+ int ret;
+
+ if (on)
+ ret = pm_runtime_get_sync(&ctx->client->dev);
+ else
+ ret = pm_runtime_put(&ctx->client->dev);
+ /* only values < 0 indicate errors */
+ return IS_ERR_VALUE(ret) ? ret : 0;
+}
+
+static int sii9234_s_stream(struct v4l2_subdev *sd, int enable)
+{
+ struct sii9234_context *ctx = sd_to_context(sd);
+
+ /* (dis/en)able TDMS output */
+ sii9234_writeb_mask(ctx->client, 0x1a, enable ? 0 : ~0 , 1 << 4);
+ return 0;
+}
+
+static const struct v4l2_subdev_core_ops sii9234_core_ops = {
+ .s_power = sii9234_s_power,
+};
+
+static const struct v4l2_subdev_video_ops sii9234_video_ops = {
+ .s_stream = sii9234_s_stream,
+};
+
+static const struct v4l2_subdev_ops sii9234_ops = {
+ .core = &sii9234_core_ops,
+ .video = &sii9234_video_ops,
+};
+
+static int __devinit sii9234_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct device *dev = &client->dev;
+ struct sii9234_platform_data *pdata = dev->platform_data;
+ struct sii9234_context *ctx;
+ int ret;
+
+ ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
+ if (!ctx) {
+ dev_err(dev, "out of memory\n");
+ ret = -ENOMEM;
+ goto fail;
+ }
+ ctx->client = client;
+
+ ctx->power = regulator_get(dev, "hdmi-en");
+ if (IS_ERR(ctx->power)) {
+ dev_err(dev, "failed to acquire regulator hdmi-en\n");
+ ret = PTR_ERR(ctx->power);
+ goto fail_ctx;
+ }
+
+ ctx->gpio_n_reset = pdata->gpio_n_reset;
+ ret = gpio_request(ctx->gpio_n_reset, "MHL_RST");
+ if (ret) {
+ dev_err(dev, "failed to acquire MHL_RST gpio\n");
+ goto fail_power;
+ }
+
+ v4l2_i2c_subdev_init(&ctx->sd, client, &sii9234_ops);
+
+ pm_runtime_enable(dev);
+
+ /* enable device */
+ ret = pm_runtime_get_sync(dev);
+ if (ret)
+ goto fail_pm;
+
+ /* verify chip version */
+ ret = sii9234_verify_version(client);
+ if (ret)
+ goto fail_pm_get;
+
+ /* stop processing */
+ pm_runtime_put(dev);
+
+ dev_info(dev, "probe successful\n");
+
+ return 0;
+
+fail_pm_get:
+ pm_runtime_put_sync(dev);
+
+fail_pm:
+ pm_runtime_disable(dev);
+ gpio_free(ctx->gpio_n_reset);
+
+fail_power:
+ regulator_put(ctx->power);
+
+fail_ctx:
+ kfree(ctx);
+
+fail:
+ dev_err(dev, "probe failed\n");
+
+ return ret;
+}
+
+static int __devexit sii9234_remove(struct i2c_client *client)
+{
+ struct device *dev = &client->dev;
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct sii9234_context *ctx = sd_to_context(sd);
+
+ pm_runtime_disable(dev);
+ gpio_free(ctx->gpio_n_reset);
+ regulator_put(ctx->power);
+ kfree(ctx);
+
+ dev_info(dev, "remove successful\n");
+
+ return 0;
+}
+
+
+static const struct i2c_device_id sii9234_id[] = {
+ { "SII9234", 0 },
+ { },
+};
+
+MODULE_DEVICE_TABLE(i2c, sii9234_id);
+static struct i2c_driver sii9234_driver = {
+ .driver = {
+ .name = "sii9234",
+ .owner = THIS_MODULE,
+ .pm = &sii9234_pm_ops,
+ },
+ .probe = sii9234_probe,
+ .remove = __devexit_p(sii9234_remove),
+ .id_table = sii9234_id,
+};
+
+static int __init sii9234_init(void)
+{
+ return i2c_add_driver(&sii9234_driver);
+}
+module_init(sii9234_init);
+
+static void __exit sii9234_exit(void)
+{
+ i2c_del_driver(&sii9234_driver);
+}
+module_exit(sii9234_exit);
diff --git a/drivers/media/video/tveeprom.c b/drivers/media/video/tveeprom.c
index bcde81cede4c..3b6cf034976a 100644
--- a/drivers/media/video/tveeprom.c
+++ b/drivers/media/video/tveeprom.c
@@ -288,6 +288,14 @@ hauppauge_tuner[] =
{ TUNER_ABSENT, "MaxLinear MxL241SF"},
{ TUNER_XC5000C, "Xceive XC5000C"},
{ TUNER_ABSENT, "Montage M68TS2020"},
+ { TUNER_ABSENT, "Siano SMS1530"},
+ { TUNER_ABSENT, "Dibcom 7090"},
+ { TUNER_ABSENT, "Xceive XC5200C"},
+ { TUNER_ABSENT, "NXP 18273"},
+ { TUNER_ABSENT, "Montage M88TS2022"},
+ /* 180-189 */
+ { TUNER_ABSENT, "NXP 18272M"},
+ { TUNER_ABSENT, "NXP 18272S"},
};
/* Use V4L2_IDENT_AMBIGUOUS for those audio 'chips' that are
diff --git a/drivers/media/video/v4l2-ctrls.c b/drivers/media/video/v4l2-ctrls.c
index 3cef2e217149..9c5caa5fd319 100644
--- a/drivers/media/video/v4l2-ctrls.c
+++ b/drivers/media/video/v4l2-ctrls.c
@@ -1478,7 +1478,7 @@ EXPORT_SYMBOL(v4l2_ctrl_add_ctrl);
int v4l2_ctrl_add_handler(struct v4l2_ctrl_handler *hdl,
struct v4l2_ctrl_handler *add)
{
- struct v4l2_ctrl *ctrl;
+ struct v4l2_ctrl_ref *ref;
int ret = 0;
/* Do nothing if either handler is NULL or if they are the same */
@@ -1487,7 +1487,9 @@ int v4l2_ctrl_add_handler(struct v4l2_ctrl_handler *hdl,
if (hdl->error)
return hdl->error;
mutex_lock(&add->lock);
- list_for_each_entry(ctrl, &add->ctrls, node) {
+ list_for_each_entry(ref, &add->ctrl_refs, node) {
+ struct v4l2_ctrl *ctrl = ref->ctrl;
+
/* Skip handler-private controls. */
if (ctrl->is_private)
continue;
diff --git a/include/media/s5p_hdmi.h b/include/media/s5p_hdmi.h
new file mode 100644
index 000000000000..361a751f73af
--- /dev/null
+++ b/include/media/s5p_hdmi.h
@@ -0,0 +1,35 @@
+/*
+ * Driver header for S5P HDMI chip.
+ *
+ * Copyright (c) 2011 Samsung Electronics, Co. Ltd
+ * Contact: Tomasz Stanislawski <t.stanislaws@samsung.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 S5P_HDMI_H
+#define S5P_HDMI_H
+
+struct i2c_board_info;
+
+/**
+ * @hdmiphy_bus: controller id for HDMIPHY bus
+ * @hdmiphy_info: template for HDMIPHY I2C device
+ * @mhl_bus: controller id for MHL control bus
+ * @mhl_info: template for MHL I2C device
+ *
+ * NULL pointer for *_info fields indicates that
+ * the corresponding chip is not present
+ */
+struct s5p_hdmi_platform_data {
+ int hdmiphy_bus;
+ struct i2c_board_info *hdmiphy_info;
+ int mhl_bus;
+ struct i2c_board_info *mhl_info;
+};
+
+#endif /* S5P_HDMI_H */
+
diff --git a/include/media/sii9234.h b/include/media/sii9234.h
new file mode 100644
index 000000000000..6a4a809fe9a3
--- /dev/null
+++ b/include/media/sii9234.h
@@ -0,0 +1,24 @@
+/*
+ * Driver header for SII9234 MHL converter chip.
+ *
+ * Copyright (c) 2011 Samsung Electronics, Co. Ltd
+ * Contact: Tomasz Stanislawski <t.stanislaws@samsung.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 SII9234_H
+#define SII9234_H
+
+/**
+ * @gpio_n_reset: GPIO driving nRESET pin
+ */
+
+struct sii9234_platform_data {
+ int gpio_n_reset;
+};
+
+#endif /* SII9234_H */

Privacy Policy