xref: /openbmc/linux/drivers/media/usb/pvrusb2/pvrusb2-eeprom.c (revision 976e3645923bdd2fe7893aae33fd7a21098bfb28)
12504ba9fSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
20c0d06caSMauro Carvalho Chehab /*
30c0d06caSMauro Carvalho Chehab  *
40c0d06caSMauro Carvalho Chehab  *  Copyright (C) 2005 Mike Isely <isely@pobox.com>
50c0d06caSMauro Carvalho Chehab  *  Copyright (C) 2004 Aurelien Alleaume <slts@free.fr>
60c0d06caSMauro Carvalho Chehab  */
70c0d06caSMauro Carvalho Chehab 
80c0d06caSMauro Carvalho Chehab #include <linux/slab.h>
90c0d06caSMauro Carvalho Chehab #include "pvrusb2-eeprom.h"
100c0d06caSMauro Carvalho Chehab #include "pvrusb2-hdw-internal.h"
110c0d06caSMauro Carvalho Chehab #include "pvrusb2-debug.h"
120c0d06caSMauro Carvalho Chehab 
130c0d06caSMauro Carvalho Chehab #define trace_eeprom(...) pvr2_trace(PVR2_TRACE_EEPROM,__VA_ARGS__)
140c0d06caSMauro Carvalho Chehab 
150c0d06caSMauro Carvalho Chehab 
160c0d06caSMauro Carvalho Chehab 
170c0d06caSMauro Carvalho Chehab /*
180c0d06caSMauro Carvalho Chehab 
190c0d06caSMauro Carvalho Chehab    Read and analyze data in the eeprom.  Use tveeprom to figure out
200c0d06caSMauro Carvalho Chehab    the packet structure, since this is another Hauppauge device and
210c0d06caSMauro Carvalho Chehab    internally it has a family resemblance to ivtv-type devices
220c0d06caSMauro Carvalho Chehab 
230c0d06caSMauro Carvalho Chehab */
240c0d06caSMauro Carvalho Chehab 
250c0d06caSMauro Carvalho Chehab #include <media/tveeprom.h>
260c0d06caSMauro Carvalho Chehab 
270c0d06caSMauro Carvalho Chehab /* We seem to only be interested in the last 128 bytes of the EEPROM */
280c0d06caSMauro Carvalho Chehab #define EEPROM_SIZE 128
290c0d06caSMauro Carvalho Chehab 
300c0d06caSMauro Carvalho Chehab /* Grab EEPROM contents, needed for direct method. */
pvr2_eeprom_fetch(struct pvr2_hdw * hdw)310c0d06caSMauro Carvalho Chehab static u8 *pvr2_eeprom_fetch(struct pvr2_hdw *hdw)
320c0d06caSMauro Carvalho Chehab {
330c0d06caSMauro Carvalho Chehab 	struct i2c_msg msg[2];
340c0d06caSMauro Carvalho Chehab 	u8 *eeprom;
350c0d06caSMauro Carvalho Chehab 	u8 iadd[2];
360c0d06caSMauro Carvalho Chehab 	u8 addr;
370c0d06caSMauro Carvalho Chehab 	u16 eepromSize;
380c0d06caSMauro Carvalho Chehab 	unsigned int offs;
390c0d06caSMauro Carvalho Chehab 	int ret;
400c0d06caSMauro Carvalho Chehab 	int mode16 = 0;
410c0d06caSMauro Carvalho Chehab 	unsigned pcnt,tcnt;
42*5db8a692SFuqian Huang 	eeprom = kzalloc(EEPROM_SIZE, GFP_KERNEL);
430c0d06caSMauro Carvalho Chehab 	if (!eeprom) {
440c0d06caSMauro Carvalho Chehab 		pvr2_trace(PVR2_TRACE_ERROR_LEGS,
4596292c89SMauro Carvalho Chehab 			   "Failed to allocate memory required to read eeprom");
460c0d06caSMauro Carvalho Chehab 		return NULL;
470c0d06caSMauro Carvalho Chehab 	}
480c0d06caSMauro Carvalho Chehab 
490c0d06caSMauro Carvalho Chehab 	trace_eeprom("Value for eeprom addr from controller was 0x%x",
500c0d06caSMauro Carvalho Chehab 		     hdw->eeprom_addr);
510c0d06caSMauro Carvalho Chehab 	addr = hdw->eeprom_addr;
520c0d06caSMauro Carvalho Chehab 	/* Seems that if the high bit is set, then the *real* eeprom
530c0d06caSMauro Carvalho Chehab 	   address is shifted right now bit position (noticed this in
540c0d06caSMauro Carvalho Chehab 	   newer PVR USB2 hardware) */
550c0d06caSMauro Carvalho Chehab 	if (addr & 0x80) addr >>= 1;
560c0d06caSMauro Carvalho Chehab 
570c0d06caSMauro Carvalho Chehab 	/* FX2 documentation states that a 16bit-addressed eeprom is
580c0d06caSMauro Carvalho Chehab 	   expected if the I2C address is an odd number (yeah, this is
590c0d06caSMauro Carvalho Chehab 	   strange but it's what they do) */
600c0d06caSMauro Carvalho Chehab 	mode16 = (addr & 1);
610c0d06caSMauro Carvalho Chehab 	eepromSize = (mode16 ? 4096 : 256);
6296292c89SMauro Carvalho Chehab 	trace_eeprom("Examining %d byte eeprom at location 0x%x using %d bit addressing",
6396292c89SMauro Carvalho Chehab 		     eepromSize, addr,
640c0d06caSMauro Carvalho Chehab 		     mode16 ? 16 : 8);
650c0d06caSMauro Carvalho Chehab 
660c0d06caSMauro Carvalho Chehab 	msg[0].addr = addr;
670c0d06caSMauro Carvalho Chehab 	msg[0].flags = 0;
680c0d06caSMauro Carvalho Chehab 	msg[0].len = mode16 ? 2 : 1;
690c0d06caSMauro Carvalho Chehab 	msg[0].buf = iadd;
700c0d06caSMauro Carvalho Chehab 	msg[1].addr = addr;
710c0d06caSMauro Carvalho Chehab 	msg[1].flags = I2C_M_RD;
720c0d06caSMauro Carvalho Chehab 
730c0d06caSMauro Carvalho Chehab 	/* We have to do the actual eeprom data fetch ourselves, because
740c0d06caSMauro Carvalho Chehab 	   (1) we're only fetching part of the eeprom, and (2) if we were
750c0d06caSMauro Carvalho Chehab 	   getting the whole thing our I2C driver can't grab it in one
760c0d06caSMauro Carvalho Chehab 	   pass - which is what tveeprom is otherwise going to attempt */
770c0d06caSMauro Carvalho Chehab 	for (tcnt = 0; tcnt < EEPROM_SIZE; tcnt += pcnt) {
780c0d06caSMauro Carvalho Chehab 		pcnt = 16;
790c0d06caSMauro Carvalho Chehab 		if (pcnt + tcnt > EEPROM_SIZE) pcnt = EEPROM_SIZE-tcnt;
800c0d06caSMauro Carvalho Chehab 		offs = tcnt + (eepromSize - EEPROM_SIZE);
810c0d06caSMauro Carvalho Chehab 		if (mode16) {
820c0d06caSMauro Carvalho Chehab 			iadd[0] = offs >> 8;
830c0d06caSMauro Carvalho Chehab 			iadd[1] = offs;
840c0d06caSMauro Carvalho Chehab 		} else {
850c0d06caSMauro Carvalho Chehab 			iadd[0] = offs;
860c0d06caSMauro Carvalho Chehab 		}
870c0d06caSMauro Carvalho Chehab 		msg[1].len = pcnt;
880c0d06caSMauro Carvalho Chehab 		msg[1].buf = eeprom+tcnt;
890c0d06caSMauro Carvalho Chehab 		if ((ret = i2c_transfer(&hdw->i2c_adap,
900c0d06caSMauro Carvalho Chehab 					msg,ARRAY_SIZE(msg))) != 2) {
910c0d06caSMauro Carvalho Chehab 			pvr2_trace(PVR2_TRACE_ERROR_LEGS,
920c0d06caSMauro Carvalho Chehab 				   "eeprom fetch set offs err=%d",ret);
930c0d06caSMauro Carvalho Chehab 			kfree(eeprom);
940c0d06caSMauro Carvalho Chehab 			return NULL;
950c0d06caSMauro Carvalho Chehab 		}
960c0d06caSMauro Carvalho Chehab 	}
970c0d06caSMauro Carvalho Chehab 	return eeprom;
980c0d06caSMauro Carvalho Chehab }
990c0d06caSMauro Carvalho Chehab 
1000c0d06caSMauro Carvalho Chehab 
1010c0d06caSMauro Carvalho Chehab /* Directly call eeprom analysis function within tveeprom. */
pvr2_eeprom_analyze(struct pvr2_hdw * hdw)1020c0d06caSMauro Carvalho Chehab int pvr2_eeprom_analyze(struct pvr2_hdw *hdw)
1030c0d06caSMauro Carvalho Chehab {
1040c0d06caSMauro Carvalho Chehab 	u8 *eeprom;
1050c0d06caSMauro Carvalho Chehab 	struct tveeprom tvdata;
1060c0d06caSMauro Carvalho Chehab 
1070c0d06caSMauro Carvalho Chehab 	memset(&tvdata,0,sizeof(tvdata));
1080c0d06caSMauro Carvalho Chehab 
1090c0d06caSMauro Carvalho Chehab 	eeprom = pvr2_eeprom_fetch(hdw);
1106830733dSArnd Bergmann 	if (!eeprom)
1116830733dSArnd Bergmann 		return -EINVAL;
1120c0d06caSMauro Carvalho Chehab 
113446aba66SMauro Carvalho Chehab 	tveeprom_hauppauge_analog(&tvdata, eeprom);
1140c0d06caSMauro Carvalho Chehab 
1150c0d06caSMauro Carvalho Chehab 	trace_eeprom("eeprom assumed v4l tveeprom module");
1160c0d06caSMauro Carvalho Chehab 	trace_eeprom("eeprom direct call results:");
1170c0d06caSMauro Carvalho Chehab 	trace_eeprom("has_radio=%d",tvdata.has_radio);
1180c0d06caSMauro Carvalho Chehab 	trace_eeprom("tuner_type=%d",tvdata.tuner_type);
1190c0d06caSMauro Carvalho Chehab 	trace_eeprom("tuner_formats=0x%x",tvdata.tuner_formats);
1200c0d06caSMauro Carvalho Chehab 	trace_eeprom("audio_processor=%d",tvdata.audio_processor);
1210c0d06caSMauro Carvalho Chehab 	trace_eeprom("model=%d",tvdata.model);
1220c0d06caSMauro Carvalho Chehab 	trace_eeprom("revision=%d",tvdata.revision);
1230c0d06caSMauro Carvalho Chehab 	trace_eeprom("serial_number=%d",tvdata.serial_number);
1240c0d06caSMauro Carvalho Chehab 	trace_eeprom("rev_str=%s",tvdata.rev_str);
1250c0d06caSMauro Carvalho Chehab 	hdw->tuner_type = tvdata.tuner_type;
1260c0d06caSMauro Carvalho Chehab 	hdw->tuner_updated = !0;
1270c0d06caSMauro Carvalho Chehab 	hdw->serial_number = tvdata.serial_number;
1280c0d06caSMauro Carvalho Chehab 	hdw->std_mask_eeprom = tvdata.tuner_formats;
1290c0d06caSMauro Carvalho Chehab 
1300c0d06caSMauro Carvalho Chehab 	kfree(eeprom);
1310c0d06caSMauro Carvalho Chehab 
1320c0d06caSMauro Carvalho Chehab 	return 0;
1330c0d06caSMauro Carvalho Chehab }
134