summaryrefslogtreecommitdiff
path: root/linux/drivers/media/video/pvrusb2/pvrusb2-eeprom.c
blob: 634095efc627ed3ddede44108a85aa730f7d9b97 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
/*
 *
 *  $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;
	pvr2_hdw_internal_set_std_avail(hdw,tvdata.tuner_formats);

	kfree(eeprom);

	return 0;
}



/* END DIRECT METHOD, V4L ONLY */
/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/







/*
  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: ***
  */