diff options
Diffstat (limited to 'drivers/media/video/cx25840/cx25840-core.c')
-rw-r--r-- | drivers/media/video/cx25840/cx25840-core.c | 306 |
1 files changed, 152 insertions, 154 deletions
diff --git a/drivers/media/video/cx25840/cx25840-core.c b/drivers/media/video/cx25840/cx25840-core.c index a961bb2ab0fd..5c2036b40ea1 100644 --- a/drivers/media/video/cx25840/cx25840-core.c +++ b/drivers/media/video/cx25840/cx25840-core.c | |||
@@ -10,6 +10,9 @@ | |||
10 | * | 10 | * |
11 | * VBI support by Hans Verkuil <hverkuil@xs4all.nl>. | 11 | * VBI support by Hans Verkuil <hverkuil@xs4all.nl>. |
12 | * | 12 | * |
13 | * NTSC sliced VBI support by Christopher Neufeld <television@cneufeld.ca> | ||
14 | * with additional fixes by Hans Verkuil <hverkuil@xs4all.nl>. | ||
15 | * | ||
13 | * This program is free software; you can redistribute it and/or | 16 | * This program is free software; you can redistribute it and/or |
14 | * modify it under the terms of the GNU General Public License | 17 | * modify it under the terms of the GNU General Public License |
15 | * as published by the Free Software Foundation; either version 2 | 18 | * as published by the Free Software Foundation; either version 2 |
@@ -43,7 +46,7 @@ MODULE_LICENSE("GPL"); | |||
43 | static unsigned short normal_i2c[] = { 0x88 >> 1, I2C_CLIENT_END }; | 46 | static unsigned short normal_i2c[] = { 0x88 >> 1, I2C_CLIENT_END }; |
44 | 47 | ||
45 | 48 | ||
46 | static int cx25840_debug; | 49 | int cx25840_debug; |
47 | 50 | ||
48 | module_param_named(debug,cx25840_debug, int, 0644); | 51 | module_param_named(debug,cx25840_debug, int, 0644); |
49 | 52 | ||
@@ -105,7 +108,7 @@ u32 cx25840_read4(struct i2c_client * client, u16 addr) | |||
105 | (buffer[2] << 8) | buffer[3]; | 108 | (buffer[2] << 8) | buffer[3]; |
106 | } | 109 | } |
107 | 110 | ||
108 | int cx25840_and_or(struct i2c_client *client, u16 addr, u8 and_mask, | 111 | int cx25840_and_or(struct i2c_client *client, u16 addr, unsigned and_mask, |
109 | u8 or_value) | 112 | u8 or_value) |
110 | { | 113 | { |
111 | return cx25840_write(client, addr, | 114 | return cx25840_write(client, addr, |
@@ -117,7 +120,8 @@ int cx25840_and_or(struct i2c_client *client, u16 addr, u8 and_mask, | |||
117 | 120 | ||
118 | static int set_input(struct i2c_client *client, enum cx25840_video_input vid_input, | 121 | static int set_input(struct i2c_client *client, enum cx25840_video_input vid_input, |
119 | enum cx25840_audio_input aud_input); | 122 | enum cx25840_audio_input aud_input); |
120 | static void log_status(struct i2c_client *client); | 123 | static void log_audio_status(struct i2c_client *client); |
124 | static void log_video_status(struct i2c_client *client); | ||
121 | 125 | ||
122 | /* ----------------------------------------------------------------------- */ | 126 | /* ----------------------------------------------------------------------- */ |
123 | 127 | ||
@@ -147,6 +151,33 @@ static void init_dll2(struct i2c_client *client) | |||
147 | cx25840_write(client, 0x15d, 0xe1); | 151 | cx25840_write(client, 0x15d, 0xe1); |
148 | } | 152 | } |
149 | 153 | ||
154 | static void cx25836_initialize(struct i2c_client *client) | ||
155 | { | ||
156 | /* reset configuration is described on page 3-77 of the CX25836 datasheet */ | ||
157 | /* 2. */ | ||
158 | cx25840_and_or(client, 0x000, ~0x01, 0x01); | ||
159 | cx25840_and_or(client, 0x000, ~0x01, 0x00); | ||
160 | /* 3a. */ | ||
161 | cx25840_and_or(client, 0x15a, ~0x70, 0x00); | ||
162 | /* 3b. */ | ||
163 | cx25840_and_or(client, 0x15b, ~0x1e, 0x06); | ||
164 | /* 3c. */ | ||
165 | cx25840_and_or(client, 0x159, ~0x02, 0x02); | ||
166 | /* 3d. */ | ||
167 | /* There should be a 10-us delay here, but since the | ||
168 | i2c bus already has a 10-us delay we don't need to do | ||
169 | anything */ | ||
170 | /* 3e. */ | ||
171 | cx25840_and_or(client, 0x159, ~0x02, 0x00); | ||
172 | /* 3f. */ | ||
173 | cx25840_and_or(client, 0x159, ~0xc0, 0xc0); | ||
174 | /* 3g. */ | ||
175 | cx25840_and_or(client, 0x159, ~0x01, 0x00); | ||
176 | cx25840_and_or(client, 0x159, ~0x01, 0x01); | ||
177 | /* 3h. */ | ||
178 | cx25840_and_or(client, 0x15b, ~0x1e, 0x10); | ||
179 | } | ||
180 | |||
150 | static void cx25840_initialize(struct i2c_client *client, int loadfw) | 181 | static void cx25840_initialize(struct i2c_client *client, int loadfw) |
151 | { | 182 | { |
152 | struct cx25840_state *state = i2c_get_clientdata(client); | 183 | struct cx25840_state *state = i2c_get_clientdata(client); |
@@ -220,17 +251,7 @@ static void input_change(struct i2c_client *client) | |||
220 | cx25840_and_or(client, 0x401, ~0x60, 0); | 251 | cx25840_and_or(client, 0x401, ~0x60, 0); |
221 | cx25840_and_or(client, 0x401, ~0x60, 0x60); | 252 | cx25840_and_or(client, 0x401, ~0x60, 0x60); |
222 | 253 | ||
223 | /* Note: perhaps V4L2_STD_PAL_M should be handled as V4L2_STD_NTSC | 254 | if (std & V4L2_STD_525_60) { |
224 | instead of V4L2_STD_PAL. Someone needs to test this. */ | ||
225 | if (std & V4L2_STD_PAL) { | ||
226 | /* Follow tuner change procedure for PAL */ | ||
227 | cx25840_write(client, 0x808, 0xff); | ||
228 | cx25840_write(client, 0x80b, 0x10); | ||
229 | } else if (std & V4L2_STD_SECAM) { | ||
230 | /* Select autodetect for SECAM */ | ||
231 | cx25840_write(client, 0x808, 0xff); | ||
232 | cx25840_write(client, 0x80b, 0x10); | ||
233 | } else if (std & V4L2_STD_NTSC) { | ||
234 | /* Certain Hauppauge PVR150 models have a hardware bug | 255 | /* Certain Hauppauge PVR150 models have a hardware bug |
235 | that causes audio to drop out. For these models the | 256 | that causes audio to drop out. For these models the |
236 | audio standard must be set explicitly. | 257 | audio standard must be set explicitly. |
@@ -249,6 +270,14 @@ static void input_change(struct i2c_client *client) | |||
249 | cx25840_write(client, 0x808, hw_fix ? 0x1f : 0xf6); | 270 | cx25840_write(client, 0x808, hw_fix ? 0x1f : 0xf6); |
250 | } | 271 | } |
251 | cx25840_write(client, 0x80b, 0x00); | 272 | cx25840_write(client, 0x80b, 0x00); |
273 | } else if (std & V4L2_STD_PAL) { | ||
274 | /* Follow tuner change procedure for PAL */ | ||
275 | cx25840_write(client, 0x808, 0xff); | ||
276 | cx25840_write(client, 0x80b, 0x10); | ||
277 | } else if (std & V4L2_STD_SECAM) { | ||
278 | /* Select autodetect for SECAM */ | ||
279 | cx25840_write(client, 0x808, 0xff); | ||
280 | cx25840_write(client, 0x80b, 0x10); | ||
252 | } | 281 | } |
253 | 282 | ||
254 | if (cx25840_read(client, 0x803) & 0x10) { | 283 | if (cx25840_read(client, 0x803) & 0x10) { |
@@ -319,8 +348,10 @@ static int set_input(struct i2c_client *client, enum cx25840_video_input vid_inp | |||
319 | 348 | ||
320 | state->vid_input = vid_input; | 349 | state->vid_input = vid_input; |
321 | state->aud_input = aud_input; | 350 | state->aud_input = aud_input; |
322 | cx25840_audio_set_path(client); | 351 | if (!state->is_cx25836) { |
323 | input_change(client); | 352 | cx25840_audio_set_path(client); |
353 | input_change(client); | ||
354 | } | ||
324 | return 0; | 355 | return 0; |
325 | } | 356 | } |
326 | 357 | ||
@@ -354,6 +385,8 @@ static int set_v4lstd(struct i2c_client *client, v4l2_std_id std) | |||
354 | } | 385 | } |
355 | } | 386 | } |
356 | 387 | ||
388 | v4l_dbg(1, cx25840_debug, client, "changing video std to fmt %i\n",fmt); | ||
389 | |||
357 | /* Follow step 9 of section 3.16 in the cx25840 datasheet. | 390 | /* Follow step 9 of section 3.16 in the cx25840 datasheet. |
358 | Without this PAL may display a vertical ghosting effect. | 391 | Without this PAL may display a vertical ghosting effect. |
359 | This happens for example with the Yuan MPC622. */ | 392 | This happens for example with the Yuan MPC622. */ |
@@ -370,6 +403,7 @@ static int set_v4lstd(struct i2c_client *client, v4l2_std_id std) | |||
370 | 403 | ||
371 | v4l2_std_id cx25840_get_v4lstd(struct i2c_client * client) | 404 | v4l2_std_id cx25840_get_v4lstd(struct i2c_client * client) |
372 | { | 405 | { |
406 | struct cx25840_state *state = i2c_get_clientdata(client); | ||
373 | /* check VID_FMT_SEL first */ | 407 | /* check VID_FMT_SEL first */ |
374 | u8 fmt = cx25840_read(client, 0x400) & 0xf; | 408 | u8 fmt = cx25840_read(client, 0x400) & 0xf; |
375 | 409 | ||
@@ -383,7 +417,7 @@ v4l2_std_id cx25840_get_v4lstd(struct i2c_client * client) | |||
383 | { | 417 | { |
384 | /* if the audio std is A2-M, then this is the South Korean | 418 | /* if the audio std is A2-M, then this is the South Korean |
385 | NTSC standard */ | 419 | NTSC standard */ |
386 | if (cx25840_read(client, 0x805) == 2) | 420 | if (!state->is_cx25836 && cx25840_read(client, 0x805) == 2) |
387 | return V4L2_STD_NTSC_M_KR; | 421 | return V4L2_STD_NTSC_M_KR; |
388 | return V4L2_STD_NTSC_M; | 422 | return V4L2_STD_NTSC_M; |
389 | } | 423 | } |
@@ -456,6 +490,8 @@ static int set_v4lctrl(struct i2c_client *client, struct v4l2_control *ctrl) | |||
456 | case V4L2_CID_AUDIO_TREBLE: | 490 | case V4L2_CID_AUDIO_TREBLE: |
457 | case V4L2_CID_AUDIO_BALANCE: | 491 | case V4L2_CID_AUDIO_BALANCE: |
458 | case V4L2_CID_AUDIO_MUTE: | 492 | case V4L2_CID_AUDIO_MUTE: |
493 | if (state->is_cx25836) | ||
494 | return -EINVAL; | ||
459 | return cx25840_audio(client, VIDIOC_S_CTRL, ctrl); | 495 | return cx25840_audio(client, VIDIOC_S_CTRL, ctrl); |
460 | 496 | ||
461 | default: | 497 | default: |
@@ -490,6 +526,8 @@ static int get_v4lctrl(struct i2c_client *client, struct v4l2_control *ctrl) | |||
490 | case V4L2_CID_AUDIO_TREBLE: | 526 | case V4L2_CID_AUDIO_TREBLE: |
491 | case V4L2_CID_AUDIO_BALANCE: | 527 | case V4L2_CID_AUDIO_BALANCE: |
492 | case V4L2_CID_AUDIO_MUTE: | 528 | case V4L2_CID_AUDIO_MUTE: |
529 | if (state->is_cx25836) | ||
530 | return -EINVAL; | ||
493 | return cx25840_audio(client, VIDIOC_G_CTRL, ctrl); | 531 | return cx25840_audio(client, VIDIOC_G_CTRL, ctrl); |
494 | default: | 532 | default: |
495 | return -EINVAL; | 533 | return -EINVAL; |
@@ -579,91 +617,6 @@ static int set_v4lfmt(struct i2c_client *client, struct v4l2_format *fmt) | |||
579 | 617 | ||
580 | /* ----------------------------------------------------------------------- */ | 618 | /* ----------------------------------------------------------------------- */ |
581 | 619 | ||
582 | static struct v4l2_queryctrl cx25840_qctrl[] = { | ||
583 | { | ||
584 | .id = V4L2_CID_BRIGHTNESS, | ||
585 | .type = V4L2_CTRL_TYPE_INTEGER, | ||
586 | .name = "Brightness", | ||
587 | .minimum = 0, | ||
588 | .maximum = 255, | ||
589 | .step = 1, | ||
590 | .default_value = 128, | ||
591 | .flags = 0, | ||
592 | }, { | ||
593 | .id = V4L2_CID_CONTRAST, | ||
594 | .type = V4L2_CTRL_TYPE_INTEGER, | ||
595 | .name = "Contrast", | ||
596 | .minimum = 0, | ||
597 | .maximum = 127, | ||
598 | .step = 1, | ||
599 | .default_value = 64, | ||
600 | .flags = 0, | ||
601 | }, { | ||
602 | .id = V4L2_CID_SATURATION, | ||
603 | .type = V4L2_CTRL_TYPE_INTEGER, | ||
604 | .name = "Saturation", | ||
605 | .minimum = 0, | ||
606 | .maximum = 127, | ||
607 | .step = 1, | ||
608 | .default_value = 64, | ||
609 | .flags = 0, | ||
610 | }, { | ||
611 | .id = V4L2_CID_HUE, | ||
612 | .type = V4L2_CTRL_TYPE_INTEGER, | ||
613 | .name = "Hue", | ||
614 | .minimum = -128, | ||
615 | .maximum = 127, | ||
616 | .step = 1, | ||
617 | .default_value = 0, | ||
618 | .flags = 0, | ||
619 | }, { | ||
620 | .id = V4L2_CID_AUDIO_VOLUME, | ||
621 | .type = V4L2_CTRL_TYPE_INTEGER, | ||
622 | .name = "Volume", | ||
623 | .minimum = 0, | ||
624 | .maximum = 65535, | ||
625 | .step = 65535/100, | ||
626 | .default_value = 58880, | ||
627 | .flags = 0, | ||
628 | }, { | ||
629 | .id = V4L2_CID_AUDIO_BALANCE, | ||
630 | .type = V4L2_CTRL_TYPE_INTEGER, | ||
631 | .name = "Balance", | ||
632 | .minimum = 0, | ||
633 | .maximum = 65535, | ||
634 | .step = 65535/100, | ||
635 | .default_value = 32768, | ||
636 | .flags = 0, | ||
637 | }, { | ||
638 | .id = V4L2_CID_AUDIO_MUTE, | ||
639 | .type = V4L2_CTRL_TYPE_BOOLEAN, | ||
640 | .name = "Mute", | ||
641 | .minimum = 0, | ||
642 | .maximum = 1, | ||
643 | .step = 1, | ||
644 | .default_value = 1, | ||
645 | .flags = 0, | ||
646 | }, { | ||
647 | .id = V4L2_CID_AUDIO_BASS, | ||
648 | .type = V4L2_CTRL_TYPE_INTEGER, | ||
649 | .name = "Bass", | ||
650 | .minimum = 0, | ||
651 | .maximum = 65535, | ||
652 | .step = 65535/100, | ||
653 | .default_value = 32768, | ||
654 | }, { | ||
655 | .id = V4L2_CID_AUDIO_TREBLE, | ||
656 | .type = V4L2_CTRL_TYPE_INTEGER, | ||
657 | .name = "Treble", | ||
658 | .minimum = 0, | ||
659 | .maximum = 65535, | ||
660 | .step = 65535/100, | ||
661 | .default_value = 32768, | ||
662 | }, | ||
663 | }; | ||
664 | |||
665 | /* ----------------------------------------------------------------------- */ | ||
666 | |||
667 | static int cx25840_command(struct i2c_client *client, unsigned int cmd, | 620 | static int cx25840_command(struct i2c_client *client, unsigned int cmd, |
668 | void *arg) | 621 | void *arg) |
669 | { | 622 | { |
@@ -706,8 +659,8 @@ static int cx25840_command(struct i2c_client *client, unsigned int cmd, | |||
706 | 659 | ||
707 | case VIDIOC_STREAMON: | 660 | case VIDIOC_STREAMON: |
708 | v4l_dbg(1, cx25840_debug, client, "enable output\n"); | 661 | v4l_dbg(1, cx25840_debug, client, "enable output\n"); |
709 | cx25840_write(client, 0x115, 0x8c); | 662 | cx25840_write(client, 0x115, state->is_cx25836 ? 0x0c : 0x8c); |
710 | cx25840_write(client, 0x116, 0x07); | 663 | cx25840_write(client, 0x116, state->is_cx25836 ? 0x04 : 0x07); |
711 | break; | 664 | break; |
712 | 665 | ||
713 | case VIDIOC_STREAMOFF: | 666 | case VIDIOC_STREAMOFF: |
@@ -717,7 +670,9 @@ static int cx25840_command(struct i2c_client *client, unsigned int cmd, | |||
717 | break; | 670 | break; |
718 | 671 | ||
719 | case VIDIOC_LOG_STATUS: | 672 | case VIDIOC_LOG_STATUS: |
720 | log_status(client); | 673 | log_video_status(client); |
674 | if (!state->is_cx25836) | ||
675 | log_audio_status(client); | ||
721 | break; | 676 | break; |
722 | 677 | ||
723 | case VIDIOC_G_CTRL: | 678 | case VIDIOC_G_CTRL: |
@@ -729,13 +684,29 @@ static int cx25840_command(struct i2c_client *client, unsigned int cmd, | |||
729 | case VIDIOC_QUERYCTRL: | 684 | case VIDIOC_QUERYCTRL: |
730 | { | 685 | { |
731 | struct v4l2_queryctrl *qc = arg; | 686 | struct v4l2_queryctrl *qc = arg; |
732 | int i; | ||
733 | 687 | ||
734 | for (i = 0; i < ARRAY_SIZE(cx25840_qctrl); i++) | 688 | switch (qc->id) { |
735 | if (qc->id && qc->id == cx25840_qctrl[i].id) { | 689 | case V4L2_CID_BRIGHTNESS: |
736 | memcpy(qc, &cx25840_qctrl[i], sizeof(*qc)); | 690 | case V4L2_CID_CONTRAST: |
737 | return 0; | 691 | case V4L2_CID_SATURATION: |
738 | } | 692 | case V4L2_CID_HUE: |
693 | return v4l2_ctrl_query_fill_std(qc); | ||
694 | default: | ||
695 | break; | ||
696 | } | ||
697 | if (state->is_cx25836) | ||
698 | return -EINVAL; | ||
699 | |||
700 | switch (qc->id) { | ||
701 | case V4L2_CID_AUDIO_VOLUME: | ||
702 | case V4L2_CID_AUDIO_MUTE: | ||
703 | case V4L2_CID_AUDIO_BALANCE: | ||
704 | case V4L2_CID_AUDIO_BASS: | ||
705 | case V4L2_CID_AUDIO_TREBLE: | ||
706 | return v4l2_ctrl_query_fill_std(qc); | ||
707 | default: | ||
708 | return -EINVAL; | ||
709 | } | ||
739 | return -EINVAL; | 710 | return -EINVAL; |
740 | } | 711 | } |
741 | 712 | ||
@@ -760,31 +731,41 @@ static int cx25840_command(struct i2c_client *client, unsigned int cmd, | |||
760 | return set_input(client, route->input, state->aud_input); | 731 | return set_input(client, route->input, state->aud_input); |
761 | 732 | ||
762 | case VIDIOC_INT_G_AUDIO_ROUTING: | 733 | case VIDIOC_INT_G_AUDIO_ROUTING: |
734 | if (state->is_cx25836) | ||
735 | return -EINVAL; | ||
763 | route->input = state->aud_input; | 736 | route->input = state->aud_input; |
764 | route->output = 0; | 737 | route->output = 0; |
765 | break; | 738 | break; |
766 | 739 | ||
767 | case VIDIOC_INT_S_AUDIO_ROUTING: | 740 | case VIDIOC_INT_S_AUDIO_ROUTING: |
741 | if (state->is_cx25836) | ||
742 | return -EINVAL; | ||
768 | return set_input(client, state->vid_input, route->input); | 743 | return set_input(client, state->vid_input, route->input); |
769 | 744 | ||
770 | case VIDIOC_S_FREQUENCY: | 745 | case VIDIOC_S_FREQUENCY: |
771 | input_change(client); | 746 | if (!state->is_cx25836) { |
747 | input_change(client); | ||
748 | } | ||
772 | break; | 749 | break; |
773 | 750 | ||
774 | case VIDIOC_G_TUNER: | 751 | case VIDIOC_G_TUNER: |
775 | { | 752 | { |
776 | u8 mode = cx25840_read(client, 0x804); | 753 | u8 vpres = cx25840_read(client, 0x40e) & 0x20; |
777 | u8 vpres = cx25840_read(client, 0x80a) & 0x10; | 754 | u8 mode; |
778 | int val = 0; | 755 | int val = 0; |
779 | 756 | ||
780 | if (state->radio) | 757 | if (state->radio) |
781 | break; | 758 | break; |
782 | 759 | ||
760 | vt->signal = vpres ? 0xffff : 0x0; | ||
761 | if (state->is_cx25836) | ||
762 | break; | ||
763 | |||
783 | vt->capability |= | 764 | vt->capability |= |
784 | V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_LANG1 | | 765 | V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_LANG1 | |
785 | V4L2_TUNER_CAP_LANG2 | V4L2_TUNER_CAP_SAP; | 766 | V4L2_TUNER_CAP_LANG2 | V4L2_TUNER_CAP_SAP; |
786 | 767 | ||
787 | vt->signal = vpres ? 0xffff : 0x0; | 768 | mode = cx25840_read(client, 0x804); |
788 | 769 | ||
789 | /* get rxsubchans and audmode */ | 770 | /* get rxsubchans and audmode */ |
790 | if ((mode & 0xf) == 1) | 771 | if ((mode & 0xf) == 1) |
@@ -804,7 +785,7 @@ static int cx25840_command(struct i2c_client *client, unsigned int cmd, | |||
804 | } | 785 | } |
805 | 786 | ||
806 | case VIDIOC_S_TUNER: | 787 | case VIDIOC_S_TUNER: |
807 | if (state->radio) | 788 | if (state->radio || state->is_cx25836) |
808 | break; | 789 | break; |
809 | 790 | ||
810 | switch (vt->audmode) { | 791 | switch (vt->audmode) { |
@@ -846,12 +827,14 @@ static int cx25840_command(struct i2c_client *client, unsigned int cmd, | |||
846 | return set_v4lfmt(client, (struct v4l2_format *)arg); | 827 | return set_v4lfmt(client, (struct v4l2_format *)arg); |
847 | 828 | ||
848 | case VIDIOC_INT_RESET: | 829 | case VIDIOC_INT_RESET: |
849 | cx25840_initialize(client, 0); | 830 | if (state->is_cx25836) |
831 | cx25836_initialize(client); | ||
832 | else | ||
833 | cx25840_initialize(client, 0); | ||
850 | break; | 834 | break; |
851 | 835 | ||
852 | case VIDIOC_INT_G_CHIP_IDENT: | 836 | case VIDIOC_INT_G_CHIP_IDENT: |
853 | *(enum v4l2_chip_ident *)arg = | 837 | *(enum v4l2_chip_ident *)arg = state->id; |
854 | V4L2_IDENT_CX25840 + ((cx25840_read(client, 0x100) >> 4) & 0xf); | ||
855 | break; | 838 | break; |
856 | 839 | ||
857 | default: | 840 | default: |
@@ -870,6 +853,7 @@ static int cx25840_detect_client(struct i2c_adapter *adapter, int address, | |||
870 | { | 853 | { |
871 | struct i2c_client *client; | 854 | struct i2c_client *client; |
872 | struct cx25840_state *state; | 855 | struct cx25840_state *state; |
856 | enum v4l2_chip_ident id; | ||
873 | u16 device_id; | 857 | u16 device_id; |
874 | 858 | ||
875 | /* Check if the adapter supports the needed features | 859 | /* Check if the adapter supports the needed features |
@@ -878,10 +862,11 @@ static int cx25840_detect_client(struct i2c_adapter *adapter, int address, | |||
878 | if (!i2c_check_functionality(adapter, I2C_FUNC_I2C)) | 862 | if (!i2c_check_functionality(adapter, I2C_FUNC_I2C)) |
879 | return 0; | 863 | return 0; |
880 | 864 | ||
881 | client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL); | 865 | state = kzalloc(sizeof(struct cx25840_state), GFP_KERNEL); |
882 | if (client == 0) | 866 | if (state == 0) |
883 | return -ENOMEM; | 867 | return -ENOMEM; |
884 | 868 | ||
869 | client = &state->c; | ||
885 | client->addr = address; | 870 | client->addr = address; |
886 | client->adapter = adapter; | 871 | client->adapter = adapter; |
887 | client->driver = &i2c_driver_cx25840; | 872 | client->driver = &i2c_driver_cx25840; |
@@ -893,10 +878,18 @@ static int cx25840_detect_client(struct i2c_adapter *adapter, int address, | |||
893 | device_id |= cx25840_read(client, 0x100); | 878 | device_id |= cx25840_read(client, 0x100); |
894 | 879 | ||
895 | /* The high byte of the device ID should be | 880 | /* The high byte of the device ID should be |
896 | * 0x84 if chip is present */ | 881 | * 0x83 for the cx2583x and 0x84 for the cx2584x */ |
897 | if ((device_id & 0xff00) != 0x8400) { | 882 | if ((device_id & 0xff00) == 0x8300) { |
883 | id = V4L2_IDENT_CX25836 + ((device_id >> 4) & 0xf) - 6; | ||
884 | state->is_cx25836 = 1; | ||
885 | } | ||
886 | else if ((device_id & 0xff00) == 0x8400) { | ||
887 | id = V4L2_IDENT_CX25840 + ((device_id >> 4) & 0xf); | ||
888 | state->is_cx25836 = 0; | ||
889 | } | ||
890 | else { | ||
898 | v4l_dbg(1, cx25840_debug, client, "cx25840 not found\n"); | 891 | v4l_dbg(1, cx25840_debug, client, "cx25840 not found\n"); |
899 | kfree(client); | 892 | kfree(state); |
900 | return 0; | 893 | return 0; |
901 | } | 894 | } |
902 | 895 | ||
@@ -905,21 +898,19 @@ static int cx25840_detect_client(struct i2c_adapter *adapter, int address, | |||
905 | (device_id & 0x0f) < 3 ? (device_id & 0x0f) + 1 : 3, | 898 | (device_id & 0x0f) < 3 ? (device_id & 0x0f) + 1 : 3, |
906 | address << 1, adapter->name); | 899 | address << 1, adapter->name); |
907 | 900 | ||
908 | state = kmalloc(sizeof(struct cx25840_state), GFP_KERNEL); | ||
909 | if (state == NULL) { | ||
910 | kfree(client); | ||
911 | return -ENOMEM; | ||
912 | } | ||
913 | |||
914 | i2c_set_clientdata(client, state); | 901 | i2c_set_clientdata(client, state); |
915 | memset(state, 0, sizeof(struct cx25840_state)); | ||
916 | state->vid_input = CX25840_COMPOSITE7; | 902 | state->vid_input = CX25840_COMPOSITE7; |
917 | state->aud_input = CX25840_AUDIO8; | 903 | state->aud_input = CX25840_AUDIO8; |
918 | state->audclk_freq = 48000; | 904 | state->audclk_freq = 48000; |
919 | state->pvr150_workaround = 0; | 905 | state->pvr150_workaround = 0; |
920 | state->audmode = V4L2_TUNER_MODE_LANG1; | 906 | state->audmode = V4L2_TUNER_MODE_LANG1; |
907 | state->vbi_line_offset = 8; | ||
908 | state->id = id; | ||
921 | 909 | ||
922 | cx25840_initialize(client, 1); | 910 | if (state->is_cx25836) |
911 | cx25836_initialize(client); | ||
912 | else | ||
913 | cx25840_initialize(client, 1); | ||
923 | 914 | ||
924 | i2c_attach_client(client); | 915 | i2c_attach_client(client); |
925 | 916 | ||
@@ -944,7 +935,6 @@ static int cx25840_detach_client(struct i2c_client *client) | |||
944 | } | 935 | } |
945 | 936 | ||
946 | kfree(state); | 937 | kfree(state); |
947 | kfree(client); | ||
948 | 938 | ||
949 | return 0; | 939 | return 0; |
950 | } | 940 | } |
@@ -977,7 +967,7 @@ module_exit(m__exit); | |||
977 | 967 | ||
978 | /* ----------------------------------------------------------------------- */ | 968 | /* ----------------------------------------------------------------------- */ |
979 | 969 | ||
980 | static void log_status(struct i2c_client *client) | 970 | static void log_video_status(struct i2c_client *client) |
981 | { | 971 | { |
982 | static const char *const fmt_strs[] = { | 972 | static const char *const fmt_strs[] = { |
983 | "0x0", | 973 | "0x0", |
@@ -989,9 +979,36 @@ static void log_status(struct i2c_client *client) | |||
989 | }; | 979 | }; |
990 | 980 | ||
991 | struct cx25840_state *state = i2c_get_clientdata(client); | 981 | struct cx25840_state *state = i2c_get_clientdata(client); |
992 | u8 microctrl_vidfmt = cx25840_read(client, 0x80a); | ||
993 | u8 vidfmt_sel = cx25840_read(client, 0x400) & 0xf; | 982 | u8 vidfmt_sel = cx25840_read(client, 0x400) & 0xf; |
994 | u8 gen_stat1 = cx25840_read(client, 0x40d); | 983 | u8 gen_stat1 = cx25840_read(client, 0x40d); |
984 | u8 gen_stat2 = cx25840_read(client, 0x40e); | ||
985 | int vid_input = state->vid_input; | ||
986 | |||
987 | v4l_info(client, "Video signal: %spresent\n", | ||
988 | (gen_stat2 & 0x20) ? "" : "not "); | ||
989 | v4l_info(client, "Detected format: %s\n", | ||
990 | fmt_strs[gen_stat1 & 0xf]); | ||
991 | |||
992 | v4l_info(client, "Specified standard: %s\n", | ||
993 | vidfmt_sel ? fmt_strs[vidfmt_sel] : "automatic detection"); | ||
994 | |||
995 | if (vid_input >= CX25840_COMPOSITE1 && | ||
996 | vid_input <= CX25840_COMPOSITE8) { | ||
997 | v4l_info(client, "Specified video input: Composite %d\n", | ||
998 | vid_input - CX25840_COMPOSITE1 + 1); | ||
999 | } else { | ||
1000 | v4l_info(client, "Specified video input: S-Video (Luma In%d, Chroma In%d)\n", | ||
1001 | (vid_input & 0xf0) >> 4, (vid_input & 0xf00) >> 8); | ||
1002 | } | ||
1003 | |||
1004 | v4l_info(client, "Specified audioclock freq: %d Hz\n", state->audclk_freq); | ||
1005 | } | ||
1006 | |||
1007 | /* ----------------------------------------------------------------------- */ | ||
1008 | |||
1009 | static void log_audio_status(struct i2c_client *client) | ||
1010 | { | ||
1011 | struct cx25840_state *state = i2c_get_clientdata(client); | ||
995 | u8 download_ctl = cx25840_read(client, 0x803); | 1012 | u8 download_ctl = cx25840_read(client, 0x803); |
996 | u8 mod_det_stat0 = cx25840_read(client, 0x804); | 1013 | u8 mod_det_stat0 = cx25840_read(client, 0x804); |
997 | u8 mod_det_stat1 = cx25840_read(client, 0x805); | 1014 | u8 mod_det_stat1 = cx25840_read(client, 0x805); |
@@ -999,15 +1016,9 @@ static void log_status(struct i2c_client *client) | |||
999 | u8 pref_mode = cx25840_read(client, 0x809); | 1016 | u8 pref_mode = cx25840_read(client, 0x809); |
1000 | u8 afc0 = cx25840_read(client, 0x80b); | 1017 | u8 afc0 = cx25840_read(client, 0x80b); |
1001 | u8 mute_ctl = cx25840_read(client, 0x8d3); | 1018 | u8 mute_ctl = cx25840_read(client, 0x8d3); |
1002 | int vid_input = state->vid_input; | ||
1003 | int aud_input = state->aud_input; | 1019 | int aud_input = state->aud_input; |
1004 | char *p; | 1020 | char *p; |
1005 | 1021 | ||
1006 | v4l_info(client, "Video signal: %spresent\n", | ||
1007 | (microctrl_vidfmt & 0x10) ? "" : "not "); | ||
1008 | v4l_info(client, "Detected format: %s\n", | ||
1009 | fmt_strs[gen_stat1 & 0xf]); | ||
1010 | |||
1011 | switch (mod_det_stat0) { | 1022 | switch (mod_det_stat0) { |
1012 | case 0x00: p = "mono"; break; | 1023 | case 0x00: p = "mono"; break; |
1013 | case 0x01: p = "stereo"; break; | 1024 | case 0x01: p = "stereo"; break; |
@@ -1107,25 +1118,12 @@ static void log_status(struct i2c_client *client) | |||
1107 | v4l_info(client, "Configured audio system: %s\n", p); | 1118 | v4l_info(client, "Configured audio system: %s\n", p); |
1108 | } | 1119 | } |
1109 | 1120 | ||
1110 | v4l_info(client, "Specified standard: %s\n", | ||
1111 | vidfmt_sel ? fmt_strs[vidfmt_sel] : "automatic detection"); | ||
1112 | |||
1113 | if (vid_input >= CX25840_COMPOSITE1 && | ||
1114 | vid_input <= CX25840_COMPOSITE8) { | ||
1115 | v4l_info(client, "Specified video input: Composite %d\n", | ||
1116 | vid_input - CX25840_COMPOSITE1 + 1); | ||
1117 | } else { | ||
1118 | v4l_info(client, "Specified video input: S-Video (Luma In%d, Chroma In%d)\n", | ||
1119 | (vid_input & 0xf0) >> 4, (vid_input & 0xf00) >> 8); | ||
1120 | } | ||
1121 | if (aud_input) { | 1121 | if (aud_input) { |
1122 | v4l_info(client, "Specified audio input: Tuner (In%d)\n", aud_input); | 1122 | v4l_info(client, "Specified audio input: Tuner (In%d)\n", aud_input); |
1123 | } else { | 1123 | } else { |
1124 | v4l_info(client, "Specified audio input: External\n"); | 1124 | v4l_info(client, "Specified audio input: External\n"); |
1125 | } | 1125 | } |
1126 | 1126 | ||
1127 | v4l_info(client, "Specified audioclock freq: %d Hz\n", state->audclk_freq); | ||
1128 | |||
1129 | switch (pref_mode & 0xf) { | 1127 | switch (pref_mode & 0xf) { |
1130 | case 0: p = "mono/language A"; break; | 1128 | case 0: p = "mono/language A"; break; |
1131 | case 1: p = "language B"; break; | 1129 | case 1: p = "language B"; break; |