summaryrefslogtreecommitdiff
path: root/linux/drivers/media/video/pvrusb2/pvrusb2-eeprom.c
diff options
context:
space:
mode:
Diffstat (limited to 'linux/drivers/media/video/pvrusb2/pvrusb2-eeprom.c')
-rw-r--r--linux/drivers/media/video/pvrusb2/pvrusb2-eeprom.c275
1 files changed, 275 insertions, 0 deletions
diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-eeprom.c b/linux/drivers/media/video/pvrusb2/pvrusb2-eeprom.c
new file mode 100644
index 000000000..60ee45ca2
--- /dev/null
+++ b/linux/drivers/media/video/pvrusb2/pvrusb2-eeprom.c
@@ -0,0 +1,275 @@
+/*
+ *
+ * $Id$
+ *
+ * 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. This is the "indirect" method.
+
+ 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. This is the "direct" method.
+
+ 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) is the default choice, but if you want to try method #1,
+ then define PVR2_EEPROM_INDIRECT 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...
+
+ */
+
+
+
+
+/* Stuff common to direct approach of operation tveeprom */
+
+/*
+
+ 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
+
+*/
+
+#include <media/tveeprom.h>
+
+/* We seem to only be interested in the last 128 bytes of the EEPROM */
+#define EEPROM_SIZE 128
+
+/* Grab EEPROM contents, needed for direct method. */
+static u8 *pvr2_eeprom_fetch(struct pvr2_hdw *hdw)
+{
+ struct i2c_msg msg[2];
+ u8 *eeprom;
+ u8 iadd[2];
+ u8 addr;
+ u16 eepromSize;
+ unsigned int offs;
+ int ret;
+ int mode16 = 0;
+ unsigned pcnt,tcnt;
+ eeprom = kmalloc(EEPROM_SIZE,GFP_KERNEL);
+ if (!eeprom) {
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "Failed to allocate memory"
+ " required to read eeprom");
+ return 0;
+ }
+
+ trace_eeprom("Value for eeprom addr from controller was 0x%x",
+ hdw->eeprom_addr);
+ addr = hdw->eeprom_addr;
+ /* Seems that if the high bit is set, then the *real* eeprom
+ address is shifted right now bit position (noticed this in
+ newer PVR USB2 hardware) */
+ if (addr & 0x80) addr >>= 1;
+
+ /* FX2 documentation states that a 16bit-addressed eeprom is
+ expected if the I2C address is an odd number (yeah, this is
+ strange bit it's what they do) */
+ mode16 = (addr & 1);
+ eepromSize = (mode16 ? 4096 : 256);
+ trace_eeprom("Examining %d byte eeprom at location 0x%x"
+ " using %d bit addressing",eepromSize,addr,
+ mode16 ? 16 : 8);
+
+ msg[0].addr = addr;
+ msg[0].flags = 0;
+ msg[0].len = mode16 ? 2 : 1;
+ msg[0].buf = iadd;
+ msg[1].addr = hdw->eeprom_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 + (eepromSize - EEPROM_SIZE);
+ if (mode16) {
+ iadd[0] = offs >> 8;
+ iadd[1] = offs;
+ } else {
+ iadd[0] = offs;
+ }
+ msg[1].len = pcnt;
+ msg[1].buf = eeprom+tcnt;
+ if ((ret = i2c_transfer(
+ &hdw->i2c_adap,
+ msg,sizeof(msg)/sizeof(msg[0]))) != 2) {
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "eeprom fetch set offs err=%d",ret);
+ kfree(eeprom);
+ return 0;
+ }
+ }
+ return eeprom;
+}
+
+
+/*VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV*/
+/* BEGIN DIRECT METHOD, V4L ONLY */
+
+
+/* Directly call eeprom analysis function within tveeprom. This
+ version directly assumes it is talking to the V4L version of
+ tveeprom.ko and does not attempt anything ugly to maintain
+ backwards compatibility. */
+
+int pvr2_eeprom_analyze(struct pvr2_hdw *hdw)
+{
+ u8 *eeprom;
+ struct tveeprom tvdata;
+
+ memset(&tvdata,0,sizeof(tvdata));
+
+ eeprom = pvr2_eeprom_fetch(hdw);
+ if (!eeprom) return -EINVAL;
+
+ {
+ struct i2c_client fake_client;
+ /* Newer version expects a useless client interface */
+ fake_client.addr = hdw->eeprom_addr;
+ fake_client.adapter = &hdw->i2c_adap;
+ tveeprom_hauppauge_analog(&fake_client,&tvdata,eeprom);
+ }
+
+ trace_eeprom("eeprom assumed v4l tveeprom module");
+ trace_eeprom("eeprom direct call results:");
+ trace_eeprom("has_radio=%d",tvdata.has_radio);
+ trace_eeprom("tuner_type=%d",tvdata.tuner_type);
+ trace_eeprom("tuner_formats=0x%x",tvdata.tuner_formats);
+ trace_eeprom("audio_processor=%d",tvdata.audio_processor);
+ trace_eeprom("model=%d",tvdata.model);
+ trace_eeprom("revision=%d",tvdata.revision);
+ trace_eeprom("serial_number=%d",tvdata.serial_number);
+ trace_eeprom("rev_str=%s",tvdata.rev_str);
+ hdw->tuner_type = tvdata.tuner_type;
+ hdw->serial_number = tvdata.serial_number;
+ hdw->video_standards = tvdata.tuner_formats;
+
+ kfree(eeprom);
+
+ return 0;
+}
+
+
+
+/* END DIRECT METHOD, V4L ONLY */
+/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/
+
+
+
+
+
+
+
+static v4l2_std_id std_choices[] = {
+ [PVR2_CVAL_VIDEOSTANDARD_NTSC_M] = V4L2_STD_NTSC_M,
+ [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,
+ [PVR2_CVAL_VIDEOSTANDARD_PAL_M] = V4L2_STD_PAL_M,
+};
+
+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: ***
+ */