summaryrefslogtreecommitdiff
path: root/v4l_experimental/pvrusb2/pvrusb2-eeprom.c
diff options
context:
space:
mode:
Diffstat (limited to 'v4l_experimental/pvrusb2/pvrusb2-eeprom.c')
-rw-r--r--v4l_experimental/pvrusb2/pvrusb2-eeprom.c329
1 files changed, 329 insertions, 0 deletions
diff --git a/v4l_experimental/pvrusb2/pvrusb2-eeprom.c b/v4l_experimental/pvrusb2/pvrusb2-eeprom.c
new file mode 100644
index 000000000..e83ef0db7
--- /dev/null
+++ b/v4l_experimental/pvrusb2/pvrusb2-eeprom.c
@@ -0,0 +1,329 @@
+/*
+ *
+ * $Id: pvrusb2-eeprom.c,v 1.1 2005/11/14 13:31:24 mchehab Exp $
+ *
+ * Copyright (C) 2005 Mike Isely <isely@pobox.com>
+ * Copyright (C) 2004 Aurelien Alleaume <slts@free.fr>
+ *
+ * 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
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include "pvrusb2-eeprom.h"
+#include "pvrusb2-hdw-internal.h"
+#include "pvrusb2-debug.h"
+#include "compat.h"
+
+#define trace_eeprom(...) pvr2_trace(PVR2_TRACE_EEPROM,__VA_ARGS__)
+
+/*
+
+ isely@pobox.com 16-Oct-2005 - There are two method by which we can
+ theoretically retrieve information from the device's eeprom:
+
+ Method #1: We expect tveeprom to attach to our I2C adapter as a
+ client, in which case we send it a command to tell us what it
+ knows about the device.
+
+ Method #2: We retrieve the eeprom contents ourselves and call into
+ tveeprom_hauppauge_analog() to parse the data and tell us what
+ it knows about the device.
+
+ Unfortunately it isn't perfectly clear which method is the best.
+ They each have pros and cons:
+
+ #1 is simpler & more portable and has an API which is more stable.
+
+ #1 doesn't provide as much information as #2 does. For example, we
+ can't retrieve the device's serial number with method #1.
+
+ #1 requires that tveeprom.ko autonomously detect the eeprom chip on
+ its own; we can't help it out here. Worse still, it seems that
+ the eeprom in some PVR USB2 devices (like mine) can't be detected
+ correctly (I don't see an ack on a zero length write which is
+ what the I2C core attempts).
+
+ #2 uses an unstable API. Current the ivtv implementation of #2 uses
+ a completely different tveeprom struct than the v4l
+ implementation of #2. This causes a usability nightmare.
+
+ Since I can't decide, both methods are implemented below. Method #2
+ (direct call) is the default choice, but if you want to try method
+ #1, comment out the PVRUSB2_EEPROM_DIRECT_CALL macro and cross your
+ fingers...
+
+ If you use method #1, please be aware that you won't have a serial
+ number for the device and thus the sysfs interface may be a little
+ different. In addition, if tveeprom.ko fails to detect the eeprom
+ you may have to force it using standard i2c module options (try
+ force=-1,80). FINALLY (and this may foreclose this option for you
+ completely), the PVR USB2 eeprom seems to have valid data only in
+ the upper 128 bytes - the lower 128 bytes causes tveeprom.ko to
+ abort. In method #2 we only read the upper 128 bytes...
+
+ */
+
+#define PVRUSB2_EEPROM_DIRECT_CALL
+
+
+#ifndef PVRUSB2_EEPROM_DIRECT_CALL
+
+#include "pvrusb2-i2c.h"
+
+/* Use I2C client interface to retrieve usable information from within
+ * tveeprom. This tends to be more stable than directly calling. */
+
+int pvr2_eeprom_analyze(struct pvr2_hdw *hdw)
+{
+ u32 props[5];
+ int stat;
+
+ stat = pvr2_i2c_tveeprom_cmd(hdw,0,props);
+ if (stat < 0) {
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "Failed to retrieve eeprom data stat=%d",stat);
+ return stat;
+ }
+
+ trace_eeprom("eeprom client query results:");
+ trace_eeprom("tuner_type=%d",props[0]);
+ trace_eeprom("tuner_formats=0x%x",props[1]);
+ trace_eeprom("model=%d",props[2]);
+ trace_eeprom("revision=%d",props[3]);
+ trace_eeprom("has_radio=%d",props[4]);
+
+ hdw->tuner_type = props[0];
+ hdw->video_standards = props[1];
+
+ return 0;
+}
+
+#endif
+
+#ifdef PVRUSB2_EEPROM_DIRECT_CALL
+
+/* Directly call eeprom analysis function within tveeprom. This has
+ portability issues because the internal API has been changing. We
+ have to do something positively gross here. The two variants of
+ tveeprom that we deal with (ivtv and v4l) use completely different
+ definitions for the tveeprom structure. To accomodate this, we'll
+ allocate enough storage for the larger of the two, initialize it to
+ bad but predictable data, and then call the analysis function.
+ Upon return, we'll check how much data was changed and use that as
+ a hint to determine exactly which tveeprom structure had been
+ used. Did I say this was ugly? It's disgusting. */
+
+#define PVR_EEPROM_I2C_ADDR 0x50
+
+#include <media/tveeprom.h>
+
+/*
+ * Read and analyze data in the eeprom. Use tveeprom to figure out
+ * the packet structure, since this is another Hauppauge device and
+ * internally it has a family resemblence to ivtv-type devices
+ */
+
+/* We seem to only be interested in the back half of the EEPROM */
+#define EEPROM_SIZE 128
+#define EEPROM_OFFS 128
+
+/* This has to be an EXACT(!!) match with the tveeprom structure
+ defined in our local copy of tveeprom.c. */
+struct tveeprom_ivtv {
+ u32 has_radio;
+
+ u32 tuner_type;
+ u32 tuner_formats;
+
+ u32 digitizer;
+ u32 digitizer_formats;
+
+ u32 audio_processor;
+ u32 decoder_processor;
+ /* a_p_fmts? */
+
+ u32 model;
+ u32 revision;
+ u32 serial_number;
+ char rev_str[5];
+};
+
+
+int pvr2_eeprom_analyze(struct pvr2_hdw *hdw)
+{
+ u8 *eeprom;
+ u8 offs;
+ unsigned pcnt,tcnt;
+ int ret;
+ int tp;
+ struct i2c_msg msg[2];
+ union {
+ struct tveeprom v4l;
+ struct tveeprom_ivtv ivtv;
+ } tvdata;
+
+ memset(&tvdata,0x93,sizeof(tvdata));
+
+ eeprom = kmalloc(EEPROM_SIZE,GFP_KERNEL);
+ if (!eeprom) {
+ return -ENOMEM;
+ }
+
+ msg[0].addr = PVR_EEPROM_I2C_ADDR;
+ msg[0].flags = 0;
+ msg[0].len = 1;
+ msg[0].buf = &offs;
+ msg[1].addr = PVR_EEPROM_I2C_ADDR;
+ msg[1].flags = I2C_M_RD;
+
+ /* We have to do the actual eeprom data fetch ourselves, because
+ (1) we're only fetching part of the eeprom, and (2) if we were
+ getting the whole thing our I2C driver can't grab it in one
+ pass - which is what tveeprom is otherwise going to attempt */
+ memset(eeprom,0,EEPROM_SIZE);
+ for (tcnt = 0; tcnt < EEPROM_SIZE; tcnt += pcnt) {
+ pcnt = 16;
+ if (pcnt + tcnt > EEPROM_SIZE) pcnt = EEPROM_SIZE-tcnt;
+ offs = tcnt + EEPROM_OFFS;
+ msg[1].len = pcnt;
+ msg[1].buf = eeprom+tcnt;
+ if ((ret = i2c_transfer(
+ &hdw->i2c_adap,
+ msg,sizeof(msg)/sizeof(msg[0]))) != 2) {
+ trace_eeprom("eeprom fetch set offs err=%d",ret);
+ if (ret > 0) ret = -EIO;
+ kfree(eeprom);
+ return ret;
+ }
+ }
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,14)
+ {
+ struct i2c_client fake_client;
+ /* Newer version expects a useless client interface */
+ fake_client.addr = PVR_EEPROM_I2C_ADDR;
+ fake_client.adapter = &hdw->i2c_adap;
+ tveeprom_hauppauge_analog(&fake_client,
+ (struct tveeprom *)&tvdata,eeprom);
+ }
+#else
+ tveeprom_hauppauge_analog((struct tveeprom *)&tvdata,eeprom);
+#endif
+
+ /* Now figure out which structure was used */
+ for (tcnt = 0; tcnt < sizeof(tvdata); tcnt++) {
+ if (((unsigned char *)(&tvdata))[sizeof(tvdata) - (tcnt+1)] !=
+ 0x93) {
+ break;
+ }
+ }
+ tcnt = sizeof(tvdata) - tcnt;
+
+ if (sizeof(tvdata.ivtv) < sizeof(tvdata.v4l)) {
+ tp = 0;
+ if (tcnt > sizeof(tvdata.ivtv)) tp = 1;
+ } else {
+ tp = 1;
+ if (tcnt > sizeof(tvdata.v4l)) tp = 0;
+ }
+
+ if (tp) {
+ /* v4l */
+ trace_eeprom("eeprom detected v4l tveeprom module");
+ trace_eeprom("eeprom direct call results:");
+ trace_eeprom("has_radio=%d",tvdata.v4l.has_radio);
+ trace_eeprom("tuner_type=%d",tvdata.v4l.tuner_type);
+ trace_eeprom("tuner_formats=0x%x",tvdata.v4l.tuner_formats);
+ trace_eeprom("audio_processor=%d",tvdata.v4l.audio_processor);
+ trace_eeprom("model=%d",tvdata.v4l.model);
+ trace_eeprom("revision=%d",tvdata.v4l.revision);
+ trace_eeprom("serial_number=%d",tvdata.v4l.serial_number);
+ trace_eeprom("rev_str=%s",tvdata.v4l.rev_str);
+ hdw->tuner_type = tvdata.v4l.tuner_type;
+ hdw->serial_number = tvdata.v4l.serial_number;
+ hdw->video_standards = tvdata.v4l.tuner_formats;
+ } else {
+ /* ivtv */
+ trace_eeprom("eeprom detected ivtv tveeprom module");
+ trace_eeprom("eeprom direct call results:");
+ trace_eeprom("has_radio=%d",tvdata.ivtv.has_radio);
+ trace_eeprom("tuner_type=%d",tvdata.ivtv.tuner_type);
+ trace_eeprom("tuner_formats=0x%x",tvdata.ivtv.tuner_formats);
+ trace_eeprom("audio_processor=%d",tvdata.ivtv.audio_processor);
+ trace_eeprom("model=%d",tvdata.ivtv.model);
+ trace_eeprom("revision=%d",tvdata.ivtv.revision);
+ trace_eeprom("serial_number=%d",tvdata.ivtv.serial_number);
+ trace_eeprom("rev_str=%s",tvdata.ivtv.rev_str);
+ hdw->tuner_type = tvdata.ivtv.tuner_type;
+ hdw->serial_number = tvdata.ivtv.serial_number;
+ hdw->video_standards = tvdata.ivtv.tuner_formats;
+ }
+
+ kfree(eeprom);
+
+ return 0;
+}
+#endif
+
+
+static v4l2_std_id std_choices[] = {
+ [PVR2_CVAL_VIDEOSTANDARD_NTSC_M] = V4L2_STD_NTSC,
+ [PVR2_CVAL_VIDEOSTANDARD_PAL_BG] = V4L2_STD_PAL_BG,
+ [PVR2_CVAL_VIDEOSTANDARD_PAL_I] = V4L2_STD_PAL_I,
+ [PVR2_CVAL_VIDEOSTANDARD_PAL_DK] = V4L2_STD_PAL_DK,
+ [PVR2_CVAL_VIDEOSTANDARD_SECAM_L] = V4L2_STD_SECAM_L,
+};
+
+void pvr2_eeprom_set_default_standard(struct pvr2_hdw *hdw)
+{
+ int vstd_value = 0;
+ int vstd_found = 0;
+ unsigned int idx;
+ v4l2_std_id vs = (v4l2_std_id)hdw->video_standards;
+
+ for (idx = 0; idx < sizeof(std_choices)/sizeof(std_choices[0]);
+ idx++) {
+ if (!(vs & std_choices[idx])) continue;
+ trace_eeprom("Detected video standard %s (from eeprom)",
+ pvr2_hdw_get_ctl_value_name(
+ hdw,PVR2_CID_VIDEOSTANDARD,idx));
+ if (vstd_found) continue;
+ vstd_value = idx;
+ vstd_found = !0;
+ }
+
+ if (!vstd_found) {
+ trace_eeprom("eeprom unable to recognize"
+ " a known video standard");
+ return;
+ }
+
+ trace_eeprom("Setting initial video standard to %s"
+ " (detected from eeprom)",
+ pvr2_hdw_get_ctl_value_name(hdw,
+ PVR2_CID_VIDEOSTANDARD,
+ vstd_value));
+ pvr2_hdw_set_ctl_value_internal(hdw,PVR2_CID_VIDEOSTANDARD,vstd_value);
+}
+
+
+/*
+ Stuff for Emacs to see, in order to encourage consistent editing style:
+ *** Local Variables: ***
+ *** mode: c ***
+ *** fill-column: 70 ***
+ *** tab-width: 8 ***
+ *** c-basic-offset: 8 ***
+ *** End: ***
+ */