diff options
author | Jonathan Herman <hermanjl@cs.unc.edu> | 2013-01-22 10:38:37 -0500 |
---|---|---|
committer | Jonathan Herman <hermanjl@cs.unc.edu> | 2013-01-22 10:38:37 -0500 |
commit | fcc9d2e5a6c89d22b8b773a64fb4ad21ac318446 (patch) | |
tree | a57612d1888735a2ec7972891b68c1ac5ec8faea /drivers/video/tegra/dc/edid.c | |
parent | 8dea78da5cee153b8af9c07a2745f6c55057fe12 (diff) |
Diffstat (limited to 'drivers/video/tegra/dc/edid.c')
-rw-r--r-- | drivers/video/tegra/dc/edid.c | 619 |
1 files changed, 619 insertions, 0 deletions
diff --git a/drivers/video/tegra/dc/edid.c b/drivers/video/tegra/dc/edid.c new file mode 100644 index 00000000000..fbcf2cc8e37 --- /dev/null +++ b/drivers/video/tegra/dc/edid.c | |||
@@ -0,0 +1,619 @@ | |||
1 | /* | ||
2 | * drivers/video/tegra/dc/edid.c | ||
3 | * | ||
4 | * Copyright (C) 2010 Google, Inc. | ||
5 | * Author: Erik Gilling <konkers@android.com> | ||
6 | * | ||
7 | * Copyright (C) 2010-2011 NVIDIA Corporation | ||
8 | * | ||
9 | * This software is licensed under the terms of the GNU General Public | ||
10 | * License version 2, as published by the Free Software Foundation, and | ||
11 | * may be copied, distributed, and modified under those terms. | ||
12 | * | ||
13 | * This program is distributed in the hope that it will be useful, | ||
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
16 | * GNU General Public License for more details. | ||
17 | * | ||
18 | */ | ||
19 | |||
20 | |||
21 | #include <linux/debugfs.h> | ||
22 | #include <linux/fb.h> | ||
23 | #include <linux/i2c.h> | ||
24 | #include <linux/seq_file.h> | ||
25 | #include <linux/vmalloc.h> | ||
26 | |||
27 | #include "edid.h" | ||
28 | |||
29 | struct tegra_edid_pvt { | ||
30 | struct kref refcnt; | ||
31 | struct tegra_edid_hdmi_eld eld; | ||
32 | bool support_stereo; | ||
33 | bool support_underscan; | ||
34 | /* Note: dc_edid must remain the last member */ | ||
35 | struct tegra_dc_edid dc_edid; | ||
36 | }; | ||
37 | |||
38 | struct tegra_edid { | ||
39 | struct i2c_client *client; | ||
40 | struct i2c_board_info info; | ||
41 | int bus; | ||
42 | |||
43 | struct tegra_edid_pvt *data; | ||
44 | |||
45 | struct mutex lock; | ||
46 | }; | ||
47 | |||
48 | #if defined(DEBUG) || defined(CONFIG_DEBUG_FS) | ||
49 | static int tegra_edid_show(struct seq_file *s, void *unused) | ||
50 | { | ||
51 | struct tegra_edid *edid = s->private; | ||
52 | struct tegra_dc_edid *data; | ||
53 | u8 *buf; | ||
54 | int i; | ||
55 | |||
56 | data = tegra_edid_get_data(edid); | ||
57 | if (!data) { | ||
58 | seq_printf(s, "No EDID\n"); | ||
59 | return 0; | ||
60 | } | ||
61 | |||
62 | buf = data->buf; | ||
63 | |||
64 | for (i = 0; i < data->len; i++) { | ||
65 | if (i % 16 == 0) | ||
66 | seq_printf(s, "edid[%03x] =", i); | ||
67 | |||
68 | seq_printf(s, " %02x", buf[i]); | ||
69 | |||
70 | if (i % 16 == 15) | ||
71 | seq_printf(s, "\n"); | ||
72 | } | ||
73 | |||
74 | tegra_edid_put_data(data); | ||
75 | |||
76 | return 0; | ||
77 | } | ||
78 | #endif | ||
79 | |||
80 | #ifdef CONFIG_DEBUG_FS | ||
81 | static int tegra_edid_debug_open(struct inode *inode, struct file *file) | ||
82 | { | ||
83 | return single_open(file, tegra_edid_show, inode->i_private); | ||
84 | } | ||
85 | |||
86 | static const struct file_operations tegra_edid_debug_fops = { | ||
87 | .open = tegra_edid_debug_open, | ||
88 | .read = seq_read, | ||
89 | .llseek = seq_lseek, | ||
90 | .release = single_release, | ||
91 | }; | ||
92 | |||
93 | void tegra_edid_debug_add(struct tegra_edid *edid) | ||
94 | { | ||
95 | char name[] = "edidX"; | ||
96 | |||
97 | snprintf(name, sizeof(name), "edid%1d", edid->bus); | ||
98 | debugfs_create_file(name, S_IRUGO, NULL, edid, &tegra_edid_debug_fops); | ||
99 | } | ||
100 | #else | ||
101 | void tegra_edid_debug_add(struct tegra_edid *edid) | ||
102 | { | ||
103 | } | ||
104 | #endif | ||
105 | |||
106 | #ifdef DEBUG | ||
107 | static char tegra_edid_dump_buff[16 * 1024]; | ||
108 | |||
109 | static void tegra_edid_dump(struct tegra_edid *edid) | ||
110 | { | ||
111 | struct seq_file s; | ||
112 | int i; | ||
113 | char c; | ||
114 | |||
115 | memset(&s, 0x0, sizeof(s)); | ||
116 | |||
117 | s.buf = tegra_edid_dump_buff; | ||
118 | s.size = sizeof(tegra_edid_dump_buff); | ||
119 | s.private = edid; | ||
120 | |||
121 | tegra_edid_show(&s, NULL); | ||
122 | |||
123 | i = 0; | ||
124 | while (i < s.count ) { | ||
125 | if ((s.count - i) > 256) { | ||
126 | c = s.buf[i + 256]; | ||
127 | s.buf[i + 256] = 0; | ||
128 | printk("%s", s.buf + i); | ||
129 | s.buf[i + 256] = c; | ||
130 | } else { | ||
131 | printk("%s", s.buf + i); | ||
132 | } | ||
133 | i += 256; | ||
134 | } | ||
135 | } | ||
136 | #else | ||
137 | static void tegra_edid_dump(struct tegra_edid *edid) | ||
138 | { | ||
139 | } | ||
140 | #endif | ||
141 | |||
142 | int tegra_edid_read_block(struct tegra_edid *edid, int block, u8 *data) | ||
143 | { | ||
144 | u8 block_buf[] = {block >> 1}; | ||
145 | u8 cmd_buf[] = {(block & 0x1) * 128}; | ||
146 | int status; | ||
147 | struct i2c_msg msg[] = { | ||
148 | { | ||
149 | .addr = 0x30, | ||
150 | .flags = 0, | ||
151 | .len = 1, | ||
152 | .buf = block_buf, | ||
153 | }, | ||
154 | { | ||
155 | .addr = 0x50, | ||
156 | .flags = 0, | ||
157 | .len = 1, | ||
158 | .buf = cmd_buf, | ||
159 | }, | ||
160 | { | ||
161 | .addr = 0x50, | ||
162 | .flags = I2C_M_RD, | ||
163 | .len = 128, | ||
164 | .buf = data, | ||
165 | }}; | ||
166 | struct i2c_msg *m; | ||
167 | int msg_len; | ||
168 | |||
169 | if (block > 1) { | ||
170 | msg_len = 3; | ||
171 | m = msg; | ||
172 | } else { | ||
173 | msg_len = 2; | ||
174 | m = &msg[1]; | ||
175 | } | ||
176 | |||
177 | status = i2c_transfer(edid->client->adapter, m, msg_len); | ||
178 | |||
179 | if (status < 0) | ||
180 | return status; | ||
181 | |||
182 | if (status != msg_len) | ||
183 | return -EIO; | ||
184 | |||
185 | return 0; | ||
186 | } | ||
187 | |||
188 | int tegra_edid_parse_ext_block(const u8 *raw, int idx, | ||
189 | struct tegra_edid_pvt *edid) | ||
190 | { | ||
191 | const u8 *ptr; | ||
192 | u8 tmp; | ||
193 | u8 code; | ||
194 | int len; | ||
195 | int i; | ||
196 | bool basic_audio = false; | ||
197 | |||
198 | ptr = &raw[0]; | ||
199 | |||
200 | /* If CEA 861 block get info for eld struct */ | ||
201 | if (edid && ptr) { | ||
202 | if (*ptr <= 3) | ||
203 | edid->eld.eld_ver = 0x02; | ||
204 | edid->eld.cea_edid_ver = ptr[1]; | ||
205 | |||
206 | /* check for basic audio support in CEA 861 block */ | ||
207 | if(raw[3] & (1<<6)) { | ||
208 | /* For basic audio, set spk_alloc to Left+Right. | ||
209 | * If there is a Speaker Alloc block this will | ||
210 | * get over written with that value */ | ||
211 | basic_audio = true; | ||
212 | } | ||
213 | } | ||
214 | |||
215 | if (raw[3] & 0x80) | ||
216 | edid->support_underscan = 1; | ||
217 | else | ||
218 | edid->support_underscan = 0; | ||
219 | |||
220 | ptr = &raw[4]; | ||
221 | |||
222 | while (ptr < &raw[idx]) { | ||
223 | tmp = *ptr; | ||
224 | len = tmp & 0x1f; | ||
225 | |||
226 | /* HDMI Specification v1.4a, section 8.3.2: | ||
227 | * see Table 8-16 for HDMI VSDB format. | ||
228 | * data blocks have tags in top 3 bits: | ||
229 | * tag code 2: video data block | ||
230 | * tag code 3: vendor specific data block | ||
231 | */ | ||
232 | code = (tmp >> 5) & 0x7; | ||
233 | switch (code) { | ||
234 | case 1: | ||
235 | { | ||
236 | edid->eld.sad_count = len; | ||
237 | edid->eld.conn_type = 0x00; | ||
238 | edid->eld.support_hdcp = 0x00; | ||
239 | for (i = 0; (i < len) && (i < ELD_MAX_SAD); i ++) | ||
240 | edid->eld.sad[i] = ptr[i + 1]; | ||
241 | len++; | ||
242 | ptr += len; /* adding the header */ | ||
243 | /* Got an audio data block so enable audio */ | ||
244 | if(basic_audio == true) | ||
245 | edid->eld.spk_alloc = 1; | ||
246 | break; | ||
247 | } | ||
248 | /* case 2 is commented out for now */ | ||
249 | case 3: | ||
250 | { | ||
251 | int j = 0; | ||
252 | |||
253 | if ((ptr[1] == 0x03) && | ||
254 | (ptr[2] == 0x0c) && | ||
255 | (ptr[3] == 0)) { | ||
256 | edid->eld.port_id[0] = ptr[4]; | ||
257 | edid->eld.port_id[1] = ptr[5]; | ||
258 | } | ||
259 | if ((len >= 8) && | ||
260 | (ptr[1] == 0x03) && | ||
261 | (ptr[2] == 0x0c) && | ||
262 | (ptr[3] == 0)) { | ||
263 | j = 8; | ||
264 | tmp = ptr[j++]; | ||
265 | /* HDMI_Video_present? */ | ||
266 | if (tmp & 0x20) { | ||
267 | /* Latency_Fields_present? */ | ||
268 | if (tmp & 0x80) | ||
269 | j += 2; | ||
270 | /* I_Latency_Fields_present? */ | ||
271 | if (tmp & 0x40) | ||
272 | j += 2; | ||
273 | /* 3D_present? */ | ||
274 | if (j <= len && (ptr[j] & 0x80)) | ||
275 | edid->support_stereo = 1; | ||
276 | } | ||
277 | } | ||
278 | if ((len > 5) && | ||
279 | (ptr[1] == 0x03) && | ||
280 | (ptr[2] == 0x0c) && | ||
281 | (ptr[3] == 0)) { | ||
282 | |||
283 | edid->eld.support_ai = (ptr[6] & 0x80); | ||
284 | } | ||
285 | |||
286 | if ((len > 9) && | ||
287 | (ptr[1] == 0x03) && | ||
288 | (ptr[2] == 0x0c) && | ||
289 | (ptr[3] == 0)) { | ||
290 | |||
291 | edid->eld.aud_synch_delay = ptr[10]; | ||
292 | } | ||
293 | len++; | ||
294 | ptr += len; /* adding the header */ | ||
295 | break; | ||
296 | } | ||
297 | case 4: | ||
298 | { | ||
299 | edid->eld.spk_alloc = ptr[1]; | ||
300 | len++; | ||
301 | ptr += len; /* adding the header */ | ||
302 | break; | ||
303 | } | ||
304 | default: | ||
305 | len++; /* len does not include header */ | ||
306 | ptr += len; | ||
307 | break; | ||
308 | } | ||
309 | } | ||
310 | |||
311 | return 0; | ||
312 | } | ||
313 | |||
314 | int tegra_edid_mode_support_stereo(struct fb_videomode *mode) | ||
315 | { | ||
316 | if (!mode) | ||
317 | return 0; | ||
318 | |||
319 | if (mode->xres == 1280 && | ||
320 | mode->yres == 720 && | ||
321 | ((mode->refresh == 60) || (mode->refresh == 50))) | ||
322 | return 1; | ||
323 | |||
324 | /* Disabling 1080p stereo mode due to bug 869099. */ | ||
325 | /* Must re-enable this to 1 once it is fixed. */ | ||
326 | if (mode->xres == 1920 && mode->yres == 1080 && mode->refresh == 24) | ||
327 | return 0; | ||
328 | |||
329 | return 0; | ||
330 | } | ||
331 | |||
332 | static void data_release(struct kref *ref) | ||
333 | { | ||
334 | struct tegra_edid_pvt *data = | ||
335 | container_of(ref, struct tegra_edid_pvt, refcnt); | ||
336 | vfree(data); | ||
337 | } | ||
338 | |||
339 | int tegra_edid_get_monspecs_test(struct tegra_edid *edid, | ||
340 | struct fb_monspecs *specs, unsigned char *edid_ptr) | ||
341 | { | ||
342 | int i, j, ret; | ||
343 | int extension_blocks; | ||
344 | struct tegra_edid_pvt *new_data, *old_data; | ||
345 | u8 *data; | ||
346 | |||
347 | new_data = vmalloc(SZ_32K + sizeof(struct tegra_edid_pvt)); | ||
348 | if (!new_data) | ||
349 | return -ENOMEM; | ||
350 | |||
351 | kref_init(&new_data->refcnt); | ||
352 | |||
353 | new_data->support_stereo = 0; | ||
354 | new_data->support_underscan = 0; | ||
355 | |||
356 | data = new_data->dc_edid.buf; | ||
357 | memcpy(data, edid_ptr, 128); | ||
358 | |||
359 | memset(specs, 0x0, sizeof(struct fb_monspecs)); | ||
360 | memset(&new_data->eld, 0x0, sizeof(new_data->eld)); | ||
361 | fb_edid_to_monspecs(data, specs); | ||
362 | if (specs->modedb == NULL) { | ||
363 | ret = -EINVAL; | ||
364 | goto fail; | ||
365 | } | ||
366 | |||
367 | memcpy(new_data->eld.monitor_name, specs->monitor, | ||
368 | sizeof(specs->monitor)); | ||
369 | |||
370 | new_data->eld.mnl = strlen(new_data->eld.monitor_name) + 1; | ||
371 | new_data->eld.product_id[0] = data[0x8]; | ||
372 | new_data->eld.product_id[1] = data[0x9]; | ||
373 | new_data->eld.manufacture_id[0] = data[0xA]; | ||
374 | new_data->eld.manufacture_id[1] = data[0xB]; | ||
375 | |||
376 | extension_blocks = data[0x7e]; | ||
377 | for (i = 1; i <= extension_blocks; i++) { | ||
378 | memcpy(data+128, edid_ptr+128, 128); | ||
379 | |||
380 | if (data[i * 128] == 0x2) { | ||
381 | fb_edid_add_monspecs(data + i * 128, specs); | ||
382 | |||
383 | tegra_edid_parse_ext_block(data + i * 128, | ||
384 | data[i * 128 + 2], new_data); | ||
385 | |||
386 | if (new_data->support_stereo) { | ||
387 | for (j = 0; j < specs->modedb_len; j++) { | ||
388 | if (tegra_edid_mode_support_stereo( | ||
389 | &specs->modedb[j])) | ||
390 | specs->modedb[j].vmode |= | ||
391 | #ifndef CONFIG_TEGRA_HDMI_74MHZ_LIMIT | ||
392 | FB_VMODE_STEREO_FRAME_PACK; | ||
393 | #else | ||
394 | FB_VMODE_STEREO_LEFT_RIGHT; | ||
395 | #endif | ||
396 | } | ||
397 | } | ||
398 | } | ||
399 | } | ||
400 | |||
401 | new_data->dc_edid.len = i * 128; | ||
402 | |||
403 | mutex_lock(&edid->lock); | ||
404 | old_data = edid->data; | ||
405 | edid->data = new_data; | ||
406 | mutex_unlock(&edid->lock); | ||
407 | |||
408 | if (old_data) | ||
409 | kref_put(&old_data->refcnt, data_release); | ||
410 | |||
411 | tegra_edid_dump(edid); | ||
412 | return 0; | ||
413 | fail: | ||
414 | vfree(new_data); | ||
415 | return ret; | ||
416 | } | ||
417 | |||
418 | int tegra_edid_get_monspecs(struct tegra_edid *edid, struct fb_monspecs *specs) | ||
419 | { | ||
420 | int i; | ||
421 | int j; | ||
422 | int ret; | ||
423 | int extension_blocks; | ||
424 | struct tegra_edid_pvt *new_data, *old_data; | ||
425 | u8 *data; | ||
426 | |||
427 | new_data = vmalloc(SZ_32K + sizeof(struct tegra_edid_pvt)); | ||
428 | if (!new_data) | ||
429 | return -ENOMEM; | ||
430 | |||
431 | kref_init(&new_data->refcnt); | ||
432 | |||
433 | new_data->support_stereo = 0; | ||
434 | |||
435 | data = new_data->dc_edid.buf; | ||
436 | |||
437 | ret = tegra_edid_read_block(edid, 0, data); | ||
438 | if (ret) | ||
439 | goto fail; | ||
440 | |||
441 | memset(specs, 0x0, sizeof(struct fb_monspecs)); | ||
442 | memset(&new_data->eld, 0x0, sizeof(new_data->eld)); | ||
443 | fb_edid_to_monspecs(data, specs); | ||
444 | if (specs->modedb == NULL) { | ||
445 | ret = -EINVAL; | ||
446 | goto fail; | ||
447 | } | ||
448 | memcpy(new_data->eld.monitor_name, specs->monitor, sizeof(specs->monitor)); | ||
449 | new_data->eld.mnl = strlen(new_data->eld.monitor_name) + 1; | ||
450 | new_data->eld.product_id[0] = data[0x8]; | ||
451 | new_data->eld.product_id[1] = data[0x9]; | ||
452 | new_data->eld.manufacture_id[0] = data[0xA]; | ||
453 | new_data->eld.manufacture_id[1] = data[0xB]; | ||
454 | |||
455 | extension_blocks = data[0x7e]; | ||
456 | |||
457 | for (i = 1; i <= extension_blocks; i++) { | ||
458 | ret = tegra_edid_read_block(edid, i, data + i * 128); | ||
459 | if (ret < 0) | ||
460 | break; | ||
461 | |||
462 | if (data[i * 128] == 0x2) { | ||
463 | fb_edid_add_monspecs(data + i * 128, specs); | ||
464 | |||
465 | tegra_edid_parse_ext_block(data + i * 128, | ||
466 | data[i * 128 + 2], new_data); | ||
467 | |||
468 | if (new_data->support_stereo) { | ||
469 | for (j = 0; j < specs->modedb_len; j++) { | ||
470 | if (tegra_edid_mode_support_stereo( | ||
471 | &specs->modedb[j])) | ||
472 | specs->modedb[j].vmode |= | ||
473 | #ifndef CONFIG_TEGRA_HDMI_74MHZ_LIMIT | ||
474 | FB_VMODE_STEREO_FRAME_PACK; | ||
475 | #else | ||
476 | FB_VMODE_STEREO_LEFT_RIGHT; | ||
477 | #endif | ||
478 | } | ||
479 | } | ||
480 | } | ||
481 | } | ||
482 | |||
483 | new_data->dc_edid.len = i * 128; | ||
484 | |||
485 | mutex_lock(&edid->lock); | ||
486 | old_data = edid->data; | ||
487 | edid->data = new_data; | ||
488 | mutex_unlock(&edid->lock); | ||
489 | |||
490 | if (old_data) | ||
491 | kref_put(&old_data->refcnt, data_release); | ||
492 | |||
493 | tegra_edid_dump(edid); | ||
494 | return 0; | ||
495 | |||
496 | fail: | ||
497 | vfree(new_data); | ||
498 | return ret; | ||
499 | } | ||
500 | |||
501 | int tegra_edid_underscan_supported(struct tegra_edid *edid) | ||
502 | { | ||
503 | if ((!edid) || (!edid->data)) | ||
504 | return 0; | ||
505 | |||
506 | return edid->data->support_underscan; | ||
507 | } | ||
508 | |||
509 | int tegra_edid_get_eld(struct tegra_edid *edid, struct tegra_edid_hdmi_eld *elddata) | ||
510 | { | ||
511 | if (!elddata || !edid->data) | ||
512 | return -EFAULT; | ||
513 | |||
514 | memcpy(elddata,&edid->data->eld,sizeof(struct tegra_edid_hdmi_eld)); | ||
515 | |||
516 | return 0; | ||
517 | } | ||
518 | |||
519 | struct tegra_edid *tegra_edid_create(int bus) | ||
520 | { | ||
521 | struct tegra_edid *edid; | ||
522 | struct i2c_adapter *adapter; | ||
523 | int err; | ||
524 | |||
525 | edid = kzalloc(sizeof(struct tegra_edid), GFP_KERNEL); | ||
526 | if (!edid) | ||
527 | return ERR_PTR(-ENOMEM); | ||
528 | |||
529 | mutex_init(&edid->lock); | ||
530 | strlcpy(edid->info.type, "tegra_edid", sizeof(edid->info.type)); | ||
531 | edid->bus = bus; | ||
532 | edid->info.addr = 0x50; | ||
533 | edid->info.platform_data = edid; | ||
534 | |||
535 | adapter = i2c_get_adapter(bus); | ||
536 | if (!adapter) { | ||
537 | pr_err("can't get adpater for bus %d\n", bus); | ||
538 | err = -EBUSY; | ||
539 | goto free_edid; | ||
540 | } | ||
541 | |||
542 | edid->client = i2c_new_device(adapter, &edid->info); | ||
543 | i2c_put_adapter(adapter); | ||
544 | |||
545 | if (!edid->client) { | ||
546 | pr_err("can't create new device\n"); | ||
547 | err = -EBUSY; | ||
548 | goto free_edid; | ||
549 | } | ||
550 | |||
551 | tegra_edid_debug_add(edid); | ||
552 | |||
553 | return edid; | ||
554 | |||
555 | free_edid: | ||
556 | kfree(edid); | ||
557 | |||
558 | return ERR_PTR(err); | ||
559 | } | ||
560 | |||
561 | void tegra_edid_destroy(struct tegra_edid *edid) | ||
562 | { | ||
563 | i2c_release_client(edid->client); | ||
564 | if (edid->data) | ||
565 | kref_put(&edid->data->refcnt, data_release); | ||
566 | kfree(edid); | ||
567 | } | ||
568 | |||
569 | struct tegra_dc_edid *tegra_edid_get_data(struct tegra_edid *edid) | ||
570 | { | ||
571 | struct tegra_edid_pvt *data; | ||
572 | |||
573 | mutex_lock(&edid->lock); | ||
574 | data = edid->data; | ||
575 | if (data) | ||
576 | kref_get(&data->refcnt); | ||
577 | mutex_unlock(&edid->lock); | ||
578 | |||
579 | return data ? &data->dc_edid : NULL; | ||
580 | } | ||
581 | |||
582 | void tegra_edid_put_data(struct tegra_dc_edid *data) | ||
583 | { | ||
584 | struct tegra_edid_pvt *pvt; | ||
585 | |||
586 | if (!data) | ||
587 | return; | ||
588 | |||
589 | pvt = container_of(data, struct tegra_edid_pvt, dc_edid); | ||
590 | |||
591 | kref_put(&pvt->refcnt, data_release); | ||
592 | } | ||
593 | |||
594 | static const struct i2c_device_id tegra_edid_id[] = { | ||
595 | { "tegra_edid", 0 }, | ||
596 | { } | ||
597 | }; | ||
598 | |||
599 | MODULE_DEVICE_TABLE(i2c, tegra_edid_id); | ||
600 | |||
601 | static struct i2c_driver tegra_edid_driver = { | ||
602 | .id_table = tegra_edid_id, | ||
603 | .driver = { | ||
604 | .name = "tegra_edid", | ||
605 | }, | ||
606 | }; | ||
607 | |||
608 | static int __init tegra_edid_init(void) | ||
609 | { | ||
610 | return i2c_add_driver(&tegra_edid_driver); | ||
611 | } | ||
612 | |||
613 | static void __exit tegra_edid_exit(void) | ||
614 | { | ||
615 | i2c_del_driver(&tegra_edid_driver); | ||
616 | } | ||
617 | |||
618 | module_init(tegra_edid_init); | ||
619 | module_exit(tegra_edid_exit); | ||