[PATCH 6/7] adding empia cx25843 driver

From: Markus Rechberger
Date: Wed Oct 22 2008 - 17:10:17 EST


em28xx-cx25843.c:
chipdriver for cx25843 but the chip configuration (eg. raw VBI
offsets, general offsets,
audio setup) is bound to the em28xx, all inputs and most
videostandards are tested
using signal generators.
commit 8e6701e401a7d169b19f4161f6fc49924014d4b8
Author: Markus Rechberger <mrechberger@xxxxxxxxxx>
Date: Wed Oct 22 22:12:24 2008 +0200

adding empia cx25843 driver

em28xx-cx25843.c:
chipdriver for cx25843 but the chip configuration (eg. raw VBI offsets, general offsets,
audio setup) is bound to the em28xx, all inputs and most videostandards are tested
using signal generators.

Signed-off-by: Markus Rechberger <mrechberger@xxxxxxxxxx>

diff --git a/drivers/media/video/empia/cx25843/Makefile b/drivers/media/video/empia/cx25843/Makefile
new file mode 100644
index 0000000..f2c769d
--- /dev/null
+++ b/drivers/media/video/empia/cx25843/Makefile
@@ -0,0 +1,3 @@
+obj-m += em28xx-cx25843.o
+
+EXTRA_CFLAGS += -Wall
diff --git a/drivers/media/video/empia/cx25843/em28xx-cx25843-fw.h b/drivers/media/video/empia/cx25843/em28xx-cx25843-fw.h
new file mode 100644
index 0000000..14941b6
--- /dev/null
+++ b/drivers/media/video/empia/cx25843/em28xx-cx25843-fw.h
@@ -0,0 +1,9 @@
+#ifndef _CX25843_FW
+#define _CX25843_FW
+/* firmware */
+
+#define CONEXANT_EXTERNAL_FIRMWARE
+__u8 *em28xx_cxfirmware;
+__u16 em28xx_cxfirmware_length;
+
+#endif
diff --git a/drivers/media/video/empia/cx25843/em28xx-cx25843.c b/drivers/media/video/empia/cx25843/em28xx-cx25843.c
new file mode 100644
index 0000000..6694812
--- /dev/null
+++ b/drivers/media/video/empia/cx25843/em28xx-cx25843.c
@@ -0,0 +1,698 @@
+/*
+ * cx25843 - Conexant cx25843 Video/Audiodecoder
+ *
+ * Copyright (c) 2008 Markus Rechberger <mrechberger@xxxxxxxxxx>
+ *
+ * Opposed to the available cx2584x kerneldriver, this driver
+ * will very likely only work for empia based devices since most
+ * registers are set up to fit the configuration of the em28xx chip
+ * in order to provide proper VBI and videostandard support.
+ *
+ */
+
+#include <linux/i2c.h>
+#include <linux/version.h>
+#include <linux/videodev.h>
+#include <linux/delay.h>
+#include <linux/video_decoder.h>
+#include <linux/firmware.h>
+#include <linux/types.h>
+
+#include <media/v4l2-common.h>
+#include "em28xx-cx25843.h"
+#include "em28xx-cx25843-fw.h"
+#include "em28xxfw-cx25843.h"
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 0)
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include "i2c-compat.h"
+#endif
+#define CONEXANT_EXTERNAL_FIRMWARE
+
+#include "em28xx-cx25843.h"
+
+MODULE_DESCRIPTION("cx25843 for Empia em28xx based devices");
+MODULE_AUTHOR("Markus Rechberger <mrechberger@xxxxxxxxxxxxx>");
+MODULE_LICENSE("GPL");
+
+/* standard i2c insmod options */
+static unsigned short normal_i2c[] = {
+ 0x88 >> 1,
+ I2C_CLIENT_END
+};
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 13)
+static unsigned short normal_i2c_range[] = { I2C_CLIENT_END };
+#endif
+I2C_CLIENT_INSMOD;
+
+struct cx25843 {
+ struct i2c_client *client;
+
+ v4l2_std_id norm; /* Current set standard */
+ struct v4l2_routing route;
+};
+
+static int cx25843_read(struct i2c_client *c, unsigned int addr)
+{
+ unsigned char buffer[2];
+ int rc;
+
+ buffer[0] = addr>>8;
+ buffer[1] = addr & 0xff;
+
+ rc = i2c_master_send(c, buffer, 2);
+
+ if (2 != rc) {
+ printk(KERN_ERR"i2c i/o error: rc == %d (should be 1)\n", rc);
+ return rc;
+ }
+
+ msleep(10);
+
+ rc = i2c_master_recv(c, buffer, 1);
+ if (1 != rc) {
+ printk(KERN_ERR"i2c i/o error: rc == %d (should be 1)\n", rc);
+ return rc;
+ }
+
+ return buffer[0];
+}
+
+static int cx25843_and_or(struct i2c_client *c, unsigned int addr,
+ unsigned char andval, unsigned char orval)
+{
+ unsigned char buffer[3];
+ int rc;
+
+ buffer[0] = addr>>8;
+ buffer[1] = addr&0xff;
+
+ rc = i2c_master_send(c, buffer, 2);
+ if (2 != rc) {
+ printk(KERN_ERR"i2c i/o error: rc == %d (should be 2)\n", rc);
+ return rc;
+ }
+
+ msleep(10);
+
+ rc = i2c_master_recv(c, buffer, 1);
+ if (1 != rc) {
+ printk(KERN_ERR"i2c i/o error: rc == %d (should be 1)\n", rc);
+ return rc;
+ }
+
+ buffer[2] = (buffer[0]&andval)|orval;
+ buffer[1] = addr&0xff;
+ buffer[0] = addr>>8;
+
+ rc = i2c_master_send(c, buffer, 3);
+ if (3 != rc)
+ printk(KERN_ERR"i2c i/o error: rc == %d (should be 3)\n", rc);
+
+ return rc;
+}
+
+static int cx25843_write(struct i2c_client *c, unsigned int addr,
+ unsigned char *value, int len)
+{
+ int rc;
+
+ __u8 buffer[100];
+
+ buffer[0] = addr>>8;
+ buffer[1] = addr&0xff;
+
+ if (len < 99)
+ memcpy(&buffer[2], value, len);
+ else
+ return -EINVAL;
+
+ rc = i2c_master_send(c, buffer, len+2);
+ if (len+2 != rc) {
+ printk(KERN_ERR"i2c i/o error: rc == %d (should be 2)\n", rc);
+ return rc;
+ }
+ return 0;
+}
+
+/****************************************************************************
+ Basic functions
+ ****************************************************************************/
+
+static inline void cx25843_set_videorouting(struct i2c_client *c)
+{
+ struct cx25843 *decoder = i2c_get_clientdata(c);
+ switch (decoder->route.input) {
+ case CX25843_TELEVISION: /* analogue TV */
+ cx25843_write(c, R103_VIDEO_INPUT_CTRL, "\x10", 1);
+
+ cx25843_write(c, R400_VIDEO_MODE_CTRL1, "\x20", 1);
+ cx25843_write(c, R401_VIDEO_MODE_CTRL2, "\xe0", 1);
+ cx25843_write(c, R402_VIDEO_MODE_CTRL3, "\x04", 1);
+ cx25843_write(c, R403_VIDEO_MODE_CTRL4, "\x00", 1);
+
+ cx25843_write(c, R108_VID_PLL_CLK, "\x0f\x04\x0a\x10", 4);
+ cx25843_write(c, R8D0_PATH_AUDIO_SLCT_CTRL,
+ "\x70\x38\x06\x1f", 4);
+ cx25843_write(c, R803_START_MICROCNTL, "\x03", 1);
+ cx25843_write(c, R803_START_MICROCNTL, "\x13", 1);
+ break;
+ case CX25843_SVIDEO: /* svideo */
+ /* TODO: better configuration */
+ cx25843_write(c, R103_VIDEO_INPUT_CTRL, "\x02", 1);
+ cx25843_write(c, R400_VIDEO_MODE_CTRL1, "\x20", 1);
+ cx25843_write(c, R401_VIDEO_MODE_CTRL2, "\x02", 1);
+ cx25843_write(c, R402_VIDEO_MODE_CTRL3, "\x00", 1);
+ cx25843_write(c, R403_VIDEO_MODE_CTRL4, "\x00", 1);
+ cx25843_write(c, R108_VID_PLL_CLK, "\x0f\x04\x0a\x18", 4);
+ cx25843_write(c, R803_START_MICROCNTL, "\x03", 1);
+ cx25843_write(c, R8D0_PATH_AUDIO_SLCT_CTRL,
+ "\x12\x10\x01\x01", 4);
+ break;
+ case CX25843_COMPOSITE1: /* composite */
+ /* TODO: better configuration */
+ cx25843_write(c, R103_VIDEO_INPUT_CTRL, "\x01", 1);
+ cx25843_write(c, R400_VIDEO_MODE_CTRL1, "\x20", 1);
+ cx25843_write(c, R401_VIDEO_MODE_CTRL2, "\xf0", 1);
+ cx25843_write(c, R402_VIDEO_MODE_CTRL3, "\x04", 1);
+ cx25843_write(c, R403_VIDEO_MODE_CTRL4, "\x00", 1);
+ cx25843_write(c, R108_VID_PLL_CLK, "\x0f\x04\x0a\x18", 4);
+ cx25843_write(c, R803_START_MICROCNTL, "\x03", 1);
+
+ cx25843_write(c, R8D0_PATH_AUDIO_SLCT_CTRL,
+ "\x12\x10\x01\x00", 4);
+ break;
+ default:
+ break;
+ }
+};
+
+struct i2c_reg_value {
+ unsigned char reg;
+ unsigned char value;
+};
+
+static int cx25843_set_std(struct i2c_client *c)
+{
+ struct cx25843 *decoder = i2c_get_clientdata(c);
+
+ if (decoder->norm == V4L2_STD_NTSC_M) {
+
+
+ cx25843_write(c, R404_VIDEO_OUTPUT_CONTROL1, "\x3e", 1);
+ cx25843_write(c, R405_VIDEO_OUTPUT_CONTROL2, "\x29", 1);
+ cx25843_write(c, R406_VIDEO_OUTPUT_CONTROL3, "\x10", 1);
+ cx25843_write(c, R407_VIDEO_OUTPUT_CONTROL4, "\x04", 1);
+
+ cx25843_write(c, R474_VBLANK_DELAY_LOW, "\x1a", 1);
+ cx25843_write(c, R475_VBLANK_DELAY_HIGH, "\x70", 1);
+ cx25843_write(c, R476_VACTIVE_HIGH, "\x1e", 1);
+ cx25843_write(c, R477_VBLANK_DELAY_656, "\x1e", 1);
+
+ cx25843_write(c, R470_HBLANK_DELAY_LOW, "\x7e", 1);
+ cx25843_write(c, R471_HBLANK_DELAY_HIGH, "\x00", 1);
+ cx25843_write(c, R472_HACTIVE_HIGH, "\x2d", 1);
+ cx25843_write(c, R473_BURST_GATE_DELAY, "\x5b", 1);
+
+ cx25843_write(c, R47C_SUBCARRIER_STEP_SIZE_LOW, "\x1f", 1);
+ cx25843_write(c, R47D_SUBCARRIER_STEP_SIZE_MID, "\x7c", 1);
+ cx25843_write(c, R47E_SUBCARRIER_STEP_SIZE_HIGH, "\x08", 1);
+ cx25843_write(c, R47F_VBI_OFFSET, "\x00", 1);
+ cx25843_write(c, R808_AUDIO_CONFIGURATION, "\xf6", 1);
+
+ } else if (decoder->norm & V4L2_STD_PAL_BG ||
+ decoder->norm & V4L2_STD_PAL_I ||
+ decoder->norm & V4L2_STD_PAL_DK) {
+
+ cx25843_write(c, R404_VIDEO_OUTPUT_CONTROL1, "\x3e", 1);
+ cx25843_write(c, R405_VIDEO_OUTPUT_CONTROL2, "\x29", 1);
+ cx25843_write(c, R406_VIDEO_OUTPUT_CONTROL3, "\x10", 1);
+ cx25843_write(c, R407_VIDEO_OUTPUT_CONTROL4, "\x00", 1);
+
+ /* those configurations match the em28xx VBI chip
+ configuration */
+ cx25843_write(c, R474_VBLANK_DELAY_LOW, "\x22", 1);
+ cx25843_write(c, R475_VBLANK_DELAY_HIGH, "\x30", 1);
+ cx25843_write(c, R476_VACTIVE_HIGH, "\x24", 1);
+ cx25843_write(c, R477_VBLANK_DELAY_656, "\x28", 1);
+
+ cx25843_write(c, R470_HBLANK_DELAY_LOW, "\x8a", 1);
+ cx25843_write(c, R471_HBLANK_DELAY_HIGH, "\x00", 1);
+ cx25843_write(c, R472_HACTIVE_HIGH, "\x2d", 1);
+ cx25843_write(c, R473_BURST_GATE_DELAY, "\x5d", 1);
+
+ cx25843_write(c, R47C_SUBCARRIER_STEP_SIZE_LOW, "\x63", 1);
+ cx25843_write(c, R47D_SUBCARRIER_STEP_SIZE_MID, "\x82", 1);
+ cx25843_write(c, R47E_SUBCARRIER_STEP_SIZE_HIGH, "\x0a", 1);
+ cx25843_write(c, R47F_VBI_OFFSET, "\x01", 1);
+ cx25843_write(c, R808_AUDIO_CONFIGURATION, "\xff", 1);
+
+ } else if (decoder->norm == V4L2_STD_SECAM_L) {
+
+ cx25843_write(c, R404_VIDEO_OUTPUT_CONTROL1, "\x3e", 1);
+ cx25843_write(c, R405_VIDEO_OUTPUT_CONTROL2, "\x29", 1);
+ cx25843_write(c, R406_VIDEO_OUTPUT_CONTROL3, "\x10", 1);
+ cx25843_write(c, R407_VIDEO_OUTPUT_CONTROL4, "\x00", 1);
+
+ /* those configurations match the em28xx VBI chip
+ configuration */
+ cx25843_write(c, R474_VBLANK_DELAY_LOW, "\x22", 1);
+ cx25843_write(c, R475_VBLANK_DELAY_HIGH, "\x30", 1);
+ cx25843_write(c, R476_VACTIVE_HIGH, "\x24", 1);
+ cx25843_write(c, R477_VBLANK_DELAY_656, "\x28", 1);
+
+ cx25843_write(c, R470_HBLANK_DELAY_LOW, "\x8a", 1);
+ cx25843_write(c, R471_HBLANK_DELAY_HIGH, "\x00", 1);
+ cx25843_write(c, R472_HACTIVE_HIGH, "\x2d", 1);
+ cx25843_write(c, R473_BURST_GATE_DELAY, "\x5d", 1);
+
+ cx25843_write(c, R47C_SUBCARRIER_STEP_SIZE_LOW, "\x5f", 1);
+ cx25843_write(c, R47D_SUBCARRIER_STEP_SIZE_MID, "\x42", 1);
+ cx25843_write(c, R47E_SUBCARRIER_STEP_SIZE_HIGH, "\x0a", 1);
+ cx25843_write(c, R47F_VBI_OFFSET, "\x01", 1);
+ cx25843_write(c, R808_AUDIO_CONFIGURATION, "\xf5", 1);
+ }
+
+ return 0;
+
+
+}
+
+static inline void cx25843_reset(struct i2c_client *c)
+{
+ int i;
+
+
+ cx25843_write(c, R803_DOWNLOAD_CONTROL, "\x00", 1);
+
+ /* initialize DLL1 */
+ cx25843_write(c, R15A_DLL1_DIAGNOSTIC_CONTROL3, "\x87", 1);
+ cx25843_write(c, R15B_DLL1_DIAGNOSTIC_CONTROL4, "\x06", 1);
+ udelay(10);
+ cx25843_write(c, R159_DLL1_DIAGNOSTIC_CONTROL2, "\x21", 1);
+ udelay(10);
+ cx25843_write(c, R159_DLL1_DIAGNOSTIC_CONTROL2, "\xe1", 1);
+ cx25843_write(c, R159_DLL1_DIAGNOSTIC_CONTROL2, "\xe0", 1);
+
+ cx25843_write(c, R15B_DLL1_DIAGNOSTIC_CONTROL4, "\x10", 1);
+ cx25843_write(c, R15B_DLL1_DIAGNOSTIC_CONTROL4, "\x00", 1);
+ cx25843_write(c, R15B_DLL1_DIAGNOSTIC_CONTROL4, "\x10", 1);
+
+ /* initialize DLL2 */
+ cx25843_write(c, R15E_DLL2_DIAGNOSTIC_CONTROL3, "\x86", 1);
+ cx25843_write(c, R15F_DLL2_DIAGNOSTIC_CONTROL4, "\x06", 1);
+ cx25843_write(c, R15D_DLL2_DIAGNOSTIC_CONTROL2, "\x23", 1);
+ cx25843_write(c, R15D_DLL2_DIAGNOSTIC_CONTROL2, "\xe3", 1);
+ udelay(10);
+ cx25843_write(c, R15D_DLL2_DIAGNOSTIC_CONTROL2, "\xe1", 1);
+ cx25843_write(c, R15D_DLL2_DIAGNOSTIC_CONTROL2, "\xe0", 1);
+ cx25843_write(c, R15D_DLL2_DIAGNOSTIC_CONTROL2, "\xe1", 1);
+
+ cx25843_write(c, R13C_AFE_DIAGNOSTIC_CONTROL5, "\x01", 1);
+ cx25843_write(c, R13C_AFE_DIAGNOSTIC_CONTROL5, "\x00", 1);
+
+ cx25843_and_or(c, R803_DOWNLOAD_CONTROL, ~0x1b, 0xb);
+ cx25843_and_or(c, R000_HOST_REGISTER_1, ~0x20, 0x20);
+
+ cx25843_write(c, R801_DOWNLOAD_ADDRESS_HIGH_BYTE, "\x00", 1);
+ cx25843_write(c, R800_DOWNLOAD_ADDRESS_LOW_BYTE, "\x00", 1);
+
+ /* load firmware */
+
+ for (i = 0; i<em28xx_cxfirmware_length; i++) {
+ cx25843_write(c, R802_DOWNLOAD_DATA_CONTROL,
+ &em28xx_cxfirmware[i+1], em28xx_cxfirmware[i]);
+
+ i += em28xx_cxfirmware[i];
+ }
+ cx25843_and_or(c, R000_HOST_REGISTER_1, ~0x20, 0x00);
+ cx25843_write(c, R803_DOWNLOAD_CONTROL, "\x13", 1);
+
+ /* Enable digital output drivers */
+ cx25843_write(c, R115_PIN_CONTROL2, "\x8c", 1);
+ cx25843_write(c, R116_PIN_CONTROL3, "\x07", 1);
+ cx25843_write(c, R118_PIN_CONTROL5, "\x02", 1);
+
+ /* initialize videodecoder */
+ cx25843_write(c, R4A5_SOFT_RESET_MASK2, "\x80", 1);
+ cx25843_write(c, R4A5_SOFT_RESET_MASK2, "\x00", 1);
+
+ cx25843_write(c, R402_VIDEO_MODE_CTRL3, "\x00", 1);
+
+ /* optimize video decoder settings */
+
+ cx25843_and_or(c, R401_VIDEO_MODE_CTRL2, ~0x28, 0);
+ cx25843_and_or(c, R4A2_WHITE_CRUSH_COMPARISON_PT, 0, 8);
+
+ cx25843_write(c, R914_SERIAL_AUDIO_INPUT_CTRL,
+ "\x20\x00\x00\x00", 4);
+
+ cx25843_write(c, R918_SERIAL_AUDIO_OUTPUT_CTRL1,
+ "\x20\x01\x00\x00", 4);
+
+ cx25843_write(c, R400_VIDEO_MODE_CTRL1, "\x20", 1);
+ cx25843_write(c, R401_VIDEO_MODE_CTRL2, "\xf0", 1);
+ cx25843_write(c, R402_VIDEO_MODE_CTRL3, "\x04", 1);
+ cx25843_write(c, R403_VIDEO_MODE_CTRL4, "\x00", 1);
+
+ cx25843_write(c, R809_PREFERRED_DECODE_MODE, "\x04", 1);
+
+ /* start microcontroller */
+ cx25843_write(c, R803_START_MICROCNTL, "\x13", 1);
+
+
+
+};
+
+static int cx25843_get_ctrl(struct i2c_client *c, struct v4l2_control *ctrl)
+{
+ return -EINVAL;
+}
+
+static int cx25843_get_tuner(struct i2c_client *c, struct v4l2_tuner *vt)
+{
+ __u8 status2;
+ __u8 modestatus;
+
+ if (vt->type == V4L2_TUNER_RADIO) {
+ status2 = cx25843_read(c, R40E_GENERAL_STATUS);
+ modestatus = cx25843_read(c, R804_MODE_DETECT_STATUS);
+
+ /* status2 doesn't always get a lock .. checking modestatus
+ as a workaround */
+
+ if (status2 & 0x20 || modestatus != 0)
+ vt->signal = 0xffff;
+ else
+ vt->signal = 0x0;
+
+ vt->type = V4L2_TUNER_RADIO;
+
+ vt->rangelow = (87*16000);
+ vt->rangehigh = (108*16000);
+
+ strcpy(vt->name, "Empia FM");
+
+ vt->capability = V4L2_TUNER_CAP_LOW;
+
+
+ vt->rxsubchans = 0;
+
+ switch (modestatus) {
+ case 0x00:
+ /* Mono */
+ vt->rxsubchans |= V4L2_TUNER_SUB_MONO;
+ break;
+ case 0x01:
+ /* Stereo */
+ vt->signal = 0xffff;
+ vt->rxsubchans |= V4L2_TUNER_SUB_STEREO;
+ break;
+ case 0x02:
+ /* Dual */
+ vt->signal = 0xffff;
+ break;
+ case 0x04:
+ /* Tri */
+ vt->signal = 0xffff;
+ break;
+ case 0x10:
+ /* SAP */
+ vt->signal = 0xffff;
+ vt->rxsubchans |= V4L2_TUNER_SUB_SAP;
+ break;
+ case 0xFE:
+ /* Forced Mode */
+ vt->signal = 0xffff;
+ vt->rxsubchans = 0;
+ break;
+ default:
+ break;
+ /* undef */
+ }
+
+ vt->audmode = V4L2_TUNER_MODE_STEREO; /* keep it that way for
+ now */
+
+ }
+
+ return 0;
+}
+
+static int cx25843_set_audio_routing(struct i2c_client *c,
+ struct v4l2_routing *route)
+{
+ switch (route->input) {
+ case CX25843_COMPOSITE1:
+ case CX25843_TELEVISION:
+ case CX25843_SVIDEO:
+ /* standard dependent actually */
+ break;
+ case CX25843_RADIO:
+ /* radio */
+ cx25843_write(c, R808_AUDIO_CONFIGURATION, "\xf9", 1);
+ cx25843_write(c, R803_START_MICROCNTL, "\x03", 1);
+ cx25843_write(c, R803_START_MICROCNTL, "\x13", 1);
+ break;
+ }
+ return 0;
+}
+
+/****************************************************************************
+ I2C Command
+ ****************************************************************************/
+static int cx25843_command(struct i2c_client *c,
+ unsigned int cmd, void *arg)
+{
+ struct cx25843 *decoder = i2c_get_clientdata(c);
+
+ switch (cmd) {
+ case VIDIOC_INT_RESET:
+ {
+#ifdef CONEXANT_EXTERNAL_FIRMWARE
+ int ret = 0;
+ const struct firmware *fw;
+ static unsigned char *cx25843_firmware;
+
+ if (cx25843_firmware == NULL) {
+ ret = request_firmware(&fw, "em28xx-cx25843.fw", &c->dev);
+
+ if (ret || fw->size != 16654) {
+ printk("em28xx-cx25843: Please check em28xx-cx25843.fw filesize has to be 16654 bytes\n");
+ printk("em28xx-cx25843: cd /lib/firmware\n");
+ printk("em28xx-cx25843: sudo wget mcentral.de/empia/em28xx-cx25843.fw\n");
+ return -EINVAL;
+ } else
+ printk("em28xx-cx25843: em28xx-cx25843.fw successfully loaded to RAM\n");
+
+ em28xx_cxfirmware = kzalloc(fw->size, GFP_KERNEL);
+
+ memcpy(em28xx_cxfirmware, &fw->data[6], fw->size-6);
+ release_firmware(fw);
+ em28xx_cxfirmware_length = 16648;
+ }
+#endif
+ cx25843_reset(c);
+ break;
+ }
+ case VIDIOC_INT_G_VIDEO_ROUTING:
+ {
+ struct v4l2_routing *route = arg;
+
+ *route = decoder->route;
+ break;
+ }
+ case VIDIOC_INT_S_VIDEO_ROUTING:
+ {
+ struct v4l2_routing *route = arg;
+
+ decoder->route = *route;
+ cx25843_set_videorouting(c);
+ break;
+ }
+ case VIDIOC_INT_S_AUDIO_ROUTING:
+ {
+ cx25843_set_audio_routing(c, arg);
+ break;
+ }
+ case VIDIOC_S_STD:
+ decoder->norm = *(v4l2_std_id *)arg;
+ return cx25843_set_std(c);
+ case VIDIOC_G_STD:
+ *(v4l2_std_id *)arg = decoder->norm;
+ break;
+
+ case VIDIOC_G_SLICED_VBI_CAP:
+ {
+ /* todo */
+ break;
+ }
+ case VIDIOC_S_FMT:
+ {
+ break;
+ }
+ case VIDIOC_G_FMT:
+ {
+ break;
+ }
+
+ case VIDIOC_LOG_STATUS:
+ break;
+
+ case VIDIOC_G_TUNER:
+ {
+ struct v4l2_tuner *vt = arg;
+ cx25843_get_tuner(c, vt);
+
+ break;
+ }
+ case VIDIOC_QUERYCTRL:
+ {
+ return -EINVAL;
+ }
+ case VIDIOC_G_CTRL:
+ {
+ struct v4l2_control *ctrl = arg;
+ return cx25843_get_ctrl(c, ctrl);
+ }
+ case VIDIOC_S_CTRL:
+ {
+ return -EINVAL;
+ }
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/****************************************************************************
+ I2C Client & Driver
+ ****************************************************************************/
+static struct i2c_driver driver;
+
+static struct i2c_client client_template = {
+ .name = "(unset)",
+#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 15)
+ .flags = I2C_CLIENT_ALLOW_USE,
+#endif
+ .driver = &driver,
+};
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 0)
+static int cx25843_detect_client(struct i2c_adapter *adapter,
+ int address, int kind)
+#else
+static int cx25843_detect_client(struct i2c_adapter *adapter,
+ int address, unsigned short flags, int kind)
+#endif
+{
+ struct i2c_client *c;
+ struct cx25843 *core;
+ int rv;
+
+ printk(KERN_INFO"cx25843.c: detecting cx25843 client on address 0x%x\n",
+ address << 1);
+
+ client_template.adapter = adapter;
+ client_template.addr = address;
+
+ /* Check if the adapter supports the needed features */
+ if (!i2c_check_functionality
+ (adapter,
+ I2C_FUNC_SMBUS_READ_BYTE |
+ I2C_FUNC_SMBUS_WRITE_BYTE_DATA))
+ return 0;
+
+ c = kmalloc(sizeof(struct i2c_client), GFP_KERNEL);
+ if (c == 0)
+ return -ENOMEM;
+ memcpy(c, &client_template, sizeof(struct i2c_client));
+
+ core = kzalloc(sizeof(struct cx25843), GFP_KERNEL);
+ if (core == 0) {
+ kfree(c);
+ return -ENOMEM;
+ }
+ i2c_set_clientdata(c, core);
+
+ rv = i2c_attach_client(c);
+
+ core->norm = V4L2_STD_ALL; /* Default is autodetect */
+ core->route.input = CX25843_TELEVISION;
+
+ if (rv) {
+ kfree(c);
+ kfree(core);
+ return rv;
+ }
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 0)
+ MOD_INC_USE_COUNT;
+#endif
+ return 0;
+}
+
+static int cx25843_attach_adapter(struct i2c_adapter *adapter)
+{
+ printk(KERN_INFO"cx25843.c: starting probe for adapter %s (0x%x)\n",
+ adapter->name, adapter->id);
+ return i2c_probe(adapter, &addr_data, &cx25843_detect_client);
+}
+
+static int cx25843_detach_client(struct i2c_client *c)
+{
+ struct cx25843 *decoder = i2c_get_clientdata(c);
+ int err;
+
+ printk(KERN_INFO"cx25843.c: removing cx25843 adapter on address 0x%x\n",
+ c->addr << 1);
+
+ err = i2c_detach_client(c);
+ if (err)
+ return err;
+
+ kfree(decoder);
+ kfree(c);
+
+ return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+ static struct i2c_driver driver = {
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 0)) && \
+ (LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 15))
+ .owner = THIS_MODULE,
+#endif
+#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 15)
+ .name = "cx25843",
+ .flags = I2C_DF_NOTIFY,
+#else
+ .driver = {
+ .name = "cx25843",
+ },
+#endif
+ .id = I2C_DRIVERID_EETI_CX25843,
+
+ .attach_adapter = cx25843_attach_adapter,
+ .detach_client = cx25843_detach_client,
+
+ .command = cx25843_command,
+ };
+
+static int __init cx25843_init(void)
+{
+ return i2c_add_driver(&driver);
+}
+
+static void __exit cx25843_exit(void)
+{
+ i2c_del_driver(&driver);
+}
+
+module_init(cx25843_init);
+module_exit(cx25843_exit);
diff --git a/drivers/media/video/empia/cx25843/em28xx-cx25843.h b/drivers/media/video/empia/cx25843/em28xx-cx25843.h
new file mode 100644
index 0000000..be5e97e
--- /dev/null
+++ b/drivers/media/video/empia/cx25843/em28xx-cx25843.h
@@ -0,0 +1,92 @@
+#ifndef _EM28XX_CX25843_H
+#define _EM28XX_CX25843_H
+
+#include <linux/i2c.h>
+
+#ifndef I2C_DRIVERID_EETI_CX25843
+#define I2C_DRIVERID_EETI_CX25843 0x100
+#endif
+
+#define R000_HOST_REGISTER_1 0x000
+#define R100_DEVICE_ID_LOW_BYTE 0x100
+#define R102_MISC_CHIP_CTRL 0x102
+#define R103_VIDEO_INPUT_CTRL 0x103
+#define R114_PIN_CONTROL1 0x114
+#define R115_PIN_CONTROL2 0x115
+#define R116_PIN_CONTROL3 0x116
+#define R118_PIN_CONTROL5 0x118
+#define R124_PIN_CONFIGURATION_9 0x124
+#define R13C_AFE_DIAGNOSTIC_CONTROL5 0x13c
+#define R13D_AFE_DIAGNOSTIC_CONTROL6 0x13d
+#define R159_DLL1_DIAGNOSTIC_CONTROL2 0x159
+#define R15A_DLL1_DIAGNOSTIC_CONTROL3 0x15A
+#define R15B_DLL1_DIAGNOSTIC_CONTROL4 0x15B
+#define R15C_DLL2_DIAGNOSTIC_CONTROL1 0x15c
+#define R15D_DLL2_DIAGNOSTIC_CONTROL2 0x15d
+#define R15E_DLL2_DIAGNOSTIC_CONTROL3 0x15e
+#define R15F_DLL2_DIAGNOSTIC_CONTROL4 0x15f
+#define R400_VIDEO_MODE_CTRL1 0x400
+#define R401_VIDEO_MODE_CTRL2 0x401
+#define R402_VIDEO_MODE_CTRL3 0x402
+#define R403_VIDEO_MODE_CTRL4 0x403
+#define R404_VIDEO_OUTPUT_CONTROL1 0x404
+#define R405_VIDEO_OUTPUT_CONTROL2 0x405
+#define R406_VIDEO_OUTPUT_CONTROL3 0x406
+#define R407_VIDEO_OUTPUT_CONTROL4 0x407
+#define R40E_GENERAL_STATUS 0x40e
+#define R418_HORIZONTAL_SCALING_LOW 0x418
+#define R422_HUE 0x422
+#define R41C_VERTICAL_SCALING_LOW 0x41c
+#define R470_HBLANK_DELAY_LOW 0x470
+#define R471_HBLANK_DELAY_HIGH 0x471
+#define R472_HACTIVE_HIGH 0x472
+#define R473_BURST_GATE_DELAY 0x473
+#define R474_VBLANK_DELAY_LOW 0x474
+#define R475_VBLANK_DELAY_HIGH 0x475
+#define R476_VACTIVE_HIGH 0x476
+#define R477_VBLANK_DELAY_656 0x477
+#define R478_SRC_DECIMATION_RATIO_LOW 0x478
+#define R47C_SUBCARRIER_STEP_SIZE_LOW 0x47c
+#define R47D_SUBCARRIER_STEP_SIZE_MID 0x47d
+#define R47E_SUBCARRIER_STEP_SIZE_HIGH 0x47e
+#define R47F_VBI_OFFSET 0x47f
+#define R49C_LUMA_COMB_ERROR_LIMIT_MAX 0x49c
+#define R4A2_WHITE_CRUSH_COMPARISON_PT 0x4a2
+#define R4A4_SOFT_RESET_MASK1 0x4a4
+#define R4A5_SOFT_RESET_MASK2 0x4a5
+#define R800_DOWNLOAD_ADDRESS_LOW_BYTE 0x800
+#define R801_DOWNLOAD_ADDRESS_HIGH_BYTE 0x801
+#define R802_DOWNLOAD_DATA_CONTROL 0x802
+#define R803_DOWNLOAD_CONTROL 0x803
+#define R804_MODE_DETECT_STATUS 0x804
+#define R808_AUDIO_CONFIGURATION 0x808
+#define R80B_AUDIO_FORMAT_CONTROL0 0x80b
+#define R910_OUTPUT_SRC_SOURCE_SELECT 0x910
+#define R914_SERIAL_AUDIO_INPUT_CTRL 0x914
+#define R918_SERIAL_AUDIO_OUTPUT_CTRL1 0x918
+#define R810_SOFT_RESET 0x810
+#define R8D0_PATH_AUDIO_SLCT_CTRL 0x8d0
+#define R8D3_MUTE_CTRL 0x8d3
+#define R8CC_PHASE_FIX_CONTROL 0x8cc
+#define R8E3_PATH2_BALANCE 0x8e3
+
+#define R808_AUDIO_CONFIGURATION 0x808
+#define R809_PREFERRED_DECODE_MODE 0x809
+#define R127_PIN_CONFIGURATION 0x127
+#define R803_START_MICROCNTL 0x803
+
+#define R108_VID_PLL_CLK 0x108
+#define R110_AUX_PLL_FRACTION1 0x110
+#define R900_SRC3_PHASE_INCREMENT 0x900
+#define R904_SRC4_PHASE_INCREMENT 0x904
+#define R90C_SRC5_PHASE_INCREMENT 0x90c
+
+enum _cx25843_input {
+ CX25843_UNSET = 0,
+ CX25843_COMPOSITE1,
+ CX25843_TELEVISION,
+ CX25843_SVIDEO,
+ CX25843_RADIO,
+};
+
+#endif
diff --git a/drivers/media/video/empia/cx25843/em28xxfw-cx25843.h b/drivers/media/video/empia/cx25843/em28xxfw-cx25843.h
new file mode 100644
index 0000000..fdcd064
--- /dev/null
+++ b/drivers/media/video/empia/cx25843/em28xxfw-cx25843.h
@@ -0,0 +1,14 @@
+#ifndef __CX25843_FW_HELPER
+#define __CX25843_FW_HELPER
+
+int cx25843_set_firmware(unsigned char *fwblob, int length) {
+ if (length != 16655) {
+ return -EINVAL;
+ }
+ em28xx_cxfirmware=&fwblob[2]; /* length: 16653 */
+ em28xx_cxfirmware_length=length-2; /* ok in that case */
+
+
+ return 0;
+}
+#endif