diff options
24 files changed, 5008 insertions, 660 deletions
diff --git a/linux/Documentation/video4linux/extract_xc3028.pl b/linux/Documentation/video4linux/extract_xc3028.pl new file mode 100755 index 000000000..cced8ac5c --- /dev/null +++ b/linux/Documentation/video4linux/extract_xc3028.pl @@ -0,0 +1,926 @@ +#!/usr/bin/perl + +# Copyright (c) Mauro Carvalho Chehab <mchehab@infradead.org> +# Released under GPLv2 +# +# In order to use, you need to: +# 1) Download the windows driver with something like: +# wget http://www.steventoth.net/linux/xc5000/HVR-12x0-14x0-17x0_1_25_25271_WHQL.zip +# 2) Extract the file hcw85bda.sys from the zip into the current dir: +# unzip -j HVR-12x0-14x0-17x0_1_25_25271_WHQL.zip Driver85/hcw85bda.sys +# 3) run the script: +# ./extract_xc3028.pl +# 4) copy the generated file: +# cp xc3028-v27.fw /lib/firmware + +#use strict; +use IO::Handle; + +my $debug=0; + +sub verify ($$) +{ + my ($filename, $hash) = @_; + my ($testhash); + + if (system("which md5sum > /dev/null 2>&1")) { + die "This firmware requires the md5sum command - see http://www.gnu.org/software/coreutils/\n"; + } + + open(CMD, "md5sum ".$filename."|"); + $testhash = <CMD>; + $testhash =~ /([a-zA-Z0-9]*)/; + $testhash = $1; + close CMD; + die "Hash of extracted file does not match (found $testhash, expected $hash!\n" if ($testhash ne $hash); +} + +sub get_hunk ($$) +{ + my ($offset, $length) = @_; + my ($chunklength, $buf, $rcount, $out); + + sysseek(INFILE, $offset, SEEK_SET); + while ($length > 0) { + # Calc chunk size + $chunklength = 2048; + $chunklength = $length if ($chunklength > $length); + + $rcount = sysread(INFILE, $buf, $chunklength); + die "Ran out of data\n" if ($rcount != $chunklength); + $out .= $buf; + $length -= $rcount; + } + return $out; +} + +sub write_le16($) +{ + my $val = shift; + my $msb = ($val >> 8) &0xff; + my $lsb = $val & 0xff; + + syswrite(OUTFILE, chr($lsb).chr($msb)); +} + +sub write_le32($) +{ + my $val = shift; + my $l3 = ($val >> 24) & 0xff; + my $l2 = ($val >> 16) & 0xff; + my $l1 = ($val >> 8) & 0xff; + my $l0 = $val & 0xff; + + syswrite(OUTFILE, chr($l0).chr($l1).chr($l2).chr($l3)); +} + +sub write_le64($$) +{ + my $msb_val = shift; + my $lsb_val = shift; + my $l7 = ($msb_val >> 24) & 0xff; + my $l6 = ($msb_val >> 16) & 0xff; + my $l5 = ($msb_val >> 8) & 0xff; + my $l4 = $msb_val & 0xff; + + my $l3 = ($lsb_val >> 24) & 0xff; + my $l2 = ($lsb_val >> 16) & 0xff; + my $l1 = ($lsb_val >> 8) & 0xff; + my $l0 = $lsb_val & 0xff; + + syswrite(OUTFILE, + chr($l0).chr($l1).chr($l2).chr($l3). + chr($l4).chr($l5).chr($l6).chr($l7)); +} + +sub write_hunk($$) +{ + my ($offset, $length) = @_; + my $out = get_hunk($offset, $length); + + printf "(len %d) ",$length if ($debug); + + for (my $i=0;$i<$length;$i++) { + printf "%02x ",ord(substr($out,$i,1)) if ($debug); + } + printf "\n" if ($debug); + + syswrite(OUTFILE, $out); +} + +sub write_hunk_fix_endian($$) +{ + my ($offset, $length) = @_; + my $out = get_hunk($offset, $length); + + printf "(len_fix %d) ",$length if ($debug); + + for (my $i=0;$i<$length;$i++) { + printf "%02x ",ord(substr($out,$i,1)) if ($debug); + } + printf "\n" if ($debug); + + my $i=0; + while ($i<$length) { + my $size = ord(substr($out,$i,1))*256+ord(substr($out,$i+1,1)); + syswrite(OUTFILE, substr($out,$i+1,1)); + syswrite(OUTFILE, substr($out,$i,1)); + $i+=2; + if ($size>0 && $size <0x8000) { + for (my $j=0;$j<$size;$j++) { + syswrite(OUTFILE, substr($out,$j+$i,1)); + } + $i+=$size; + } + } +} + +sub main_firmware($$$$) +{ + my $out; + my $j=0; + my $outfile = shift; + my $name = shift; + my $version = shift; + my $nr_desc = shift; + + for ($j = length($name); $j <32; $j++) { + $name = $name.chr(0); +} + + open OUTFILE, ">$outfile"; + syswrite(OUTFILE, $name); + write_le16($version); + write_le16($nr_desc); + + # + # Firmware 0, type: BASE FW F8MHZ (0x00000003), id: (0000000000000000), size: 8718 + # + + write_le32(0x00000003); # Type + write_le64(0x00000000, 0x00000000); # ID + write_le32(8718); # Size + write_hunk_fix_endian(813432, 8718); + + # + # Firmware 1, type: BASE FW F8MHZ MTS (0x00000007), id: (0000000000000000), size: 8712 + # + + write_le32(0x00000007); # Type + write_le64(0x00000000, 0x00000000); # ID + write_le32(8712); # Size + write_hunk_fix_endian(822152, 8712); + + # + # Firmware 2, type: BASE FW FM (0x00000401), id: (0000000000000000), size: 8562 + # + + write_le32(0x00000401); # Type + write_le64(0x00000000, 0x00000000); # ID + write_le32(8562); # Size + write_hunk_fix_endian(830872, 8562); + + # + # Firmware 3, type: BASE FW FM INPUT1 (0x00000c01), id: (0000000000000000), size: 8576 + # + + write_le32(0x00000c01); # Type + write_le64(0x00000000, 0x00000000); # ID + write_le32(8576); # Size + write_hunk_fix_endian(839440, 8576); + + # + # Firmware 4, type: BASE FW (0x00000001), id: (0000000000000000), size: 8706 + # + + write_le32(0x00000001); # Type + write_le64(0x00000000, 0x00000000); # ID + write_le32(8706); # Size + write_hunk_fix_endian(848024, 8706); + + # + # Firmware 5, type: BASE FW MTS (0x00000005), id: (0000000000000000), size: 8682 + # + + write_le32(0x00000005); # Type + write_le64(0x00000000, 0x00000000); # ID + write_le32(8682); # Size + write_hunk_fix_endian(856736, 8682); + + # + # Firmware 6, type: STD FW (0x00000000), id: PAL/BG A2/A (0000000100000007), size: 161 + # + + write_le32(0x00000000); # Type + write_le64(0x00000001, 0x00000007); # ID + write_le32(161); # Size + write_hunk_fix_endian(865424, 161); + + # + # Firmware 7, type: STD FW MTS (0x00000004), id: PAL/BG A2/A (0000000100000007), size: 169 + # + + write_le32(0x00000004); # Type + write_le64(0x00000001, 0x00000007); # ID + write_le32(169); # Size + write_hunk_fix_endian(865592, 169); + + # + # Firmware 8, type: STD FW (0x00000000), id: PAL/BG A2/B (0000000200000007), size: 161 + # + + write_le32(0x00000000); # Type + write_le64(0x00000002, 0x00000007); # ID + write_le32(161); # Size + write_hunk_fix_endian(865424, 161); + + # + # Firmware 9, type: STD FW MTS (0x00000004), id: PAL/BG A2/B (0000000200000007), size: 169 + # + + write_le32(0x00000004); # Type + write_le64(0x00000002, 0x00000007); # ID + write_le32(169); # Size + write_hunk_fix_endian(865592, 169); + + # + # Firmware 10, type: STD FW (0x00000000), id: PAL/BG NICAM/A (0000000400000007), size: 161 + # + + write_le32(0x00000000); # Type + write_le64(0x00000004, 0x00000007); # ID + write_le32(161); # Size + write_hunk_fix_endian(866112, 161); + + # + # Firmware 11, type: STD FW MTS (0x00000004), id: PAL/BG NICAM/A (0000000400000007), size: 169 + # + + write_le32(0x00000004); # Type + write_le64(0x00000004, 0x00000007); # ID + write_le32(169); # Size + write_hunk_fix_endian(866280, 169); + + # + # Firmware 12, type: STD FW (0x00000000), id: PAL/BG NICAM/B (0000000800000007), size: 161 + # + + write_le32(0x00000000); # Type + write_le64(0x00000008, 0x00000007); # ID + write_le32(161); # Size + write_hunk_fix_endian(866112, 161); + + # + # Firmware 13, type: STD FW MTS (0x00000004), id: PAL/BG NICAM/B (0000000800000007), size: 169 + # + + write_le32(0x00000004); # Type + write_le64(0x00000008, 0x00000007); # ID + write_le32(169); # Size + write_hunk_fix_endian(866280, 169); + + # + # Firmware 14, type: STD FW (0x00000000), id: PAL/DK A2 (00000003000000e0), size: 161 + # + + write_le32(0x00000000); # Type + write_le64(0x00000003, 0x000000e0); # ID + write_le32(161); # Size + write_hunk_fix_endian(866800, 161); + + # + # Firmware 15, type: STD FW MTS (0x00000004), id: PAL/DK A2 (00000003000000e0), size: 169 + # + + write_le32(0x00000004); # Type + write_le64(0x00000003, 0x000000e0); # ID + write_le32(169); # Size + write_hunk_fix_endian(866968, 169); + + # + # Firmware 16, type: STD FW (0x00000000), id: PAL/DK NICAM (0000000c000000e0), size: 161 + # + + write_le32(0x00000000); # Type + write_le64(0x0000000c, 0x000000e0); # ID + write_le32(161); # Size + write_hunk_fix_endian(867144, 161); + + # + # Firmware 17, type: STD FW MTS (0x00000004), id: PAL/DK NICAM (0000000c000000e0), size: 169 + # + + write_le32(0x00000004); # Type + write_le64(0x0000000c, 0x000000e0); # ID + write_le32(169); # Size + write_hunk_fix_endian(867312, 169); + + # + # Firmware 18, type: STD FW (0x00000000), id: SECAM/K1 (0000000000200000), size: 161 + # + + write_le32(0x00000000); # Type + write_le64(0x00000000, 0x00200000); # ID + write_le32(161); # Size + write_hunk_fix_endian(867488, 161); + + # + # Firmware 19, type: STD FW MTS (0x00000004), id: SECAM/K1 (0000000000200000), size: 169 + # + + write_le32(0x00000004); # Type + write_le64(0x00000000, 0x00200000); # ID + write_le32(169); # Size + write_hunk_fix_endian(867656, 169); + + # + # Firmware 20, type: STD FW (0x00000000), id: SECAM/K3 (0000000004000000), size: 161 + # + + write_le32(0x00000000); # Type + write_le64(0x00000000, 0x04000000); # ID + write_le32(161); # Size + write_hunk_fix_endian(867832, 161); + + # + # Firmware 21, type: STD FW MTS (0x00000004), id: SECAM/K3 (0000000004000000), size: 169 + # + + write_le32(0x00000004); # Type + write_le64(0x00000000, 0x04000000); # ID + write_le32(169); # Size + write_hunk_fix_endian(868000, 169); + + # + # Firmware 22, type: STD FW D2633 DTV6 ATSC (0x00010030), id: (0000000000000000), size: 149 + # + + write_le32(0x00010030); # Type + write_le64(0x00000000, 0x00000000); # ID + write_le32(149); # Size + write_hunk_fix_endian(868176, 149); + + # + # Firmware 23, type: STD FW D2620 DTV6 QAM (0x00000068), id: (0000000000000000), size: 149 + # + + write_le32(0x00000068); # Type + write_le64(0x00000000, 0x00000000); # ID + write_le32(149); # Size + write_hunk_fix_endian(868336, 149); + + # + # Firmware 24, type: STD FW D2633 DTV6 QAM (0x00000070), id: (0000000000000000), size: 149 + # + + write_le32(0x00000070); # Type + write_le64(0x00000000, 0x00000000); # ID + write_le32(149); # Size + write_hunk_fix_endian(868488, 149); + + # + # Firmware 25, type: STD FW D2620 DTV7 (0x00000088), id: (0000000000000000), size: 149 + # + + write_le32(0x00000088); # Type + write_le64(0x00000000, 0x00000000); # ID + write_le32(149); # Size + write_hunk_fix_endian(868648, 149); + + # + # Firmware 26, type: STD FW D2633 DTV7 (0x00000090), id: (0000000000000000), size: 149 + # + + write_le32(0x00000090); # Type + write_le64(0x00000000, 0x00000000); # ID + write_le32(149); # Size + write_hunk_fix_endian(868800, 149); + + # + # Firmware 27, type: STD FW D2620 DTV78 (0x00000108), id: (0000000000000000), size: 149 + # + + write_le32(0x00000108); # Type + write_le64(0x00000000, 0x00000000); # ID + write_le32(149); # Size + write_hunk_fix_endian(868960, 149); + + # + # Firmware 28, type: STD FW D2633 DTV78 (0x00000110), id: (0000000000000000), size: 149 + # + + write_le32(0x00000110); # Type + write_le64(0x00000000, 0x00000000); # ID + write_le32(149); # Size + write_hunk_fix_endian(869112, 149); + + # + # Firmware 29, type: STD FW D2620 DTV8 (0x00000208), id: (0000000000000000), size: 149 + # + + write_le32(0x00000208); # Type + write_le64(0x00000000, 0x00000000); # ID + write_le32(149); # Size + write_hunk_fix_endian(868648, 149); + + # + # Firmware 30, type: STD FW D2633 DTV8 (0x00000210), id: (0000000000000000), size: 149 + # + + write_le32(0x00000210); # Type + write_le64(0x00000000, 0x00000000); # ID + write_le32(149); # Size + write_hunk_fix_endian(868800, 149); + + # + # Firmware 31, type: STD FW FM (0x00000400), id: (0000000000000000), size: 135 + # + + write_le32(0x00000400); # Type + write_le64(0x00000000, 0x00000000); # ID + write_le32(135); # Size + write_hunk_fix_endian(869584, 135); + + # + # Firmware 32, type: STD FW (0x00000000), id: PAL/I (0000000000000010), size: 161 + # + + write_le32(0x00000000); # Type + write_le64(0x00000000, 0x00000010); # ID + write_le32(161); # Size + write_hunk_fix_endian(869728, 161); + + # + # Firmware 33, type: STD FW MTS (0x00000004), id: PAL/I (0000000000000010), size: 169 + # + + write_le32(0x00000004); # Type + write_le64(0x00000000, 0x00000010); # ID + write_le32(169); # Size + write_hunk_fix_endian(869896, 169); + + # + # Firmware 34, type: STD FW (0x00000000), id: SECAM/L AM (0000001000400000), size: 169 + # + + write_le32(0x00000000); # Type + write_le64(0x00000010, 0x00400000); # ID + write_le32(169); # Size + write_hunk_fix_endian(870072, 169); + + # + # Firmware 35, type: STD FW (0x00000000), id: SECAM/L NICAM (0000000c00400000), size: 161 + # + + write_le32(0x00000000); # Type + write_le64(0x0000000c, 0x00400000); # ID + write_le32(161); # Size + write_hunk_fix_endian(870248, 161); + + # + # Firmware 36, type: STD FW (0x00000000), id: SECAM/Lc (0000000000800000), size: 161 + # + + write_le32(0x00000000); # Type + write_le64(0x00000000, 0x00800000); # ID + write_le32(161); # Size + write_hunk_fix_endian(870416, 161); + + # + # Firmware 37, type: STD FW (0x00000000), id: NTSC/M Kr (0000000000008000), size: 161 + # + + write_le32(0x00000000); # Type + write_le64(0x00000000, 0x00008000); # ID + write_le32(161); # Size + write_hunk_fix_endian(870584, 161); + + # + # Firmware 38, type: STD FW LCD (0x00001000), id: NTSC/M Kr (0000000000008000), size: 161 + # + + write_le32(0x00001000); # Type + write_le64(0x00000000, 0x00008000); # ID + write_le32(161); # Size + write_hunk_fix_endian(870752, 161); + + # + # Firmware 39, type: STD FW LCD NOGD (0x00003000), id: NTSC/M Kr (0000000000008000), size: 161 + # + + write_le32(0x00003000); # Type + write_le64(0x00000000, 0x00008000); # ID + write_le32(161); # Size + write_hunk_fix_endian(870920, 161); + + # + # Firmware 40, type: STD FW MTS (0x00000004), id: NTSC/M Kr (0000000000008000), size: 169 + # + + write_le32(0x00000004); # Type + write_le64(0x00000000, 0x00008000); # ID + write_le32(169); # Size + write_hunk_fix_endian(871088, 169); + + # + # Firmware 41, type: STD FW (0x00000000), id: NTSC PAL/M PAL/N (000000000000b700), size: 161 + # + + write_le32(0x00000000); # Type + write_le64(0x00000000, 0x0000b700); # ID + write_le32(161); # Size + write_hunk_fix_endian(871264, 161); + + # + # Firmware 42, type: STD FW LCD (0x00001000), id: NTSC PAL/M PAL/N (000000000000b700), size: 161 + # + + write_le32(0x00001000); # Type + write_le64(0x00000000, 0x0000b700); # ID + write_le32(161); # Size + write_hunk_fix_endian(871432, 161); + + # + # Firmware 43, type: STD FW LCD NOGD (0x00003000), id: NTSC PAL/M PAL/N (000000000000b700), size: 161 + # + + write_le32(0x00003000); # Type + write_le64(0x00000000, 0x0000b700); # ID + write_le32(161); # Size + write_hunk_fix_endian(871600, 161); + + # + # Firmware 44, type: STD FW (0x00000000), id: NTSC/M Jp (0000000000002000), size: 161 + # + + write_le32(0x00000000); # Type + write_le64(0x00000000, 0x00002000); # ID + write_le32(161); # Size + write_hunk_fix_endian(871264, 161); + + # + # Firmware 45, type: STD FW MTS (0x00000004), id: NTSC PAL/M PAL/N (000000000000b700), size: 169 + # + + write_le32(0x00000004); # Type + write_le64(0x00000000, 0x0000b700); # ID + write_le32(169); # Size + write_hunk_fix_endian(871936, 169); + + # + # Firmware 46, type: STD FW MTS LCD (0x00001004), id: NTSC PAL/M PAL/N (000000000000b700), size: 169 + # + + write_le32(0x00001004); # Type + write_le64(0x00000000, 0x0000b700); # ID + write_le32(169); # Size + write_hunk_fix_endian(872112, 169); + + # + # Firmware 47, type: STD FW MTS LCD NOGD (0x00003004), id: NTSC PAL/M PAL/N (000000000000b700), size: 169 + # + + write_le32(0x00003004); # Type + write_le64(0x00000000, 0x0000b700); # ID + write_le32(169); # Size + write_hunk_fix_endian(872288, 169); + + # + # Firmware 48, type: SCODE FW HAS IF (0x60000000), IF = 3.28 MHz id: (0000000000000000), size: 192 + # + + write_le32(0x60000000); # Type + write_le64(0x00000000, 0x00000000); # ID + write_le16(3280); # IF + write_le32(192); # Size + write_hunk(811896, 192); + + # + # Firmware 49, type: SCODE FW HAS IF (0x60000000), IF = 3.30 MHz id: (0000000000000000), size: 192 + # + + write_le32(0x60000000); # Type + write_le64(0x00000000, 0x00000000); # ID + write_le16(3300); # IF + write_le32(192); # Size + write_hunk(813048, 192); + + # + # Firmware 50, type: SCODE FW HAS IF (0x60000000), IF = 3.44 MHz id: (0000000000000000), size: 192 + # + + write_le32(0x60000000); # Type + write_le64(0x00000000, 0x00000000); # ID + write_le16(3440); # IF + write_le32(192); # Size + write_hunk(812280, 192); + + # + # Firmware 51, type: SCODE FW HAS IF (0x60000000), IF = 3.46 MHz id: (0000000000000000), size: 192 + # + + write_le32(0x60000000); # Type + write_le64(0x00000000, 0x00000000); # ID + write_le16(3460); # IF + write_le32(192); # Size + write_hunk(812472, 192); + + # + # Firmware 52, type: SCODE FW DTV6 ATSC OREN36 HAS IF (0x60210020), IF = 3.80 MHz id: (0000000000000000), size: 192 + # + + write_le32(0x60210020); # Type + write_le64(0x00000000, 0x00000000); # ID + write_le16(3800); # IF + write_le32(192); # Size + write_hunk(809784, 192); + + # + # Firmware 53, type: SCODE FW HAS IF (0x60000000), IF = 4.00 MHz id: (0000000000000000), size: 192 + # + + write_le32(0x60000000); # Type + write_le64(0x00000000, 0x00000000); # ID + write_le16(4000); # IF + write_le32(192); # Size + write_hunk(812088, 192); + + # + # Firmware 54, type: SCODE FW DTV6 ATSC TOYOTA388 HAS IF (0x60410020), IF = 4.08 MHz id: (0000000000000000), size: 192 + # + + write_le32(0x60410020); # Type + write_le64(0x00000000, 0x00000000); # ID + write_le16(4080); # IF + write_le32(192); # Size + write_hunk(809976, 192); + + # + # Firmware 55, type: SCODE FW HAS IF (0x60000000), IF = 4.20 MHz id: (0000000000000000), size: 192 + # + + write_le32(0x60000000); # Type + write_le64(0x00000000, 0x00000000); # ID + write_le16(4200); # IF + write_le32(192); # Size + write_hunk(811704, 192); + + # + # Firmware 56, type: SCODE FW MONO HAS IF (0x60008000), IF = 4.32 MHz id: NTSC/M Kr (0000000000008000), size: 192 + # + + write_le32(0x60008000); # Type + write_le64(0x00000000, 0x00008000); # ID + write_le16(4320); # IF + write_le32(192); # Size + write_hunk(808056, 192); + + # + # Firmware 57, type: SCODE FW HAS IF (0x60000000), IF = 4.45 MHz id: (0000000000000000), size: 192 + # + + write_le32(0x60000000); # Type + write_le64(0x00000000, 0x00000000); # ID + write_le16(4450); # IF + write_le32(192); # Size + write_hunk(812664, 192); + + # + # Firmware 58, type: SCODE FW HAS IF (0x60000000), IF = 4.50 MHz id: NTSC/M Jp (0000000000002000), size: 192 + # + + write_le32(0x60000000); # Type + write_le64(0x00000000, 0x00002000); # ID + write_le16(4500); # IF + write_le32(192); # Size + write_hunk(807672, 192); + + # + # Firmware 59, type: SCODE FW LCD NOGD IF HAS IF (0x60023000), IF = 4.60 MHz id: NTSC/M Kr (0000000000008000), size: 192 + # + + write_le32(0x60023000); # Type + write_le64(0x00000000, 0x00008000); # ID + write_le16(4600); # IF + write_le32(192); # Size + write_hunk(807864, 192); + + # + # Firmware 60, type: SCODE FW DTV78 ZARLINK456 HAS IF (0x62000100), IF = 4.76 MHz id: (0000000000000000), size: 192 + # + + write_le32(0x62000100); # Type + write_le64(0x00000000, 0x00000000); # ID + write_le16(4760); # IF + write_le32(192); # Size + write_hunk(807288, 192); + + # + # Firmware 61, type: SCODE FW HAS IF (0x60000000), IF = 4.94 MHz id: (0000000000000000), size: 192 + # + + write_le32(0x60000000); # Type + write_le64(0x00000000, 0x00000000); # ID + write_le16(4940); # IF + write_le32(192); # Size + write_hunk(811512, 192); + + # + # Firmware 62, type: SCODE FW DTV7 ZARLINK456 HAS IF (0x62000080), IF = 5.26 MHz id: (0000000000000000), size: 192 + # + + write_le32(0x62000080); # Type + write_le64(0x00000000, 0x00000000); # ID + write_le16(5260); # IF + write_le32(192); # Size + write_hunk(810552, 192); + + # + # Firmware 63, type: SCODE FW MONO HAS IF (0x60008000), IF = 5.32 MHz id: PAL/BG NICAM/B (0000000800000007), size: 192 + # + + write_le32(0x60008000); # Type + write_le64(0x00000008, 0x00000007); # ID + write_le16(5320); # IF + write_le32(192); # Size + write_hunk(810744, 192); + + # + # Firmware 64, type: SCODE FW DTV8 CHINA HAS IF (0x64000200), IF = 5.40 MHz id: (0000000000000000), size: 192 + # + + write_le32(0x64000200); # Type + write_le64(0x00000000, 0x00000000); # ID + write_le16(5400); # IF + write_le32(192); # Size + write_hunk(807096, 192); + + # + # Firmware 65, type: SCODE FW DTV6 ATSC OREN538 HAS IF (0x60110020), IF = 5.58 MHz id: (0000000000000000), size: 192 + # + + write_le32(0x60110020); # Type + write_le64(0x00000000, 0x00000000); # ID + write_le16(5580); # IF + write_le32(192); # Size + write_hunk(809592, 192); + + # + # Firmware 66, type: SCODE FW HAS IF (0x60000000), IF = 5.64 MHz id: PAL/BG A2/B (0000000200000007), size: 192 + # + + write_le32(0x60000000); # Type + write_le64(0x00000002, 0x00000007); # ID + write_le16(5640); # IF + write_le32(192); # Size + write_hunk(808440, 192); + + # + # Firmware 67, type: SCODE FW HAS IF (0x60000000), IF = 5.74 MHz id: PAL/BG NICAM/B (0000000800000007), size: 192 + # + + write_le32(0x60000000); # Type + write_le64(0x00000008, 0x00000007); # ID + write_le16(5740); # IF + write_le32(192); # Size + write_hunk(808632, 192); + + # + # Firmware 68, type: SCODE FW DTV7 DIBCOM52 HAS IF (0x61000080), IF = 5.90 MHz id: (0000000000000000), size: 192 + # + + write_le32(0x61000080); # Type + write_le64(0x00000000, 0x00000000); # ID + write_le16(5900); # IF + write_le32(192); # Size + write_hunk(810360, 192); + + # + # Firmware 69, type: SCODE FW MONO HAS IF (0x60008000), IF = 6.00 MHz id: PAL/I (0000000000000010), size: 192 + # + + write_le32(0x60008000); # Type + write_le64(0x00000000, 0x00000010); # ID + write_le16(6000); # IF + write_le32(192); # Size + write_hunk(808824, 192); + + # + # Firmware 70, type: SCODE FW DTV6 QAM F6MHZ HAS IF (0x68000060), IF = 6.20 MHz id: (0000000000000000), size: 192 + # + + write_le32(0x68000060); # Type + write_le64(0x00000000, 0x00000000); # ID + write_le16(6200); # IF + write_le32(192); # Size + write_hunk(809400, 192); + + # + # Firmware 71, type: SCODE FW HAS IF (0x60000000), IF = 6.24 MHz id: PAL/I (0000000000000010), size: 192 + # + + write_le32(0x60000000); # Type + write_le64(0x00000000, 0x00000010); # ID + write_le16(6240); # IF + write_le32(192); # Size + write_hunk(808248, 192); + + # + # Firmware 72, type: SCODE FW MONO HAS IF (0x60008000), IF = 6.32 MHz id: SECAM/K1 (0000000000200000), size: 192 + # + + write_le32(0x60008000); # Type + write_le64(0x00000000, 0x00200000); # ID + write_le16(6320); # IF + write_le32(192); # Size + write_hunk(811320, 192); + + # + # Firmware 73, type: SCODE FW HAS IF (0x60000000), IF = 6.34 MHz id: SECAM/K1 (0000000000200000), size: 192 + # + + write_le32(0x60000000); # Type + write_le64(0x00000000, 0x00200000); # ID + write_le16(6340); # IF + write_le32(192); # Size + write_hunk(809208, 192); + + # + # Firmware 74, type: SCODE FW MONO HAS IF (0x60008000), IF = 6.50 MHz id: SECAM/K3 (0000000004000000), size: 192 + # + + write_le32(0x60008000); # Type + write_le64(0x00000000, 0x04000000); # ID + write_le16(6500); # IF + write_le32(192); # Size + write_hunk(811128, 192); + + # + # Firmware 75, type: SCODE FW DTV6 ATSC ATI638 HAS IF (0x60090020), IF = 6.58 MHz id: (0000000000000000), size: 192 + # + + write_le32(0x60090020); # Type + write_le64(0x00000000, 0x00000000); # ID + write_le16(6580); # IF + write_le32(192); # Size + write_hunk(807480, 192); + + # + # Firmware 76, type: SCODE FW HAS IF (0x60000000), IF = 6.60 MHz id: PAL/DK A2 (00000003000000e0), size: 192 + # + + write_le32(0x60000000); # Type + write_le64(0x00000003, 0x000000e0); # ID + write_le16(6600); # IF + write_le32(192); # Size + write_hunk(809016, 192); + + # + # Firmware 77, type: SCODE FW MONO HAS IF (0x60008000), IF = 6.68 MHz id: PAL/DK A2 (00000003000000e0), size: 192 + # + + write_le32(0x60008000); # Type + write_le64(0x00000003, 0x000000e0); # ID + write_le16(6680); # IF + write_le32(192); # Size + write_hunk(810936, 192); + + # + # Firmware 78, type: SCODE FW DTV6 ATSC TOYOTA794 HAS IF (0x60810020), IF = 8.14 MHz id: (0000000000000000), size: 192 + # + + write_le32(0x60810020); # Type + write_le64(0x00000000, 0x00000000); # ID + write_le16(8140); # IF + write_le32(192); # Size + write_hunk(810168, 192); + + # + # Firmware 79, type: SCODE FW HAS IF (0x60000000), IF = 8.20 MHz id: (0000000000000000), size: 192 + # + + write_le32(0x60000000); # Type + write_le64(0x00000000, 0x00000000); # ID + write_le16(8200); # IF + write_le32(192); # Size + write_hunk(812856, 192); +} + +sub extract_firmware { + my $sourcefile = "hcw85bda.sys"; + my $hash = "0e44dbf63bb0169d57446aec21881ff2"; + my $outfile = "xc3028-v27.fw"; + my $name = "xc2028 firmware"; + my $version = 519; + my $nr_desc = 80; + my $out; + + verify($sourcefile, $hash); + + open INFILE, "<$sourcefile"; + main_firmware($outfile, $name, $version, $nr_desc); + close INFILE; +} + +extract_firmware; +printf "Firmwares generated.\n"; diff --git a/linux/drivers/media/dvb/frontends/Makefile b/linux/drivers/media/dvb/frontends/Makefile index 1c082a6a9..16bd107eb 100644 --- a/linux/drivers/media/dvb/frontends/Makefile +++ b/linux/drivers/media/dvb/frontends/Makefile @@ -5,7 +5,7 @@ EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core/ EXTRA_CFLAGS += -Idrivers/media/video/ -tda18271-objs := tda18271-tables.o tda18271-fe.o +tda18271-objs := tda18271-tables.o tda18271-common.o tda18271-fe.o obj-$(CONFIG_DVB_PLL) += dvb-pll.o obj-$(CONFIG_DVB_STV0299) += stv0299.o diff --git a/linux/drivers/media/dvb/frontends/tda18271-common.c b/linux/drivers/media/dvb/frontends/tda18271-common.c new file mode 100644 index 000000000..6d45313d6 --- /dev/null +++ b/linux/drivers/media/dvb/frontends/tda18271-common.c @@ -0,0 +1,685 @@ +/* + tda18271-common.c - driver for the Philips / NXP TDA18271 silicon tuner + + Copyright (C) 2007, 2008 Michael Krufky <mkrufky@linuxtv.org> + + 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, or + (at your option) any later version. + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "tda18271-priv.h" + +static int tda18271_i2c_gate_ctrl(struct dvb_frontend *fe, int enable) +{ + struct tda18271_priv *priv = fe->tuner_priv; + enum tda18271_i2c_gate gate; + int ret = 0; + + switch (priv->gate) { + case TDA18271_GATE_DIGITAL: + case TDA18271_GATE_ANALOG: + gate = priv->gate; + break; + case TDA18271_GATE_AUTO: + default: + switch (priv->mode) { + case TDA18271_DIGITAL: + gate = TDA18271_GATE_DIGITAL; + break; + case TDA18271_ANALOG: + default: + gate = TDA18271_GATE_ANALOG; + break; + } + } + + switch (gate) { + case TDA18271_GATE_ANALOG: + if (fe->ops.analog_ops.i2c_gate_ctrl) + ret = fe->ops.analog_ops.i2c_gate_ctrl(fe, enable); + break; + case TDA18271_GATE_DIGITAL: + if (fe->ops.i2c_gate_ctrl) + ret = fe->ops.i2c_gate_ctrl(fe, enable); + break; + default: + ret = -EINVAL; + break; + } + + return ret; +}; + +/*---------------------------------------------------------------------*/ + +static void tda18271_dump_regs(struct dvb_frontend *fe, int extended) +{ + struct tda18271_priv *priv = fe->tuner_priv; + unsigned char *regs = priv->tda18271_regs; + + tda_reg("=== TDA18271 REG DUMP ===\n"); + tda_reg("ID_BYTE = 0x%02x\n", 0xff & regs[R_ID]); + tda_reg("THERMO_BYTE = 0x%02x\n", 0xff & regs[R_TM]); + tda_reg("POWER_LEVEL_BYTE = 0x%02x\n", 0xff & regs[R_PL]); + tda_reg("EASY_PROG_BYTE_1 = 0x%02x\n", 0xff & regs[R_EP1]); + tda_reg("EASY_PROG_BYTE_2 = 0x%02x\n", 0xff & regs[R_EP2]); + tda_reg("EASY_PROG_BYTE_3 = 0x%02x\n", 0xff & regs[R_EP3]); + tda_reg("EASY_PROG_BYTE_4 = 0x%02x\n", 0xff & regs[R_EP4]); + tda_reg("EASY_PROG_BYTE_5 = 0x%02x\n", 0xff & regs[R_EP5]); + tda_reg("CAL_POST_DIV_BYTE = 0x%02x\n", 0xff & regs[R_CPD]); + tda_reg("CAL_DIV_BYTE_1 = 0x%02x\n", 0xff & regs[R_CD1]); + tda_reg("CAL_DIV_BYTE_2 = 0x%02x\n", 0xff & regs[R_CD2]); + tda_reg("CAL_DIV_BYTE_3 = 0x%02x\n", 0xff & regs[R_CD3]); + tda_reg("MAIN_POST_DIV_BYTE = 0x%02x\n", 0xff & regs[R_MPD]); + tda_reg("MAIN_DIV_BYTE_1 = 0x%02x\n", 0xff & regs[R_MD1]); + tda_reg("MAIN_DIV_BYTE_2 = 0x%02x\n", 0xff & regs[R_MD2]); + tda_reg("MAIN_DIV_BYTE_3 = 0x%02x\n", 0xff & regs[R_MD3]); + + /* only dump extended regs if DBG_ADV is set */ + if (!(tda18271_debug & DBG_ADV)) + return; + + /* W indicates write-only registers. + * Register dump for write-only registers shows last value written. */ + + tda_reg("EXTENDED_BYTE_1 = 0x%02x\n", 0xff & regs[R_EB1]); + tda_reg("EXTENDED_BYTE_2 = 0x%02x\n", 0xff & regs[R_EB2]); + tda_reg("EXTENDED_BYTE_3 = 0x%02x\n", 0xff & regs[R_EB3]); + tda_reg("EXTENDED_BYTE_4 = 0x%02x\n", 0xff & regs[R_EB4]); + tda_reg("EXTENDED_BYTE_5 = 0x%02x\n", 0xff & regs[R_EB5]); + tda_reg("EXTENDED_BYTE_6 = 0x%02x\n", 0xff & regs[R_EB6]); + tda_reg("EXTENDED_BYTE_7 = 0x%02x\n", 0xff & regs[R_EB7]); + tda_reg("EXTENDED_BYTE_8 = 0x%02x\n", 0xff & regs[R_EB8]); + tda_reg("EXTENDED_BYTE_9 W = 0x%02x\n", 0xff & regs[R_EB9]); + tda_reg("EXTENDED_BYTE_10 = 0x%02x\n", 0xff & regs[R_EB10]); + tda_reg("EXTENDED_BYTE_11 = 0x%02x\n", 0xff & regs[R_EB11]); + tda_reg("EXTENDED_BYTE_12 = 0x%02x\n", 0xff & regs[R_EB12]); + tda_reg("EXTENDED_BYTE_13 = 0x%02x\n", 0xff & regs[R_EB13]); + tda_reg("EXTENDED_BYTE_14 = 0x%02x\n", 0xff & regs[R_EB14]); + tda_reg("EXTENDED_BYTE_15 = 0x%02x\n", 0xff & regs[R_EB15]); + tda_reg("EXTENDED_BYTE_16 W = 0x%02x\n", 0xff & regs[R_EB16]); + tda_reg("EXTENDED_BYTE_17 W = 0x%02x\n", 0xff & regs[R_EB17]); + tda_reg("EXTENDED_BYTE_18 = 0x%02x\n", 0xff & regs[R_EB18]); + tda_reg("EXTENDED_BYTE_19 W = 0x%02x\n", 0xff & regs[R_EB19]); + tda_reg("EXTENDED_BYTE_20 W = 0x%02x\n", 0xff & regs[R_EB20]); + tda_reg("EXTENDED_BYTE_21 = 0x%02x\n", 0xff & regs[R_EB21]); + tda_reg("EXTENDED_BYTE_22 = 0x%02x\n", 0xff & regs[R_EB22]); + tda_reg("EXTENDED_BYTE_23 = 0x%02x\n", 0xff & regs[R_EB23]); +} + +int tda18271_read_regs(struct dvb_frontend *fe) +{ + struct tda18271_priv *priv = fe->tuner_priv; + unsigned char *regs = priv->tda18271_regs; + unsigned char buf = 0x00; + int ret; + struct i2c_msg msg[] = { + { .addr = priv->i2c_addr, .flags = 0, + .buf = &buf, .len = 1 }, + { .addr = priv->i2c_addr, .flags = I2C_M_RD, + .buf = regs, .len = 16 } + }; + + tda18271_i2c_gate_ctrl(fe, 1); + + /* read all registers */ + ret = i2c_transfer(priv->i2c_adap, msg, 2); + + tda18271_i2c_gate_ctrl(fe, 0); + + if (ret != 2) + tda_err("ERROR: i2c_transfer returned: %d\n", ret); + + if (tda18271_debug & DBG_REG) + tda18271_dump_regs(fe, 0); + + return (ret == 2 ? 0 : ret); +} + +int tda18271_read_extended(struct dvb_frontend *fe) +{ + struct tda18271_priv *priv = fe->tuner_priv; + unsigned char *regs = priv->tda18271_regs; + unsigned char regdump[TDA18271_NUM_REGS]; + unsigned char buf = 0x00; + int ret, i; + struct i2c_msg msg[] = { + { .addr = priv->i2c_addr, .flags = 0, + .buf = &buf, .len = 1 }, + { .addr = priv->i2c_addr, .flags = I2C_M_RD, + .buf = regdump, .len = TDA18271_NUM_REGS } + }; + + tda18271_i2c_gate_ctrl(fe, 1); + + /* read all registers */ + ret = i2c_transfer(priv->i2c_adap, msg, 2); + + tda18271_i2c_gate_ctrl(fe, 0); + + if (ret != 2) + tda_err("ERROR: i2c_transfer returned: %d\n", ret); + + for (i = 0; i <= TDA18271_NUM_REGS; i++) { + /* don't update write-only registers */ + if ((i != R_EB9) && + (i != R_EB16) && + (i != R_EB17) && + (i != R_EB19) && + (i != R_EB20)) + regs[i] = regdump[i]; + } + + if (tda18271_debug & DBG_REG) + tda18271_dump_regs(fe, 1); + + return (ret == 2 ? 0 : ret); +} + +int tda18271_write_regs(struct dvb_frontend *fe, int idx, int len) +{ + struct tda18271_priv *priv = fe->tuner_priv; + unsigned char *regs = priv->tda18271_regs; + unsigned char buf[TDA18271_NUM_REGS + 1]; + struct i2c_msg msg = { .addr = priv->i2c_addr, .flags = 0, + .buf = buf, .len = len + 1 }; + int i, ret; + + BUG_ON((len == 0) || (idx + len > sizeof(buf))); + + buf[0] = idx; + for (i = 1; i <= len; i++) + buf[i] = regs[idx - 1 + i]; + + tda18271_i2c_gate_ctrl(fe, 1); + + /* write registers */ + ret = i2c_transfer(priv->i2c_adap, &msg, 1); + + tda18271_i2c_gate_ctrl(fe, 0); + + if (ret != 1) + tda_err("ERROR: i2c_transfer returned: %d\n", ret); + + return (ret == 1 ? 0 : ret); +} + +/*---------------------------------------------------------------------*/ + +int tda18271_init_regs(struct dvb_frontend *fe) +{ + struct tda18271_priv *priv = fe->tuner_priv; + unsigned char *regs = priv->tda18271_regs; + + tda_dbg("initializing registers for device @ %d-%04x\n", + i2c_adapter_id(priv->i2c_adap), priv->i2c_addr); + + /* initialize registers */ + switch (priv->id) { + case TDA18271HDC1: + regs[R_ID] = 0x83; + break; + case TDA18271HDC2: + regs[R_ID] = 0x84; + break; + }; + + regs[R_TM] = 0x08; + regs[R_PL] = 0x80; + regs[R_EP1] = 0xc6; + regs[R_EP2] = 0xdf; + regs[R_EP3] = 0x16; + regs[R_EP4] = 0x60; + regs[R_EP5] = 0x80; + regs[R_CPD] = 0x80; + regs[R_CD1] = 0x00; + regs[R_CD2] = 0x00; + regs[R_CD3] = 0x00; + regs[R_MPD] = 0x00; + regs[R_MD1] = 0x00; + regs[R_MD2] = 0x00; + regs[R_MD3] = 0x00; + + switch (priv->id) { + case TDA18271HDC1: + regs[R_EB1] = 0xff; + break; + case TDA18271HDC2: + regs[R_EB1] = 0xfc; + break; + }; + + regs[R_EB2] = 0x01; + regs[R_EB3] = 0x84; + regs[R_EB4] = 0x41; + regs[R_EB5] = 0x01; + regs[R_EB6] = 0x84; + regs[R_EB7] = 0x40; + regs[R_EB8] = 0x07; + regs[R_EB9] = 0x00; + regs[R_EB10] = 0x00; + regs[R_EB11] = 0x96; + + switch (priv->id) { + case TDA18271HDC1: + regs[R_EB12] = 0x0f; + break; + case TDA18271HDC2: + regs[R_EB12] = 0x33; + break; + }; + + regs[R_EB13] = 0xc1; + regs[R_EB14] = 0x00; + regs[R_EB15] = 0x8f; + regs[R_EB16] = 0x00; + regs[R_EB17] = 0x00; + + switch (priv->id) { + case TDA18271HDC1: + regs[R_EB18] = 0x00; + break; + case TDA18271HDC2: + regs[R_EB18] = 0x8c; + break; + }; + + regs[R_EB19] = 0x00; + regs[R_EB20] = 0x20; + + switch (priv->id) { + case TDA18271HDC1: + regs[R_EB21] = 0x33; + break; + case TDA18271HDC2: + regs[R_EB21] = 0xb3; + break; + }; + + regs[R_EB22] = 0x48; + regs[R_EB23] = 0xb0; + + tda18271_write_regs(fe, 0x00, TDA18271_NUM_REGS); + + /* setup agc1 gain */ + regs[R_EB17] = 0x00; + tda18271_write_regs(fe, R_EB17, 1); + regs[R_EB17] = 0x03; + tda18271_write_regs(fe, R_EB17, 1); + regs[R_EB17] = 0x43; + tda18271_write_regs(fe, R_EB17, 1); + regs[R_EB17] = 0x4c; + tda18271_write_regs(fe, R_EB17, 1); + + /* setup agc2 gain */ + if ((priv->id) == TDA18271HDC1) { + regs[R_EB20] = 0xa0; + tda18271_write_regs(fe, R_EB20, 1); + regs[R_EB20] = 0xa7; + tda18271_write_regs(fe, R_EB20, 1); + regs[R_EB20] = 0xe7; + tda18271_write_regs(fe, R_EB20, 1); + regs[R_EB20] = 0xec; + tda18271_write_regs(fe, R_EB20, 1); + } + + /* image rejection calibration */ + + /* low-band */ + regs[R_EP3] = 0x1f; + regs[R_EP4] = 0x66; + regs[R_EP5] = 0x81; + regs[R_CPD] = 0xcc; + regs[R_CD1] = 0x6c; + regs[R_CD2] = 0x00; + regs[R_CD3] = 0x00; + regs[R_MPD] = 0xcd; + regs[R_MD1] = 0x77; + regs[R_MD2] = 0x08; + regs[R_MD3] = 0x00; + + switch (priv->id) { + case TDA18271HDC1: + tda18271_write_regs(fe, R_EP3, 11); + break; + case TDA18271HDC2: + tda18271_write_regs(fe, R_EP3, 12); + break; + }; + + if ((priv->id) == TDA18271HDC2) { + /* main pll cp source on */ + regs[R_EB4] = 0x61; + tda18271_write_regs(fe, R_EB4, 1); + msleep(1); + + /* main pll cp source off */ + regs[R_EB4] = 0x41; + tda18271_write_regs(fe, R_EB4, 1); + } + + msleep(5); /* pll locking */ + + /* launch detector */ +#if 0 + regs[R_EP1] = 0xc6; /* already set */ +#endif + tda18271_write_regs(fe, R_EP1, 1); + msleep(5); /* wanted low measurement */ + +#if 0 + regs[R_EP3] = 0x1f; /* already set */ + regs[R_EP4] = 0x66; /* already set */ +#endif + regs[R_EP5] = 0x85; + regs[R_CPD] = 0xcb; + regs[R_CD1] = 0x66; + regs[R_CD2] = 0x70; +#if 0 + regs[R_CD3] = 0x00; /* already set */ +#endif + + tda18271_write_regs(fe, R_EP3, 7); + msleep(5); /* pll locking */ + + /* launch optimization algorithm */ +#if 0 + regs[R_EP2] = 0xdf; /* already set */ +#endif + tda18271_write_regs(fe, R_EP2, 1); + msleep(30); /* image low optimization completion */ + + /* mid-band */ +#if 0 + regs[R_EP3] = 0x1f; /* already set */ + regs[R_EP4] = 0x66; /* already set */ +#endif + regs[R_EP5] = 0x82; + regs[R_CPD] = 0xa8; +#if 0 + regs[R_CD1] = 0x66; /* already set */ +#endif + regs[R_CD2] = 0x00; +#if 0 + regs[R_CD3] = 0x00; /* already set */ +#endif + regs[R_MPD] = 0xa9; + regs[R_MD1] = 0x73; + regs[R_MD2] = 0x1a; +#if 0 + regs[R_MD3] = 0x00; /* already set */ +#endif + + tda18271_write_regs(fe, R_EP3, 11); + msleep(5); /* pll locking */ + +#if 0 + regs[R_EP1] = 0xc6; /* already set */ +#endif + tda18271_write_regs(fe, R_EP1, 1); + msleep(5); /* wanted mid measurement */ + +#if 0 + regs[R_EP3] = 0x1f; /* already set */ + regs[R_EP4] = 0x66; /* already set */ +#endif + regs[R_EP5] = 0x86; + regs[R_CPD] = 0xa8; + regs[R_CD1] = 0x66; + regs[R_CD2] = 0xa0; +#if 0 + regs[R_CD3] = 0x00; /* already set */ +#endif + + tda18271_write_regs(fe, R_EP3, 7); + msleep(5); /* pll locking */ + + /* launch optimization algorithm */ +#if 0 + regs[R_EP2] = 0xdf; /* already set */ +#endif + tda18271_write_regs(fe, R_EP2, 1); + msleep(30); /* image mid optimization completion */ + + /* high-band */ +#if 0 + regs[R_EP3] = 0x1f; /* already set */ + regs[R_EP4] = 0x66; /* already set */ +#endif + regs[R_EP5] = 0x83; + regs[R_CPD] = 0x98; + regs[R_CD1] = 0x65; + regs[R_CD2] = 0x00; +#if 0 + regs[R_CD3] = 0x00; /* already set */ +#endif + regs[R_MPD] = 0x99; + regs[R_MD1] = 0x71; + regs[R_MD2] = 0xcd; +#if 0 + regs[R_MD3] = 0x00; /* already set */ +#endif + + tda18271_write_regs(fe, R_EP3, 11); + msleep(5); /* pll locking */ + + /* launch detector */ +#if 0 + regs[R_EP1] = 0xc6; /* already set */ +#endif + tda18271_write_regs(fe, R_EP1, 1); + msleep(5); /* wanted high measurement */ + +#if 0 + regs[R_EP3] = 0x1f; /* already set */ + regs[R_EP4] = 0x66; /* already set */ +#endif + regs[R_EP5] = 0x87; +#if 0 + regs[R_CPD] = 0x98; /* already set */ +#endif + regs[R_CD1] = 0x65; + regs[R_CD2] = 0x50; +#if 0 + regs[R_CD3] = 0x00; /* already set */ +#endif + + tda18271_write_regs(fe, R_EP3, 7); + msleep(5); /* pll locking */ + + /* launch optimization algorithm */ +#if 0 + regs[R_EP2] = 0xdf; /* already set */ +#endif + tda18271_write_regs(fe, R_EP2, 1); + msleep(30); /* image high optimization completion */ + + /* return to normal mode */ + regs[R_EP4] = 0x64; + tda18271_write_regs(fe, R_EP4, 1); + + /* synchronize */ +#if 0 + regs[R_EP1] = 0xc6; /* already set */ +#endif + tda18271_write_regs(fe, R_EP1, 1); + + return 0; +} + +/*---------------------------------------------------------------------*/ + +int tda18271_calc_main_pll(struct dvb_frontend *fe, u32 freq) +{ + /* sets main post divider & divider bytes, but does not write them */ + struct tda18271_priv *priv = fe->tuner_priv; + unsigned char *regs = priv->tda18271_regs; + u8 d, pd; + u32 div; + + int ret = tda18271_lookup_pll_map(fe, MAIN_PLL, &freq, &pd, &d); + if (ret < 0) + goto fail; + + regs[R_MPD] = (0x77 & pd); + + switch (priv->mode) { + case TDA18271_ANALOG: + regs[R_MPD] &= ~0x08; + break; + case TDA18271_DIGITAL: + regs[R_MPD] |= 0x08; + break; + } + + div = ((d * (freq / 1000)) << 7) / 125; + + regs[R_MD1] = 0x7f & (div >> 16); + regs[R_MD2] = 0xff & (div >> 8); + regs[R_MD3] = 0xff & div; +fail: + return ret; +} + +int tda18271_calc_cal_pll(struct dvb_frontend *fe, u32 freq) +{ + /* sets cal post divider & divider bytes, but does not write them */ + struct tda18271_priv *priv = fe->tuner_priv; + unsigned char *regs = priv->tda18271_regs; + u8 d, pd; + u32 div; + + int ret = tda18271_lookup_pll_map(fe, CAL_PLL, &freq, &pd, &d); + if (ret < 0) + goto fail; + + regs[R_CPD] = pd; + + div = ((d * (freq / 1000)) << 7) / 125; + + regs[R_CD1] = 0x7f & (div >> 16); + regs[R_CD2] = 0xff & (div >> 8); + regs[R_CD3] = 0xff & div; +fail: + return ret; +} + +/*---------------------------------------------------------------------*/ + +int tda18271_calc_bp_filter(struct dvb_frontend *fe, u32 *freq) +{ + /* sets bp filter bits, but does not write them */ + struct tda18271_priv *priv = fe->tuner_priv; + unsigned char *regs = priv->tda18271_regs; + u8 val; + + int ret = tda18271_lookup_map(fe, BP_FILTER, freq, &val); + if (ret < 0) + goto fail; + + regs[R_EP1] &= ~0x07; /* clear bp filter bits */ + regs[R_EP1] |= (0x07 & val); +fail: + return ret; +} + +int tda18271_calc_km(struct dvb_frontend *fe, u32 *freq) +{ + /* sets K & M bits, but does not write them */ + struct tda18271_priv *priv = fe->tuner_priv; + unsigned char *regs = priv->tda18271_regs; + u8 val; + + int ret = tda18271_lookup_map(fe, RF_CAL_KMCO, freq, &val); + if (ret < 0) + goto fail; + + regs[R_EB13] &= ~0x7c; /* clear k & m bits */ + regs[R_EB13] |= (0x7c & val); +fail: + return ret; +} + +int tda18271_calc_rf_band(struct dvb_frontend *fe, u32 *freq) +{ + /* sets rf band bits, but does not write them */ + struct tda18271_priv *priv = fe->tuner_priv; + unsigned char *regs = priv->tda18271_regs; + u8 val; + + int ret = tda18271_lookup_map(fe, RF_BAND, freq, &val); + if (ret < 0) + goto fail; + + regs[R_EP2] &= ~0xe0; /* clear rf band bits */ + regs[R_EP2] |= (0xe0 & (val << 5)); +fail: + return ret; +} + +int tda18271_calc_gain_taper(struct dvb_frontend *fe, u32 *freq) +{ + /* sets gain taper bits, but does not write them */ + struct tda18271_priv *priv = fe->tuner_priv; + unsigned char *regs = priv->tda18271_regs; + u8 val; + + int ret = tda18271_lookup_map(fe, GAIN_TAPER, freq, &val); + if (ret < 0) + goto fail; + + regs[R_EP2] &= ~0x1f; /* clear gain taper bits */ + regs[R_EP2] |= (0x1f & val); +fail: + return ret; +} + +int tda18271_calc_ir_measure(struct dvb_frontend *fe, u32 *freq) +{ + /* sets IR Meas bits, but does not write them */ + struct tda18271_priv *priv = fe->tuner_priv; + unsigned char *regs = priv->tda18271_regs; + u8 val; + + int ret = tda18271_lookup_map(fe, IR_MEASURE, freq, &val); + if (ret < 0) + goto fail; + + regs[R_EP5] &= ~0x07; + regs[R_EP5] |= (0x07 & val); +fail: + return ret; +} + +int tda18271_calc_rf_cal(struct dvb_frontend *fe, u32 *freq) +{ + /* sets rf cal byte (RFC_Cprog), but does not write it */ + struct tda18271_priv *priv = fe->tuner_priv; + unsigned char *regs = priv->tda18271_regs; + u8 val; + + tda18271_lookup_map(fe, RF_CAL, freq, &val); + + regs[R_EB14] = val; + + return 0; +} + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * --------------------------------------------------------------------------- + * Local variables: + * c-basic-offset: 8 + * End: + */ diff --git a/linux/drivers/media/dvb/frontends/tda18271-fe.c b/linux/drivers/media/dvb/frontends/tda18271-fe.c index f5baaab91..a6e40e969 100644 --- a/linux/drivers/media/dvb/frontends/tda18271-fe.c +++ b/linux/drivers/media/dvb/frontends/tda18271-fe.c @@ -1,7 +1,7 @@ /* tda18271-fe.c - driver for the Philips / NXP TDA18271 silicon tuner - Copyright (C) 2007 Michael Krufky (mkrufky@linuxtv.org) + Copyright (C) 2007, 2008 Michael Krufky <mkrufky@linuxtv.org> 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 @@ -25,491 +25,623 @@ int tda18271_debug; module_param_named(debug, tda18271_debug, int, 0644); -MODULE_PARM_DESC(debug, "set debug level (info=1, map=2, reg=4 (or-able))"); +MODULE_PARM_DESC(debug, "set debug level " + "(info=1, map=2, reg=4, adv=8 (or-able))"); /*---------------------------------------------------------------------*/ -static int tda18271_i2c_gate_ctrl(struct dvb_frontend *fe, int enable) +static int tda18271_init(struct dvb_frontend *fe) { struct tda18271_priv *priv = fe->tuner_priv; - enum tda18271_i2c_gate gate; - int ret = 0; + unsigned char *regs = priv->tda18271_regs; + + tda18271_read_regs(fe); + + /* test IR_CAL_OK to see if we need init */ + if ((regs[R_EP1] & 0x08) == 0) + tda18271_init_regs(fe); + + return 0; +} + +/* ------------------------------------------------------------------ */ + +static int tda18271_channel_configuration(struct dvb_frontend *fe, + u32 ifc, u32 freq, u32 bw, u8 std) +{ + struct tda18271_priv *priv = fe->tuner_priv; + unsigned char *regs = priv->tda18271_regs; + u32 N; + + /* update TV broadcast parameters */ - switch (priv->gate) { - case TDA18271_GATE_DIGITAL: - case TDA18271_GATE_ANALOG: - gate = priv->gate; + /* set standard */ + regs[R_EP3] &= ~0x1f; /* clear std bits */ + regs[R_EP3] |= std; + + /* set cal mode to normal */ + regs[R_EP4] &= ~0x03; + + /* update IF output level & IF notch frequency */ + regs[R_EP4] &= ~0x1c; /* clear if level bits */ + + switch (priv->mode) { + case TDA18271_ANALOG: + regs[R_MPD] &= ~0x80; /* IF notch = 0 */ + break; + case TDA18271_DIGITAL: + regs[R_EP4] |= 0x04; /* IF level = 1 */ + regs[R_MPD] |= 0x80; /* IF notch = 1 */ break; - case TDA18271_GATE_AUTO: - default: - switch (priv->mode) { - case TDA18271_DIGITAL: - gate = TDA18271_GATE_DIGITAL; - break; - case TDA18271_ANALOG: - default: - gate = TDA18271_GATE_ANALOG; - break; - } } + regs[R_EP4] &= ~0x80; /* FM_RFn: turn this bit on only for fm radio */ - switch (gate) { - case TDA18271_GATE_ANALOG: - if (fe->ops.analog_ops.i2c_gate_ctrl) - ret = fe->ops.analog_ops.i2c_gate_ctrl(fe, enable); - break; - case TDA18271_GATE_DIGITAL: - if (fe->ops.i2c_gate_ctrl) - ret = fe->ops.i2c_gate_ctrl(fe, enable); + /* update RF_TOP / IF_TOP */ + switch (priv->mode) { + case TDA18271_ANALOG: + regs[R_EB22] = 0x2c; break; - default: - ret = -EINVAL; + case TDA18271_DIGITAL: + regs[R_EB22] = 0x37; break; } + tda18271_write_regs(fe, R_EB22, 1); - return ret; -}; + /* --------------------------------------------------------------- */ -/*---------------------------------------------------------------------*/ + /* disable Power Level Indicator */ + regs[R_EP1] |= 0x40; + + /* frequency dependent parameters */ + + tda18271_calc_ir_measure(fe, &freq); + + tda18271_calc_bp_filter(fe, &freq); + + tda18271_calc_rf_band(fe, &freq); + + tda18271_calc_gain_taper(fe, &freq); + + /* --------------------------------------------------------------- */ + + /* dual tuner and agc1 extra configuration */ + + /* main vco when Master, cal vco when slave */ + regs[R_EB1] |= 0x04; /* FIXME: assumes master */ + + /* agc1 always active */ + regs[R_EB1] &= ~0x02; + + /* agc1 has priority on agc2 */ + regs[R_EB1] &= ~0x01; + + tda18271_write_regs(fe, R_EB1, 1); + + /* --------------------------------------------------------------- */ + + N = freq + ifc; + +#if 1 + /* FIXME: assumes master */ + tda18271_calc_main_pll(fe, N); + tda18271_write_regs(fe, R_MPD, 4); +#else + /* slave */ + tda18271_calc_cal_pll(fe, N); + tda18271_write_regs(fe, R_CPD, 4); + regs[R_MPD] = regs[R_CPD]; +#endif + + tda18271_write_regs(fe, R_TM, 7); + +#if 1 + /* main pll charge pump source */ + regs[R_EB4] |= 0x20; + tda18271_write_regs(fe, R_EB4, 1); +#else + /* cal pll charge pump source */ + regs[R_EB7] |= 0x20; + tda18271_write_regs(fe, R_EB7, 1); +#endif -static void tda18271_dump_regs(struct dvb_frontend *fe) + msleep(1); + +#if 1 + /* normal operation for the main pll */ + regs[R_EB4] &= ~0x20; + tda18271_write_regs(fe, R_EB4, 1); +#else + /* normal operation for the cal pll */ + regs[R_EB7] &= ~0x20; + tda18271_write_regs(fe, R_EB7, 1); +#endif + + msleep(5); + + return 0; +} + +static int tda18271_read_thermometer(struct dvb_frontend *fe) { struct tda18271_priv *priv = fe->tuner_priv; unsigned char *regs = priv->tda18271_regs; + int tm; + + /* switch thermometer on */ + regs[R_TM] |= 0x10; + tda18271_write_regs(fe, R_TM, 1); + + /* read thermometer info */ + tda18271_read_regs(fe); + + if ((((regs[R_TM] & 0x0f) == 0x00) && ((regs[R_TM] & 0x20) == 0x20)) || + (((regs[R_TM] & 0x0f) == 0x08) && ((regs[R_TM] & 0x20) == 0x00))) { + + if ((regs[R_TM] & 0x20) == 0x20) + regs[R_TM] &= ~0x20; + else + regs[R_TM] |= 0x20; + + tda18271_write_regs(fe, R_TM, 1); + + msleep(10); /* temperature sensing */ + + /* read thermometer info */ + tda18271_read_regs(fe); + } - tda_reg("=== TDA18271 REG DUMP ===\n"); - tda_reg("ID_BYTE = 0x%02x\n", 0xff & regs[R_ID]); - tda_reg("THERMO_BYTE = 0x%02x\n", 0xff & regs[R_TM]); - tda_reg("POWER_LEVEL_BYTE = 0x%02x\n", 0xff & regs[R_PL]); - tda_reg("EASY_PROG_BYTE_1 = 0x%02x\n", 0xff & regs[R_EP1]); - tda_reg("EASY_PROG_BYTE_2 = 0x%02x\n", 0xff & regs[R_EP2]); - tda_reg("EASY_PROG_BYTE_3 = 0x%02x\n", 0xff & regs[R_EP3]); - tda_reg("EASY_PROG_BYTE_4 = 0x%02x\n", 0xff & regs[R_EP4]); - tda_reg("EASY_PROG_BYTE_5 = 0x%02x\n", 0xff & regs[R_EP5]); - tda_reg("CAL_POST_DIV_BYTE = 0x%02x\n", 0xff & regs[R_CPD]); - tda_reg("CAL_DIV_BYTE_1 = 0x%02x\n", 0xff & regs[R_CD1]); - tda_reg("CAL_DIV_BYTE_2 = 0x%02x\n", 0xff & regs[R_CD2]); - tda_reg("CAL_DIV_BYTE_3 = 0x%02x\n", 0xff & regs[R_CD3]); - tda_reg("MAIN_POST_DIV_BYTE = 0x%02x\n", 0xff & regs[R_MPD]); - tda_reg("MAIN_DIV_BYTE_1 = 0x%02x\n", 0xff & regs[R_MD1]); - tda_reg("MAIN_DIV_BYTE_2 = 0x%02x\n", 0xff & regs[R_MD2]); - tda_reg("MAIN_DIV_BYTE_3 = 0x%02x\n", 0xff & regs[R_MD3]); + tm = tda18271_lookup_thermometer(fe); + + /* switch thermometer off */ + regs[R_TM] &= ~0x10; + tda18271_write_regs(fe, R_TM, 1); + + /* set CAL mode to normal */ + regs[R_EP4] &= ~0x03; + tda18271_write_regs(fe, R_EP4, 1); + + return tm; } -static void tda18271_read_regs(struct dvb_frontend *fe) +static int tda18271_rf_tracking_filters_correction(struct dvb_frontend *fe, + u32 freq, int tm_rfcal) { struct tda18271_priv *priv = fe->tuner_priv; + struct tda18271_rf_tracking_filter_cal *map = priv->rf_cal_state; unsigned char *regs = priv->tda18271_regs; - unsigned char buf = 0x00; - int ret; - struct i2c_msg msg[] = { - { .addr = priv->i2c_addr, .flags = 0, - .buf = &buf, .len = 1 }, - { .addr = priv->i2c_addr, .flags = I2C_M_RD, - .buf = regs, .len = 16 } - }; + int tm_current, rfcal_comp, approx, i; + u8 dc_over_dt, rf_tab; + + /* power up */ + regs[R_EP3] &= ~0xe0; /* sm = 0, sm_lt = 0, sm_xt = 0 */ + tda18271_write_regs(fe, R_EP3, 1); - tda18271_i2c_gate_ctrl(fe, 1); + /* read die current temperature */ + tm_current = tda18271_read_thermometer(fe); - /* read all registers */ - ret = i2c_transfer(priv->i2c_adap, msg, 2); + /* frequency dependent parameters */ - tda18271_i2c_gate_ctrl(fe, 0); + tda18271_calc_rf_cal(fe, &freq); + rf_tab = regs[R_EB14]; + + i = tda18271_lookup_rf_band(fe, &freq, NULL); + if (i < 0) + return -EINVAL; + + if ((0 == map[i].rf3) || (freq / 1000 < map[i].rf2)) { + approx = map[i].rf_a1 * + (freq / 1000 - map[i].rf1) + map[i].rf_b1 + rf_tab; + } else { + approx = map[i].rf_a2 * + (freq / 1000 - map[i].rf2) + map[i].rf_b2 + rf_tab; + } - if (ret != 2) - tda_err("ERROR: i2c_transfer returned: %d\n", ret); + if (approx < 0) + approx = 0; + if (approx > 255) + approx = 255; - if (tda18271_debug & DBG_REG) - tda18271_dump_regs(fe); + tda18271_lookup_map(fe, RF_CAL_DC_OVER_DT, &freq, &dc_over_dt); + + /* calculate temperature compensation */ + rfcal_comp = dc_over_dt * (tm_current - tm_rfcal); + + regs[R_EB14] = approx + rfcal_comp; + tda18271_write_regs(fe, R_EB14, 1); + + return 0; } -static void tda18271_write_regs(struct dvb_frontend *fe, int idx, int len) +static int tda18271_por(struct dvb_frontend *fe) { struct tda18271_priv *priv = fe->tuner_priv; unsigned char *regs = priv->tda18271_regs; - unsigned char buf[TDA18271_NUM_REGS+1]; - struct i2c_msg msg = { .addr = priv->i2c_addr, .flags = 0, - .buf = buf, .len = len+1 }; - int i, ret; - BUG_ON((len == 0) || (idx+len > sizeof(buf))); + /* power up detector 1 */ + regs[R_EB12] &= ~0x20; + tda18271_write_regs(fe, R_EB12, 1); - buf[0] = idx; - for (i = 1; i <= len; i++) { - buf[i] = regs[idx-1+i]; - } + regs[R_EB18] &= ~0x80; /* turn agc1 loop on */ + regs[R_EB18] &= ~0x03; /* set agc1_gain to 6 dB */ + tda18271_write_regs(fe, R_EB18, 1); - tda18271_i2c_gate_ctrl(fe, 1); + regs[R_EB21] |= 0x03; /* set agc2_gain to -6 dB */ - /* write registers */ - ret = i2c_transfer(priv->i2c_adap, &msg, 1); + /* POR mode */ + regs[R_EP3] &= ~0xe0; /* clear sm, sm_lt, sm_xt */ + regs[R_EP3] |= 0x80; /* sm = 1, sm_lt = 0, sm_xt = 0 */ + tda18271_write_regs(fe, R_EP3, 1); - tda18271_i2c_gate_ctrl(fe, 0); + /* disable 1.5 MHz low pass filter */ + regs[R_EB23] &= ~0x04; /* forcelp_fc2_en = 0 */ + regs[R_EB23] &= ~0x02; /* XXX: lp_fc[2] = 0 */ + tda18271_write_regs(fe, R_EB21, 3); - if (ret != 1) - tda_err("ERROR: i2c_transfer returned: %d\n", ret); + return 0; } -/*---------------------------------------------------------------------*/ - -static int tda18271_init_regs(struct dvb_frontend *fe) +static int tda18271_calibrate_rf(struct dvb_frontend *fe, u32 freq) { struct tda18271_priv *priv = fe->tuner_priv; unsigned char *regs = priv->tda18271_regs; + u32 N; - tda_dbg("initializing registers for device @ %d-%04x\n", - i2c_adapter_id(priv->i2c_adap), priv->i2c_addr); - - /* initialize registers */ - regs[R_ID] = 0x83; - regs[R_TM] = 0x08; - regs[R_PL] = 0x80; - regs[R_EP1] = 0xc6; - regs[R_EP2] = 0xdf; - regs[R_EP3] = 0x16; - regs[R_EP4] = 0x60; - regs[R_EP5] = 0x80; - regs[R_CPD] = 0x80; - regs[R_CD1] = 0x00; - regs[R_CD2] = 0x00; - regs[R_CD3] = 0x00; - regs[R_MPD] = 0x00; - regs[R_MD1] = 0x00; - regs[R_MD2] = 0x00; - regs[R_MD3] = 0x00; - regs[R_EB1] = 0xff; - regs[R_EB2] = 0x01; - regs[R_EB3] = 0x84; - regs[R_EB4] = 0x41; - regs[R_EB5] = 0x01; - regs[R_EB6] = 0x84; - regs[R_EB7] = 0x40; - regs[R_EB8] = 0x07; - regs[R_EB9] = 0x00; - regs[R_EB10] = 0x00; - regs[R_EB11] = 0x96; - regs[R_EB12] = 0x0f; - regs[R_EB13] = 0xc1; + /* set CAL mode to normal */ + regs[R_EP4] &= ~0x03; + tda18271_write_regs(fe, R_EP4, 1); + + /* switch off agc1 */ + regs[R_EP3] |= 0x40; /* sm_lt = 1 */ + + regs[R_EB18] |= 0x03; /* set agc1_gain to 15 dB */ + tda18271_write_regs(fe, R_EB18, 1); + + /* frequency dependent parameters */ + + tda18271_calc_bp_filter(fe, &freq); + tda18271_calc_gain_taper(fe, &freq); + tda18271_calc_rf_band(fe, &freq); + tda18271_calc_km(fe, &freq); + + tda18271_write_regs(fe, R_EP1, 3); + tda18271_write_regs(fe, R_EB13, 1); + + /* main pll charge pump source */ + regs[R_EB4] |= 0x20; + tda18271_write_regs(fe, R_EB4, 1); + + /* cal pll charge pump source */ + regs[R_EB7] |= 0x20; + tda18271_write_regs(fe, R_EB7, 1); + + /* force dcdc converter to 0 V */ regs[R_EB14] = 0x00; - regs[R_EB15] = 0x8f; - regs[R_EB16] = 0x00; - regs[R_EB17] = 0x00; - regs[R_EB18] = 0x00; - regs[R_EB19] = 0x00; - regs[R_EB20] = 0x20; - regs[R_EB21] = 0x33; - regs[R_EB22] = 0x48; - regs[R_EB23] = 0xb0; - - tda18271_write_regs(fe, 0x00, TDA18271_NUM_REGS); - /* setup AGC1 & AGC2 */ - regs[R_EB17] = 0x00; - tda18271_write_regs(fe, R_EB17, 1); - regs[R_EB17] = 0x03; - tda18271_write_regs(fe, R_EB17, 1); - regs[R_EB17] = 0x43; - tda18271_write_regs(fe, R_EB17, 1); - regs[R_EB17] = 0x4c; - tda18271_write_regs(fe, R_EB17, 1); - - regs[R_EB20] = 0xa0; - tda18271_write_regs(fe, R_EB20, 1); - regs[R_EB20] = 0xa7; - tda18271_write_regs(fe, R_EB20, 1); - regs[R_EB20] = 0xe7; - tda18271_write_regs(fe, R_EB20, 1); - regs[R_EB20] = 0xec; + tda18271_write_regs(fe, R_EB14, 1); + + /* disable plls lock */ + regs[R_EB20] &= ~0x20; tda18271_write_regs(fe, R_EB20, 1); - /* image rejection calibration */ - - /* low-band */ - regs[R_EP3] = 0x1f; - regs[R_EP4] = 0x66; - regs[R_EP5] = 0x81; - regs[R_CPD] = 0xcc; - regs[R_CD1] = 0x6c; - regs[R_CD2] = 0x00; - regs[R_CD3] = 0x00; - regs[R_MPD] = 0xcd; - regs[R_MD1] = 0x77; - regs[R_MD2] = 0x08; - regs[R_MD3] = 0x00; + /* set CAL mode to RF tracking filter calibration */ + regs[R_EP4] |= 0x03; + tda18271_write_regs(fe, R_EP4, 2); - tda18271_write_regs(fe, R_EP3, 11); - msleep(5); /* pll locking */ + /* --------------------------------------------------------------- */ - regs[R_EP1] = 0xc6; - tda18271_write_regs(fe, R_EP1, 1); - msleep(5); /* wanted low measurement */ + /* set the internal calibration signal */ + N = freq; - regs[R_EP3] = 0x1f; - regs[R_EP4] = 0x66; - regs[R_EP5] = 0x85; - regs[R_CPD] = 0xcb; - regs[R_CD1] = 0x66; - regs[R_CD2] = 0x70; - regs[R_CD3] = 0x00; + tda18271_calc_main_pll(fe, N); + tda18271_write_regs(fe, R_MPD, 4); - tda18271_write_regs(fe, R_EP3, 7); - msleep(5); /* pll locking */ + /* downconvert internal calibration */ + N += 1000000; - regs[R_EP2] = 0xdf; - tda18271_write_regs(fe, R_EP2, 1); - msleep(30); /* image low optimization completion */ - - /* mid-band */ - regs[R_EP3] = 0x1f; - regs[R_EP4] = 0x66; - regs[R_EP5] = 0x82; - regs[R_CPD] = 0xa8; - regs[R_CD1] = 0x66; - regs[R_CD2] = 0x00; - regs[R_CD3] = 0x00; - regs[R_MPD] = 0xa9; - regs[R_MD1] = 0x73; - regs[R_MD2] = 0x1a; - regs[R_MD3] = 0x00; + tda18271_calc_main_pll(fe, N); + tda18271_write_regs(fe, R_MPD, 4); - tda18271_write_regs(fe, R_EP3, 11); - msleep(5); /* pll locking */ + msleep(5); - regs[R_EP1] = 0xc6; + tda18271_write_regs(fe, R_EP2, 1); + tda18271_write_regs(fe, R_EP1, 1); + tda18271_write_regs(fe, R_EP2, 1); tda18271_write_regs(fe, R_EP1, 1); - msleep(5); /* wanted mid measurement */ - regs[R_EP3] = 0x1f; - regs[R_EP4] = 0x66; - regs[R_EP5] = 0x86; - regs[R_CPD] = 0xa8; - regs[R_CD1] = 0x66; - regs[R_CD2] = 0xa0; - regs[R_CD3] = 0x00; + /* --------------------------------------------------------------- */ - tda18271_write_regs(fe, R_EP3, 7); - msleep(5); /* pll locking */ + /* normal operation for the main pll */ + regs[R_EB4] &= ~0x20; + tda18271_write_regs(fe, R_EB4, 1); - regs[R_EP2] = 0xdf; - tda18271_write_regs(fe, R_EP2, 1); - msleep(30); /* image mid optimization completion */ - - /* high-band */ - regs[R_EP3] = 0x1f; - regs[R_EP4] = 0x66; - regs[R_EP5] = 0x83; - regs[R_CPD] = 0x98; - regs[R_CD1] = 0x65; - regs[R_CD2] = 0x00; - regs[R_CD3] = 0x00; - regs[R_MPD] = 0x99; - regs[R_MD1] = 0x71; - regs[R_MD2] = 0xcd; - regs[R_MD3] = 0x00; + /* normal operation for the cal pll */ + regs[R_EB7] &= ~0x20; + tda18271_write_regs(fe, R_EB7, 1); - tda18271_write_regs(fe, R_EP3, 11); - msleep(5); /* pll locking */ + msleep(5); /* plls locking */ - regs[R_EP1] = 0xc6; - tda18271_write_regs(fe, R_EP1, 1); - msleep(5); /* wanted high measurement */ + /* launch the rf tracking filters calibration */ + regs[R_EB20] |= 0x20; + tda18271_write_regs(fe, R_EB20, 1); - regs[R_EP3] = 0x1f; - regs[R_EP4] = 0x66; - regs[R_EP5] = 0x87; - regs[R_CPD] = 0x98; - regs[R_CD1] = 0x65; - regs[R_CD2] = 0x50; - regs[R_CD3] = 0x00; + msleep(60); /* calibration */ - tda18271_write_regs(fe, R_EP3, 7); - msleep(5); /* pll locking */ + /* --------------------------------------------------------------- */ - regs[R_EP2] = 0xdf; + /* set CAL mode to normal */ + regs[R_EP4] &= ~0x03; - tda18271_write_regs(fe, R_EP2, 1); - msleep(30); /* image high optimization completion */ + /* switch on agc1 */ + regs[R_EP3] &= ~0x40; /* sm_lt = 0 */ - regs[R_EP4] = 0x64; - tda18271_write_regs(fe, R_EP4, 1); + regs[R_EB18] &= ~0x03; /* set agc1_gain to 6 dB */ + tda18271_write_regs(fe, R_EB18, 1); - regs[R_EP1] = 0xc6; + tda18271_write_regs(fe, R_EP3, 2); + + /* synchronization */ tda18271_write_regs(fe, R_EP1, 1); - return 0; + /* get calibration result */ + tda18271_read_extended(fe); + + return regs[R_EB14]; } -static int tda18271_init(struct dvb_frontend *fe) +static int tda18271_powerscan(struct dvb_frontend *fe, + u32 *freq_in, u32 *freq_out) { struct tda18271_priv *priv = fe->tuner_priv; unsigned char *regs = priv->tda18271_regs; + int sgn, bcal, count, wait; + u8 cid_target; + u16 count_limit; + u32 freq; - tda18271_read_regs(fe); + freq = *freq_in; - /* test IR_CAL_OK to see if we need init */ - if ((regs[R_EP1] & 0x08) == 0) - tda18271_init_regs(fe); + tda18271_calc_rf_band(fe, &freq); + tda18271_calc_rf_cal(fe, &freq); + tda18271_calc_gain_taper(fe, &freq); + tda18271_lookup_cid_target(fe, &freq, &cid_target, &count_limit); - return 0; -} + tda18271_write_regs(fe, R_EP2, 1); + tda18271_write_regs(fe, R_EB14, 1); -static int tda18271_calc_main_pll(struct dvb_frontend *fe, u32 freq) -{ - /* Sets Main Post-Divider & Divider bytes, but does not write them */ - struct tda18271_priv *priv = fe->tuner_priv; - unsigned char *regs = priv->tda18271_regs; - u8 d, pd; - u32 div; + /* downconvert frequency */ + freq += 1000000; - int ret = tda18271_lookup_pll_map(MAIN_PLL, &freq, &pd, &d); - if (ret < 0) - goto fail; + tda18271_calc_main_pll(fe, freq); + tda18271_write_regs(fe, R_MPD, 4); - regs[R_MPD] = (0x77 & pd); + msleep(5); /* pll locking */ - switch (priv->mode) { - case TDA18271_ANALOG: - regs[R_MPD] &= ~0x08; - break; - case TDA18271_DIGITAL: - regs[R_MPD] |= 0x08; - break; + /* detection mode */ + regs[R_EP4] &= ~0x03; + regs[R_EP4] |= 0x01; + tda18271_write_regs(fe, R_EP4, 1); + + /* launch power detection measurement */ + tda18271_write_regs(fe, R_EP2, 1); + + /* read power detection info, stored in EB10 */ + tda18271_read_extended(fe); + + /* algorithm initialization */ + sgn = 1; + *freq_out = *freq_in; + bcal = 0; + count = 0; + wait = false; + + while ((regs[R_EB10] & 0x3f) < cid_target) { + /* downconvert updated freq to 1 MHz */ + freq = *freq_in + (sgn * count) + 1000000; + + tda18271_calc_main_pll(fe, freq); + tda18271_write_regs(fe, R_MPD, 4); + + if (wait) { + msleep(5); /* pll locking */ + wait = false; + } else +#if 0 + msleep(1); /* pll locking */ +#else + udelay(100); /* pll locking */ +#endif + + /* launch power detection measurement */ + tda18271_write_regs(fe, R_EP2, 1); + + /* read power detection info, stored in EB10 */ + tda18271_read_extended(fe); + + count += 200; + + if (count < count_limit) + continue; + + if (sgn <= 0) + break; + + sgn = -1 * sgn; + count = 200; + wait = true; } - div = ((d * (freq / 1000)) << 7) / 125; + if ((regs[R_EB10] & 0x3f) >= cid_target) { + bcal = 1; + *freq_out = freq - 1000000; + } else + bcal = 0; - regs[R_MD1] = 0x7f & (div >> 16); - regs[R_MD2] = 0xff & (div >> 8); - regs[R_MD3] = 0xff & div; -fail: - return ret; + tda_dbg("bcal = %d, freq_in = %d, freq_out = %d (freq = %d)\n", + bcal, *freq_in, *freq_out, freq); + + return bcal; } -static int tda18271_calc_cal_pll(struct dvb_frontend *fe, u32 freq) +static int tda18271_powerscan_init(struct dvb_frontend *fe) { - /* Sets Cal Post-Divider & Divider bytes, but does not write them */ struct tda18271_priv *priv = fe->tuner_priv; unsigned char *regs = priv->tda18271_regs; - u8 d, pd; - u32 div; - int ret = tda18271_lookup_pll_map(CAL_PLL, &freq, &pd, &d); - if (ret < 0) - goto fail; + /* set standard to digital */ + regs[R_EP3] &= ~0x1f; /* clear std bits */ + regs[R_EP3] |= 0x12; - regs[R_CPD] = pd; + /* set cal mode to normal */ + regs[R_EP4] &= ~0x03; - div = ((d * (freq / 1000)) << 7) / 125; + /* update IF output level & IF notch frequency */ + regs[R_EP4] &= ~0x1c; /* clear if level bits */ - regs[R_CD1] = 0x7f & (div >> 16); - regs[R_CD2] = 0xff & (div >> 8); - regs[R_CD3] = 0xff & div; -fail: - return ret; -} + tda18271_write_regs(fe, R_EP3, 2); -static int tda18271_calc_bp_filter(struct dvb_frontend *fe, u32 *freq) -{ - /* Sets BP filter bits, but does not write them */ - struct tda18271_priv *priv = fe->tuner_priv; - unsigned char *regs = priv->tda18271_regs; - u8 val; + regs[R_EB18] &= ~0x03; /* set agc1_gain to 6 dB */ + tda18271_write_regs(fe, R_EB18, 1); - int ret = tda18271_lookup_map(BP_FILTER, freq, &val); - if (ret < 0) - goto fail; + regs[R_EB21] &= ~0x03; /* set agc2_gain to -15 dB */ - regs[R_EP1] &= ~0x07; /* clear bp filter bits */ - regs[R_EP1] |= (0x07 & val); -fail: - return ret; + /* 1.5 MHz low pass filter */ + regs[R_EB23] |= 0x04; /* forcelp_fc2_en = 1 */ + regs[R_EB23] |= 0x02; /* lp_fc[2] = 1 */ + + tda18271_write_regs(fe, R_EB21, 3); + + return 0; } -static int tda18271_calc_km(struct dvb_frontend *fe, u32 *freq) +static int tda18271_rf_tracking_filters_init(struct dvb_frontend *fe, u32 freq) { - /* Sets K & M bits, but does not write them */ struct tda18271_priv *priv = fe->tuner_priv; + struct tda18271_rf_tracking_filter_cal *map = priv->rf_cal_state; unsigned char *regs = priv->tda18271_regs; - u8 val; - - int ret = tda18271_lookup_map(RF_CAL_KMCO, freq, &val); - if (ret < 0) - goto fail; + int bcal, rf, i; +#define RF1 0 +#define RF2 1 +#define RF3 2 + u32 rf_default[3]; + u32 rf_freq[3]; + u8 prog_cal[3]; + u8 prog_tab[3]; + + i = tda18271_lookup_rf_band(fe, &freq, NULL); + + if (i < 0) + return i; + + rf_default[RF1] = 1000 * map[i].rf1_def; + rf_default[RF2] = 1000 * map[i].rf2_def; + rf_default[RF3] = 1000 * map[i].rf3_def; + + for (rf = RF1; rf <= RF3; rf++) { + if (0 == rf_default[rf]) + return 0; + tda_dbg("freq = %d, rf = %d\n", freq, rf); + + /* look for optimized calibration frequency */ + bcal = tda18271_powerscan(fe, &rf_default[rf], &rf_freq[rf]); + + tda18271_calc_rf_cal(fe, &rf_freq[rf]); + prog_tab[rf] = regs[R_EB14]; + + if (1 == bcal) + prog_cal[rf] = tda18271_calibrate_rf(fe, rf_freq[rf]); + else + prog_cal[rf] = prog_tab[rf]; + + switch (rf) { + case RF1: + map[i].rf_a1 = 0; + map[i].rf_b1 = prog_cal[RF1] - prog_tab[RF1]; + map[i].rf1 = rf_freq[RF1] / 1000; + break; + case RF2: + map[i].rf_a1 = (prog_cal[RF2] - prog_tab[RF2] - + prog_cal[RF1] + prog_tab[RF1]) / + ((rf_freq[RF2] - rf_freq[RF1]) / 1000); + map[i].rf2 = rf_freq[RF2] / 1000; + break; + case RF3: + map[i].rf_a2 = (prog_cal[RF3] - prog_tab[RF3] - + prog_cal[RF2] + prog_tab[RF2]) / + ((rf_freq[RF3] - rf_freq[RF2]) / 1000); + map[i].rf_b2 = prog_cal[RF2] - prog_tab[RF2]; + map[i].rf3 = rf_freq[RF3] / 1000; + break; + default: + BUG(); + } + } - regs[R_EB13] &= ~0x7c; /* clear k & m bits */ - regs[R_EB13] |= (0x7c & val); -fail: - return ret; + return 0; } -static int tda18271_calc_rf_band(struct dvb_frontend *fe, u32 *freq) +static int tda18271_calc_rf_filter_curve(struct dvb_frontend *fe, + int *tm_rfcal) { - /* Sets RF Band bits, but does not write them */ struct tda18271_priv *priv = fe->tuner_priv; - unsigned char *regs = priv->tda18271_regs; - u8 val; + unsigned int i; - int ret = tda18271_lookup_map(RF_BAND, freq, &val); - if (ret < 0) - goto fail; + tda_info("tda18271: performing RF tracking filter calibration\n"); - regs[R_EP2] &= ~0xe0; /* clear rf band bits */ - regs[R_EP2] |= (0xe0 & (val << 5)); -fail: - return ret; -} + /* wait for die temperature stabilization */ + msleep(200); -static int tda18271_calc_gain_taper(struct dvb_frontend *fe, u32 *freq) -{ - /* Sets Gain Taper bits, but does not write them */ - struct tda18271_priv *priv = fe->tuner_priv; - unsigned char *regs = priv->tda18271_regs; - u8 val; + tda18271_powerscan_init(fe); - int ret = tda18271_lookup_map(GAIN_TAPER, freq, &val); - if (ret < 0) - goto fail; + /* rf band calibration */ + for (i = 0; priv->rf_cal_state[i].rfmax != 0; i++) + tda18271_rf_tracking_filters_init(fe, 1000 * + priv->rf_cal_state[i].rfmax); - regs[R_EP2] &= ~0x1f; /* clear gain taper bits */ - regs[R_EP2] |= (0x1f & val); -fail: - return ret; + *tm_rfcal = tda18271_read_thermometer(fe); + + return 0; } -static int tda18271_calc_ir_measure(struct dvb_frontend *fe, u32 *freq) +/* ------------------------------------------------------------------ */ + +static int tda18271_init_cal(struct dvb_frontend *fe, int *tm) { - /* Sets IR Meas bits, but does not write them */ struct tda18271_priv *priv = fe->tuner_priv; - unsigned char *regs = priv->tda18271_regs; - u8 val; - int ret = tda18271_lookup_map(IR_MEASURE, freq, &val); - if (ret < 0) - goto fail; + if (priv->cal_initialized) + return 0; - regs[R_EP5] &= ~0x07; - regs[R_EP5] |= (0x07 & val); -fail: - return ret; + /* initialization */ + tda18271_init(fe); + + tda18271_calc_rf_filter_curve(fe, tm); + + tda18271_por(fe); + + priv->cal_initialized = true; + + return 0; } -static int tda18271_calc_rf_cal(struct dvb_frontend *fe, u32 *freq) +static int tda18271c2_tune(struct dvb_frontend *fe, + u32 ifc, u32 freq, u32 bw, u8 std) { - /* Sets RF Cal bits, but does not write them */ - struct tda18271_priv *priv = fe->tuner_priv; - unsigned char *regs = priv->tda18271_regs; - u8 val; + int tm = 0; - int ret = tda18271_lookup_map(RF_CAL, freq, &val); - if (ret < 0) - goto fail; + tda_dbg("freq = %d, ifc = %d\n", freq, ifc); - regs[R_EB14] = val; -fail: - return ret; + tda18271_init_cal(fe, &tm); + + tda18271_rf_tracking_filters_correction(fe, freq, tm); + + tda18271_channel_configuration(fe, ifc, freq, bw, std); + + return 0; } -static int tda18271_tune(struct dvb_frontend *fe, - u32 ifc, u32 freq, u32 bw, u8 std) +/* ------------------------------------------------------------------ */ + +static int tda18271c1_tune(struct dvb_frontend *fe, + u32 ifc, u32 freq, u32 bw, u8 std) { struct tda18271_priv *priv = fe->tuner_priv; unsigned char *regs = priv->tda18271_regs; @@ -526,7 +658,7 @@ static int tda18271_tune(struct dvb_frontend *fe, /* RF tracking filter calibration */ - /* calculate BP_Filter */ + /* calculate bp filter */ tda18271_calc_bp_filter(fe, &freq); tda18271_write_regs(fe, R_EP1, 1); @@ -543,10 +675,10 @@ static int tda18271_tune(struct dvb_frontend *fe, regs[R_EB20] = 0xcc; tda18271_write_regs(fe, R_EB20, 1); - /* set CAL mode to RF tracking filter calibration */ + /* set cal mode to RF tracking filter calibration */ regs[R_EP4] |= 0x03; - /* calculate CAL PLL */ + /* calculate cal pll */ switch (priv->mode) { case TDA18271_ANALOG: @@ -559,7 +691,7 @@ static int tda18271_tune(struct dvb_frontend *fe, tda18271_calc_cal_pll(fe, N); - /* calculate MAIN PLL */ + /* calculate main pll */ switch (priv->mode) { case TDA18271_ANALOG: @@ -575,14 +707,14 @@ static int tda18271_tune(struct dvb_frontend *fe, tda18271_write_regs(fe, R_EP3, 11); msleep(5); /* RF tracking filter calibration initialization */ - /* search for K,M,CO for RF Calibration */ + /* search for K,M,CO for RF calibration */ tda18271_calc_km(fe, &freq); tda18271_write_regs(fe, R_EB13, 1); - /* search for RF_BAND */ + /* search for rf band */ tda18271_calc_rf_band(fe, &freq); - /* search for Gain_Taper */ + /* search for gain taper */ tda18271_calc_gain_taper(fe, &freq); tda18271_write_regs(fe, R_EP2, 1); @@ -666,9 +798,12 @@ static int tda18271_set_params(struct dvb_frontend *fe, struct dvb_frontend_parameters *params) { struct tda18271_priv *priv = fe->tuner_priv; + struct tda18271_std_map *std_map = &priv->std; u8 std; - u32 bw, sgIF = 0; - u32 freq = params->frequency; + u16 sgIF; + u32 bw, freq = params->frequency; + + BUG_ON(!priv->tune); priv->mode = TDA18271_DIGITAL; @@ -677,13 +812,13 @@ static int tda18271_set_params(struct dvb_frontend *fe, switch (params->u.vsb.modulation) { case VSB_8: case VSB_16: - std = 0x1b; /* device-specific (spec says 0x1c) */ - sgIF = 5380000; + std = std_map->atsc_6.std_bits; + sgIF = std_map->atsc_6.if_freq; break; case QAM_64: case QAM_256: - std = 0x18; /* device-specific (spec says 0x1d) */ - sgIF = 4000000; + std = std_map->qam_6.std_bits; + sgIF = std_map->qam_6.if_freq; break; default: tda_warn("modulation not set!\n"); @@ -697,19 +832,19 @@ static int tda18271_set_params(struct dvb_frontend *fe, } else if (fe->ops.info.type == FE_OFDM) { switch (params->u.ofdm.bandwidth) { case BANDWIDTH_6_MHZ: - std = 0x1b; /* device-specific (spec says 0x1c) */ bw = 6000000; - sgIF = 3300000; + std = std_map->dvbt_6.std_bits; + sgIF = std_map->dvbt_6.if_freq; break; case BANDWIDTH_7_MHZ: - std = 0x19; /* device-specific (spec says 0x1d) */ bw = 7000000; - sgIF = 3800000; + std = std_map->dvbt_7.std_bits; + sgIF = std_map->dvbt_7.if_freq; break; case BANDWIDTH_8_MHZ: - std = 0x1a; /* device-specific (spec says 0x1e) */ bw = 8000000; - sgIF = 4300000; + std = std_map->dvbt_8.std_bits; + sgIF = std_map->dvbt_8.if_freq; break; default: tda_warn("bandwidth not set!\n"); @@ -720,61 +855,64 @@ static int tda18271_set_params(struct dvb_frontend *fe, return -EINVAL; } - return tda18271_tune(fe, sgIF, freq, bw, std); + return priv->tune(fe, sgIF * 1000, freq, bw, std); } static int tda18271_set_analog_params(struct dvb_frontend *fe, struct analog_parameters *params) { struct tda18271_priv *priv = fe->tuner_priv; + struct tda18271_std_map *std_map = &priv->std; char *mode; u8 std; - u32 sgIF, freq = params->frequency * 62500; + u16 sgIF; + u32 freq = params->frequency * 62500; + + BUG_ON(!priv->tune); priv->mode = TDA18271_ANALOG; - /* see table 22 */ if (params->std & V4L2_STD_MN) { - std = 0x0d; - sgIF = 5750000; + std = std_map->atv_mn.std_bits; + sgIF = std_map->atv_mn.if_freq; mode = "MN"; } else if (params->std & V4L2_STD_B) { - std = 0x0e; - sgIF = 6750000; + std = std_map->atv_b.std_bits; + sgIF = std_map->atv_b.if_freq; mode = "B"; } else if (params->std & V4L2_STD_GH) { - std = 0x0f; - sgIF = 7750000; + std = std_map->atv_gh.std_bits; + sgIF = std_map->atv_gh.if_freq; mode = "GH"; } else if (params->std & V4L2_STD_PAL_I) { - std = 0x0f; - sgIF = 7750000; + std = std_map->atv_i.std_bits; + sgIF = std_map->atv_i.if_freq; mode = "I"; } else if (params->std & V4L2_STD_DK) { - std = 0x0f; - sgIF = 7750000; + std = std_map->atv_dk.std_bits; + sgIF = std_map->atv_dk.if_freq; mode = "DK"; } else if (params->std & V4L2_STD_SECAM_L) { - std = 0x0f; - sgIF = 7750000; + std = std_map->atv_l.std_bits; + sgIF = std_map->atv_l.if_freq; mode = "L"; } else if (params->std & V4L2_STD_SECAM_LC) { - std = 0x0f; - sgIF = 1250000; + std = std_map->atv_lc.std_bits; + sgIF = std_map->atv_lc.if_freq; mode = "L'"; } else { - std = 0x0f; - sgIF = 7750000; + std = std_map->atv_i.std_bits; + sgIF = std_map->atv_i.if_freq; mode = "xx"; } #if 0 if (params->mode == V4L2_TUNER_RADIO) - sgIF = 5500000; /* XXX: 1250000 */ + sgIF = std_map->fm_radio.if_freq; #endif tda_dbg("setting tda18271 to system %s\n", mode); - return tda18271_tune(fe, sgIF, freq, 0, std); + return priv->tune(fe, sgIF * 1000, freq, 0, std); } static int tda18271_release(struct dvb_frontend *fe) @@ -798,6 +936,75 @@ static int tda18271_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth) return 0; } +/* ------------------------------------------------------------------ */ + +#define tda18271_update_std(std_cfg, name) do { \ + if (map->std_cfg.if_freq + map->std_cfg.std_bits > 0) { \ + tda_dbg("Using custom std config for %s\n", name); \ + memcpy(&std->std_cfg, &map->std_cfg, \ + sizeof(struct tda18271_std_map_item)); \ + } } while (0) + +#define tda18271_dump_std_item(std_cfg, name) do { \ + tda_dbg("(%s) if freq = %d, std bits = 0x%02x\n", \ + name, std->std_cfg.if_freq, std->std_cfg.std_bits); \ + } while (0) + +static int tda18271_dump_std_map(struct dvb_frontend *fe) +{ + struct tda18271_priv *priv = fe->tuner_priv; + struct tda18271_std_map *std = &priv->std; + + tda_dbg("========== STANDARD MAP SETTINGS ==========\n"); +#if 0 + tda18271_dump_std_item(fm_radio, "fm"); +#endif + tda18271_dump_std_item(atv_b, "pal b"); + tda18271_dump_std_item(atv_dk, "pal dk"); + tda18271_dump_std_item(atv_gh, "pal gh"); + tda18271_dump_std_item(atv_i, "pal i"); + tda18271_dump_std_item(atv_l, "pal l"); + tda18271_dump_std_item(atv_lc, "pal l'"); + tda18271_dump_std_item(atv_mn, "atv mn"); + tda18271_dump_std_item(atsc_6, "atsc 6"); + tda18271_dump_std_item(dvbt_6, "dvbt 6"); + tda18271_dump_std_item(dvbt_7, "dvbt 7"); + tda18271_dump_std_item(dvbt_8, "dvbt 8"); + tda18271_dump_std_item(qam_6, "qam 6"); + tda18271_dump_std_item(qam_8, "qam 8"); + + return 0; +} + +static int tda18271_update_std_map(struct dvb_frontend *fe, + struct tda18271_std_map *map) +{ + struct tda18271_priv *priv = fe->tuner_priv; + struct tda18271_std_map *std = &priv->std; + + if (!map) + return -EINVAL; + +#if 0 + tda18271_update_std(fm_radio, "fm"); +#endif + tda18271_update_std(atv_b, "atv b"); + tda18271_update_std(atv_dk, "atv dk"); + tda18271_update_std(atv_gh, "atv gh"); + tda18271_update_std(atv_i, "atv i"); + tda18271_update_std(atv_l, "atv l"); + tda18271_update_std(atv_lc, "atv l'"); + tda18271_update_std(atv_mn, "atv mn"); + tda18271_update_std(atsc_6, "atsc 6"); + tda18271_update_std(dvbt_6, "dvbt 6"); + tda18271_update_std(dvbt_7, "dvbt 7"); + tda18271_update_std(dvbt_8, "dvbt 8"); + tda18271_update_std(qam_6, "qam 6"); + tda18271_update_std(qam_8, "qam 8"); + + return 0; +} + static int tda18271_get_id(struct dvb_frontend *fe) { struct tda18271_priv *priv = fe->tuner_priv; @@ -810,10 +1017,13 @@ static int tda18271_get_id(struct dvb_frontend *fe) switch (regs[R_ID] & 0x7f) { case 3: name = "TDA18271HD/C1"; + priv->id = TDA18271HDC1; + priv->tune = tda18271c1_tune; break; case 4: name = "TDA18271HD/C2"; - ret = -EPROTONOSUPPORT; + priv->id = TDA18271HDC2; + priv->tune = tda18271c2_tune; break; default: name = "Unknown device"; @@ -845,7 +1055,7 @@ static struct dvb_tuner_ops tda18271_tuner_ops = { struct dvb_frontend *tda18271_attach(struct dvb_frontend *fe, u8 addr, struct i2c_adapter *i2c, - enum tda18271_i2c_gate gate) + struct tda18271_config *cfg) { struct tda18271_priv *priv = NULL; @@ -855,16 +1065,27 @@ struct dvb_frontend *tda18271_attach(struct dvb_frontend *fe, u8 addr, priv->i2c_addr = addr; priv->i2c_adap = i2c; - priv->gate = gate; + priv->gate = (cfg) ? cfg->gate : TDA18271_GATE_AUTO; + priv->cal_initialized = false; fe->tuner_priv = priv; if (tda18271_get_id(fe) < 0) goto fail; + if (tda18271_assign_map_layout(fe) < 0) + goto fail; + memcpy(&fe->ops.tuner_ops, &tda18271_tuner_ops, sizeof(struct dvb_tuner_ops)); + /* override default std map with values in config struct */ + if ((cfg) && (cfg->std_map)) + tda18271_update_std_map(fe, cfg->std_map); + + if (tda18271_debug & DBG_MAP) + tda18271_dump_std_map(fe); + tda18271_init_regs(fe); return fe; @@ -876,7 +1097,7 @@ EXPORT_SYMBOL_GPL(tda18271_attach); MODULE_DESCRIPTION("NXP TDA18271HD analog / digital tuner driver"); MODULE_AUTHOR("Michael Krufky <mkrufky@linuxtv.org>"); MODULE_LICENSE("GPL"); -MODULE_VERSION("0.1"); +MODULE_VERSION("0.2"); /* * Overrides for Emacs so that we follow Linus's tabbing style. diff --git a/linux/drivers/media/dvb/frontends/tda18271-priv.h b/linux/drivers/media/dvb/frontends/tda18271-priv.h index 912b81e0c..6e04258ca 100644 --- a/linux/drivers/media/dvb/frontends/tda18271-priv.h +++ b/linux/drivers/media/dvb/frontends/tda18271-priv.h @@ -1,7 +1,7 @@ /* tda18271-priv.h - private header for the NXP TDA18271 silicon tuner - Copyright (C) 2007 Michael Krufky (mkrufky@linuxtv.org) + Copyright (C) 2007, 2008 Michael Krufky <mkrufky@linuxtv.org> 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 @@ -69,11 +69,33 @@ /*---------------------------------------------------------------------*/ +struct tda18271_rf_tracking_filter_cal { + u32 rfmax; + u8 rfband; + u32 rf1_def; + u32 rf2_def; + u32 rf3_def; + u32 rf1; + u32 rf2; + u32 rf3; + int rf_a1; + int rf_b1; + int rf_a2; + int rf_b2; +}; + enum tda18271_mode { TDA18271_ANALOG, TDA18271_DIGITAL, }; +struct tda18271_map_layout; + +enum tda18271_ver { + TDA18271HDC1, + TDA18271HDC2, +}; + struct tda18271_priv { u8 i2c_addr; struct i2c_adapter *i2c_adap; @@ -81,6 +103,16 @@ struct tda18271_priv { enum tda18271_mode mode; enum tda18271_i2c_gate gate; + enum tda18271_ver id; + + unsigned int cal_initialized:1; + + struct tda18271_map_layout *maps; + struct tda18271_std_map std; + struct tda18271_rf_tracking_filter_cal rf_cal_state[8]; + + int (*tune) (struct dvb_frontend *fe, + u32 ifc, u32 freq, u32 bw, u8 std); u32 frequency; u32 bandwidth; @@ -93,6 +125,7 @@ extern int tda18271_debug; #define DBG_INFO 1 #define DBG_MAP 2 #define DBG_REG 4 +#define DBG_ADV 8 #define tda_printk(kern, fmt, arg...) \ printk(kern "%s: " fmt, __FUNCTION__, ##arg) @@ -117,17 +150,48 @@ enum tda18271_map_type { /* tda18271_map */ RF_CAL, RF_CAL_KMCO, + RF_CAL_DC_OVER_DT, BP_FILTER, RF_BAND, GAIN_TAPER, IR_MEASURE, }; -extern int tda18271_lookup_pll_map(enum tda18271_map_type map_type, +extern int tda18271_lookup_pll_map(struct dvb_frontend *fe, + enum tda18271_map_type map_type, u32 *freq, u8 *post_div, u8 *div); -extern int tda18271_lookup_map(enum tda18271_map_type map_type, +extern int tda18271_lookup_map(struct dvb_frontend *fe, + enum tda18271_map_type map_type, u32 *freq, u8 *val); +extern int tda18271_lookup_thermometer(struct dvb_frontend *fe); + +extern int tda18271_lookup_rf_band(struct dvb_frontend *fe, + u32 *freq, u8 *rf_band); + +extern int tda18271_lookup_cid_target(struct dvb_frontend *fe, + u32 *freq, u8 *cid_target, + u16 *count_limit); + +extern int tda18271_assign_map_layout(struct dvb_frontend *fe); + +/*---------------------------------------------------------------------*/ + +extern int tda18271_read_regs(struct dvb_frontend *fe); +extern int tda18271_read_extended(struct dvb_frontend *fe); +extern int tda18271_write_regs(struct dvb_frontend *fe, int idx, int len); +extern int tda18271_init_regs(struct dvb_frontend *fe); + +extern int tda18271_calc_main_pll(struct dvb_frontend *fe, u32 freq); +extern int tda18271_calc_cal_pll(struct dvb_frontend *fe, u32 freq); + +extern int tda18271_calc_bp_filter(struct dvb_frontend *fe, u32 *freq); +extern int tda18271_calc_km(struct dvb_frontend *fe, u32 *freq); +extern int tda18271_calc_rf_band(struct dvb_frontend *fe, u32 *freq); +extern int tda18271_calc_gain_taper(struct dvb_frontend *fe, u32 *freq); +extern int tda18271_calc_ir_measure(struct dvb_frontend *fe, u32 *freq); +extern int tda18271_calc_rf_cal(struct dvb_frontend *fe, u32 *freq); + #endif /* __TDA18271_PRIV_H__ */ /* diff --git a/linux/drivers/media/dvb/frontends/tda18271-tables.c b/linux/drivers/media/dvb/frontends/tda18271-tables.c index f8202c40b..a39725f7b 100644 --- a/linux/drivers/media/dvb/frontends/tda18271-tables.c +++ b/linux/drivers/media/dvb/frontends/tda18271-tables.c @@ -1,7 +1,7 @@ /* tda18271-tables.c - driver for the Philips / NXP TDA18271 silicon tuner - Copyright (C) 2007 Michael Krufky (mkrufky@linuxtv.org) + Copyright (C) 2007, 2008 Michael Krufky <mkrufky@linuxtv.org> 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 @@ -33,7 +33,7 @@ struct tda18271_map { /*---------------------------------------------------------------------*/ -static struct tda18271_pll_map tda18271_main_pll[] = { +static struct tda18271_pll_map tda18271c1_main_pll[] = { { .lomax = 32000, .pd = 0x5f, .d = 0xf0 }, { .lomax = 35000, .pd = 0x5e, .d = 0xe0 }, { .lomax = 37000, .pd = 0x5d, .d = 0xd0 }, @@ -77,7 +77,51 @@ static struct tda18271_pll_map tda18271_main_pll[] = { { .lomax = 0, .pd = 0x00, .d = 0x00 }, /* end */ }; -static struct tda18271_pll_map tda18271_cal_pll[] = { +static struct tda18271_pll_map tda18271c2_main_pll[] = { + { .lomax = 33125, .pd = 0x57, .d = 0xf0 }, + { .lomax = 35500, .pd = 0x56, .d = 0xe0 }, + { .lomax = 38188, .pd = 0x55, .d = 0xd0 }, + { .lomax = 41375, .pd = 0x54, .d = 0xc0 }, + { .lomax = 45125, .pd = 0x53, .d = 0xb0 }, + { .lomax = 49688, .pd = 0x52, .d = 0xa0 }, + { .lomax = 55188, .pd = 0x51, .d = 0x90 }, + { .lomax = 62125, .pd = 0x50, .d = 0x80 }, + { .lomax = 66250, .pd = 0x47, .d = 0x78 }, + { .lomax = 71000, .pd = 0x46, .d = 0x70 }, + { .lomax = 76375, .pd = 0x45, .d = 0x68 }, + { .lomax = 82750, .pd = 0x44, .d = 0x60 }, + { .lomax = 90250, .pd = 0x43, .d = 0x58 }, + { .lomax = 99375, .pd = 0x42, .d = 0x50 }, + { .lomax = 110375, .pd = 0x41, .d = 0x48 }, + { .lomax = 124250, .pd = 0x40, .d = 0x40 }, + { .lomax = 132500, .pd = 0x37, .d = 0x3c }, + { .lomax = 142000, .pd = 0x36, .d = 0x38 }, + { .lomax = 152750, .pd = 0x35, .d = 0x34 }, + { .lomax = 165500, .pd = 0x34, .d = 0x30 }, + { .lomax = 180500, .pd = 0x33, .d = 0x2c }, + { .lomax = 198750, .pd = 0x32, .d = 0x28 }, + { .lomax = 220750, .pd = 0x31, .d = 0x24 }, + { .lomax = 248500, .pd = 0x30, .d = 0x20 }, + { .lomax = 265000, .pd = 0x27, .d = 0x1e }, + { .lomax = 284000, .pd = 0x26, .d = 0x1c }, + { .lomax = 305500, .pd = 0x25, .d = 0x1a }, + { .lomax = 331000, .pd = 0x24, .d = 0x18 }, + { .lomax = 361000, .pd = 0x23, .d = 0x16 }, + { .lomax = 397500, .pd = 0x22, .d = 0x14 }, + { .lomax = 441500, .pd = 0x21, .d = 0x12 }, + { .lomax = 497000, .pd = 0x20, .d = 0x10 }, + { .lomax = 530000, .pd = 0x17, .d = 0x0f }, + { .lomax = 568000, .pd = 0x16, .d = 0x0e }, + { .lomax = 611000, .pd = 0x15, .d = 0x0d }, + { .lomax = 662000, .pd = 0x14, .d = 0x0c }, + { .lomax = 722000, .pd = 0x13, .d = 0x0b }, + { .lomax = 795000, .pd = 0x12, .d = 0x0a }, + { .lomax = 883000, .pd = 0x11, .d = 0x09 }, + { .lomax = 994000, .pd = 0x10, .d = 0x08 }, + { .lomax = 0, .pd = 0x00, .d = 0x00 }, /* end */ +}; + +static struct tda18271_pll_map tda18271c1_cal_pll[] = { { .lomax = 33000, .pd = 0xdd, .d = 0xd0 }, { .lomax = 36000, .pd = 0xdc, .d = 0xc0 }, { .lomax = 40000, .pd = 0xdb, .d = 0xb0 }, @@ -116,6 +160,44 @@ static struct tda18271_pll_map tda18271_cal_pll[] = { { .lomax = 0, .pd = 0x00, .d = 0x00 }, /* end */ }; +static struct tda18271_pll_map tda18271c2_cal_pll[] = { + { .lomax = 33813, .pd = 0xdd, .d = 0xd0 }, + { .lomax = 36625, .pd = 0xdc, .d = 0xc0 }, + { .lomax = 39938, .pd = 0xdb, .d = 0xb0 }, + { .lomax = 43938, .pd = 0xda, .d = 0xa0 }, + { .lomax = 48813, .pd = 0xd9, .d = 0x90 }, + { .lomax = 54938, .pd = 0xd8, .d = 0x80 }, + { .lomax = 62813, .pd = 0xd3, .d = 0x70 }, + { .lomax = 67625, .pd = 0xcd, .d = 0x68 }, + { .lomax = 73250, .pd = 0xcc, .d = 0x60 }, + { .lomax = 79875, .pd = 0xcb, .d = 0x58 }, + { .lomax = 87875, .pd = 0xca, .d = 0x50 }, + { .lomax = 97625, .pd = 0xc9, .d = 0x48 }, + { .lomax = 109875, .pd = 0xc8, .d = 0x40 }, + { .lomax = 125625, .pd = 0xc3, .d = 0x38 }, + { .lomax = 135250, .pd = 0xbd, .d = 0x34 }, + { .lomax = 146500, .pd = 0xbc, .d = 0x30 }, + { .lomax = 159750, .pd = 0xbb, .d = 0x2c }, + { .lomax = 175750, .pd = 0xba, .d = 0x28 }, + { .lomax = 195250, .pd = 0xb9, .d = 0x24 }, + { .lomax = 219750, .pd = 0xb8, .d = 0x20 }, + { .lomax = 251250, .pd = 0xb3, .d = 0x1c }, + { .lomax = 270500, .pd = 0xad, .d = 0x1a }, + { .lomax = 293000, .pd = 0xac, .d = 0x18 }, + { .lomax = 319500, .pd = 0xab, .d = 0x16 }, + { .lomax = 351500, .pd = 0xaa, .d = 0x14 }, + { .lomax = 390500, .pd = 0xa9, .d = 0x12 }, + { .lomax = 439500, .pd = 0xa8, .d = 0x10 }, + { .lomax = 502500, .pd = 0xa3, .d = 0x0e }, + { .lomax = 541000, .pd = 0x9d, .d = 0x0d }, + { .lomax = 586000, .pd = 0x9c, .d = 0x0c }, + { .lomax = 639000, .pd = 0x9b, .d = 0x0b }, + { .lomax = 703000, .pd = 0x9a, .d = 0x0a }, + { .lomax = 781000, .pd = 0x99, .d = 0x09 }, + { .lomax = 879000, .pd = 0x98, .d = 0x08 }, + { .lomax = 0, .pd = 0x00, .d = 0x00 }, /* end */ +}; + static struct tda18271_map tda18271_bp_filter[] = { { .rfmax = 62000, .val = 0x00 }, { .rfmax = 84000, .val = 0x01 }, @@ -127,7 +209,7 @@ static struct tda18271_map tda18271_bp_filter[] = { { .rfmax = 0, .val = 0x00 }, /* end */ }; -static struct tda18271_map tda18271_km[] = { +static struct tda18271_map tda18271c1_km[] = { { .rfmax = 61100, .val = 0x74 }, { .rfmax = 350000, .val = 0x40 }, { .rfmax = 720000, .val = 0x30 }, @@ -135,6 +217,15 @@ static struct tda18271_map tda18271_km[] = { { .rfmax = 0, .val = 0x00 }, /* end */ }; +static struct tda18271_map tda18271c2_km[] = { + { .rfmax = 47900, .val = 0x38 }, + { .rfmax = 61100, .val = 0x44 }, + { .rfmax = 350000, .val = 0x30 }, + { .rfmax = 720000, .val = 0x24 }, + { .rfmax = 865000, .val = 0x3c }, + { .rfmax = 0, .val = 0x00 }, /* end */ +}; + static struct tda18271_map tda18271_rf_band[] = { { .rfmax = 47900, .val = 0x00 }, { .rfmax = 61100, .val = 0x01 }, @@ -236,7 +327,7 @@ static struct tda18271_map tda18271_gain_taper[] = { { .rfmax = 0, .val = 0x00 }, /* end */ }; -static struct tda18271_map tda18271_rf_cal[] = { +static struct tda18271_map tda18271c1_rf_cal[] = { { .rfmax = 41000, .val = 0x1e }, { .rfmax = 43000, .val = 0x30 }, { .rfmax = 45000, .val = 0x43 }, @@ -257,6 +348,446 @@ static struct tda18271_map tda18271_rf_cal[] = { { .rfmax = 0, .val = 0x00 }, /* end */ }; +static struct tda18271_map tda18271c2_rf_cal[] = { + { .rfmax = 41000, .val = 0x0f }, + { .rfmax = 43000, .val = 0x1c }, + { .rfmax = 45000, .val = 0x2f }, + { .rfmax = 46000, .val = 0x39 }, + { .rfmax = 47000, .val = 0x40 }, + { .rfmax = 47900, .val = 0x50 }, + { .rfmax = 49100, .val = 0x16 }, + { .rfmax = 50000, .val = 0x18 }, + { .rfmax = 51000, .val = 0x20 }, + { .rfmax = 53000, .val = 0x28 }, + { .rfmax = 55000, .val = 0x2b }, + { .rfmax = 56000, .val = 0x32 }, + { .rfmax = 57000, .val = 0x35 }, + { .rfmax = 58000, .val = 0x3e }, + { .rfmax = 59000, .val = 0x43 }, + { .rfmax = 60000, .val = 0x4e }, + { .rfmax = 61100, .val = 0x55 }, + { .rfmax = 63000, .val = 0x0f }, + { .rfmax = 64000, .val = 0x11 }, + { .rfmax = 65000, .val = 0x12 }, + { .rfmax = 66000, .val = 0x15 }, + { .rfmax = 67000, .val = 0x16 }, + { .rfmax = 68000, .val = 0x17 }, + { .rfmax = 70000, .val = 0x19 }, + { .rfmax = 71000, .val = 0x1c }, + { .rfmax = 72000, .val = 0x1d }, + { .rfmax = 73000, .val = 0x1f }, + { .rfmax = 74000, .val = 0x20 }, + { .rfmax = 75000, .val = 0x21 }, + { .rfmax = 76000, .val = 0x24 }, + { .rfmax = 77000, .val = 0x25 }, + { .rfmax = 78000, .val = 0x27 }, + { .rfmax = 80000, .val = 0x28 }, + { .rfmax = 81000, .val = 0x29 }, + { .rfmax = 82000, .val = 0x2d }, + { .rfmax = 83000, .val = 0x2e }, + { .rfmax = 84000, .val = 0x2f }, + { .rfmax = 85000, .val = 0x31 }, + { .rfmax = 86000, .val = 0x33 }, + { .rfmax = 87000, .val = 0x34 }, + { .rfmax = 88000, .val = 0x35 }, + { .rfmax = 89000, .val = 0x37 }, + { .rfmax = 90000, .val = 0x38 }, + { .rfmax = 91000, .val = 0x39 }, + { .rfmax = 93000, .val = 0x3c }, + { .rfmax = 94000, .val = 0x3e }, + { .rfmax = 95000, .val = 0x3f }, + { .rfmax = 96000, .val = 0x40 }, + { .rfmax = 97000, .val = 0x42 }, + { .rfmax = 99000, .val = 0x45 }, + { .rfmax = 100000, .val = 0x46 }, + { .rfmax = 102000, .val = 0x48 }, + { .rfmax = 103000, .val = 0x4a }, + { .rfmax = 105000, .val = 0x4d }, + { .rfmax = 106000, .val = 0x4e }, + { .rfmax = 107000, .val = 0x50 }, + { .rfmax = 108000, .val = 0x51 }, + { .rfmax = 110000, .val = 0x54 }, + { .rfmax = 111000, .val = 0x56 }, + { .rfmax = 112000, .val = 0x57 }, + { .rfmax = 113000, .val = 0x58 }, + { .rfmax = 114000, .val = 0x59 }, + { .rfmax = 115000, .val = 0x5c }, + { .rfmax = 116000, .val = 0x5d }, + { .rfmax = 117000, .val = 0x5f }, + { .rfmax = 119000, .val = 0x60 }, + { .rfmax = 120000, .val = 0x64 }, + { .rfmax = 121000, .val = 0x65 }, + { .rfmax = 122000, .val = 0x66 }, + { .rfmax = 123000, .val = 0x68 }, + { .rfmax = 124000, .val = 0x69 }, + { .rfmax = 125000, .val = 0x6c }, + { .rfmax = 126000, .val = 0x6d }, + { .rfmax = 127000, .val = 0x6e }, + { .rfmax = 128000, .val = 0x70 }, + { .rfmax = 129000, .val = 0x71 }, + { .rfmax = 130000, .val = 0x75 }, + { .rfmax = 131000, .val = 0x77 }, + { .rfmax = 132000, .val = 0x78 }, + { .rfmax = 133000, .val = 0x7b }, + { .rfmax = 134000, .val = 0x7e }, + { .rfmax = 135000, .val = 0x81 }, + { .rfmax = 136000, .val = 0x82 }, + { .rfmax = 137000, .val = 0x87 }, + { .rfmax = 138000, .val = 0x88 }, + { .rfmax = 139000, .val = 0x8d }, + { .rfmax = 140000, .val = 0x8e }, + { .rfmax = 141000, .val = 0x91 }, + { .rfmax = 142000, .val = 0x95 }, + { .rfmax = 143000, .val = 0x9a }, + { .rfmax = 144000, .val = 0x9d }, + { .rfmax = 145000, .val = 0xa1 }, + { .rfmax = 146000, .val = 0xa2 }, + { .rfmax = 147000, .val = 0xa4 }, + { .rfmax = 148000, .val = 0xa9 }, + { .rfmax = 149000, .val = 0xae }, + { .rfmax = 150000, .val = 0xb0 }, + { .rfmax = 151000, .val = 0xb1 }, + { .rfmax = 152000, .val = 0xb7 }, + { .rfmax = 153000, .val = 0xbd }, + { .rfmax = 154000, .val = 0x20 }, + { .rfmax = 155000, .val = 0x22 }, + { .rfmax = 156000, .val = 0x24 }, + { .rfmax = 157000, .val = 0x25 }, + { .rfmax = 158000, .val = 0x27 }, + { .rfmax = 159000, .val = 0x29 }, + { .rfmax = 160000, .val = 0x2c }, + { .rfmax = 161000, .val = 0x2d }, + { .rfmax = 163000, .val = 0x2e }, + { .rfmax = 164000, .val = 0x2f }, + { .rfmax = 165000, .val = 0x30 }, + { .rfmax = 166000, .val = 0x11 }, + { .rfmax = 167000, .val = 0x12 }, + { .rfmax = 168000, .val = 0x13 }, + { .rfmax = 169000, .val = 0x14 }, + { .rfmax = 170000, .val = 0x15 }, + { .rfmax = 172000, .val = 0x16 }, + { .rfmax = 173000, .val = 0x17 }, + { .rfmax = 174000, .val = 0x18 }, + { .rfmax = 175000, .val = 0x1a }, + { .rfmax = 176000, .val = 0x1b }, + { .rfmax = 178000, .val = 0x1d }, + { .rfmax = 179000, .val = 0x1e }, + { .rfmax = 180000, .val = 0x1f }, + { .rfmax = 181000, .val = 0x20 }, + { .rfmax = 182000, .val = 0x21 }, + { .rfmax = 183000, .val = 0x22 }, + { .rfmax = 184000, .val = 0x24 }, + { .rfmax = 185000, .val = 0x25 }, + { .rfmax = 186000, .val = 0x26 }, + { .rfmax = 187000, .val = 0x27 }, + { .rfmax = 188000, .val = 0x29 }, + { .rfmax = 189000, .val = 0x2a }, + { .rfmax = 190000, .val = 0x2c }, + { .rfmax = 191000, .val = 0x2d }, + { .rfmax = 192000, .val = 0x2e }, + { .rfmax = 193000, .val = 0x2f }, + { .rfmax = 194000, .val = 0x30 }, + { .rfmax = 195000, .val = 0x33 }, + { .rfmax = 196000, .val = 0x35 }, + { .rfmax = 198000, .val = 0x36 }, + { .rfmax = 200000, .val = 0x38 }, + { .rfmax = 201000, .val = 0x3c }, + { .rfmax = 202000, .val = 0x3d }, + { .rfmax = 203500, .val = 0x3e }, + { .rfmax = 206000, .val = 0x0e }, + { .rfmax = 208000, .val = 0x0f }, + { .rfmax = 212000, .val = 0x10 }, + { .rfmax = 216000, .val = 0x11 }, + { .rfmax = 217000, .val = 0x12 }, + { .rfmax = 218000, .val = 0x13 }, + { .rfmax = 220000, .val = 0x14 }, + { .rfmax = 222000, .val = 0x15 }, + { .rfmax = 225000, .val = 0x16 }, + { .rfmax = 228000, .val = 0x17 }, + { .rfmax = 231000, .val = 0x18 }, + { .rfmax = 234000, .val = 0x19 }, + { .rfmax = 235000, .val = 0x1a }, + { .rfmax = 236000, .val = 0x1b }, + { .rfmax = 237000, .val = 0x1c }, + { .rfmax = 240000, .val = 0x1d }, + { .rfmax = 242000, .val = 0x1f }, + { .rfmax = 247000, .val = 0x20 }, + { .rfmax = 249000, .val = 0x21 }, + { .rfmax = 252000, .val = 0x22 }, + { .rfmax = 253000, .val = 0x23 }, + { .rfmax = 254000, .val = 0x24 }, + { .rfmax = 256000, .val = 0x25 }, + { .rfmax = 259000, .val = 0x26 }, + { .rfmax = 262000, .val = 0x27 }, + { .rfmax = 264000, .val = 0x28 }, + { .rfmax = 267000, .val = 0x29 }, + { .rfmax = 269000, .val = 0x2a }, + { .rfmax = 271000, .val = 0x2b }, + { .rfmax = 273000, .val = 0x2c }, + { .rfmax = 275000, .val = 0x2d }, + { .rfmax = 277000, .val = 0x2e }, + { .rfmax = 279000, .val = 0x2f }, + { .rfmax = 282000, .val = 0x30 }, + { .rfmax = 284000, .val = 0x31 }, + { .rfmax = 286000, .val = 0x32 }, + { .rfmax = 287000, .val = 0x33 }, + { .rfmax = 290000, .val = 0x34 }, + { .rfmax = 293000, .val = 0x35 }, + { .rfmax = 295000, .val = 0x36 }, + { .rfmax = 297000, .val = 0x37 }, + { .rfmax = 300000, .val = 0x38 }, + { .rfmax = 303000, .val = 0x39 }, + { .rfmax = 305000, .val = 0x3a }, + { .rfmax = 306000, .val = 0x3b }, + { .rfmax = 307000, .val = 0x3c }, + { .rfmax = 310000, .val = 0x3d }, + { .rfmax = 312000, .val = 0x3e }, + { .rfmax = 315000, .val = 0x3f }, + { .rfmax = 318000, .val = 0x40 }, + { .rfmax = 320000, .val = 0x41 }, + { .rfmax = 323000, .val = 0x42 }, + { .rfmax = 324000, .val = 0x43 }, + { .rfmax = 325000, .val = 0x44 }, + { .rfmax = 327000, .val = 0x45 }, + { .rfmax = 331000, .val = 0x46 }, + { .rfmax = 334000, .val = 0x47 }, + { .rfmax = 337000, .val = 0x48 }, + { .rfmax = 339000, .val = 0x49 }, + { .rfmax = 340000, .val = 0x4a }, + { .rfmax = 341000, .val = 0x4b }, + { .rfmax = 343000, .val = 0x4c }, + { .rfmax = 345000, .val = 0x4d }, + { .rfmax = 349000, .val = 0x4e }, + { .rfmax = 352000, .val = 0x4f }, + { .rfmax = 353000, .val = 0x50 }, + { .rfmax = 355000, .val = 0x51 }, + { .rfmax = 357000, .val = 0x52 }, + { .rfmax = 359000, .val = 0x53 }, + { .rfmax = 361000, .val = 0x54 }, + { .rfmax = 362000, .val = 0x55 }, + { .rfmax = 364000, .val = 0x56 }, + { .rfmax = 368000, .val = 0x57 }, + { .rfmax = 370000, .val = 0x58 }, + { .rfmax = 372000, .val = 0x59 }, + { .rfmax = 375000, .val = 0x5a }, + { .rfmax = 376000, .val = 0x5b }, + { .rfmax = 377000, .val = 0x5c }, + { .rfmax = 379000, .val = 0x5d }, + { .rfmax = 382000, .val = 0x5e }, + { .rfmax = 384000, .val = 0x5f }, + { .rfmax = 385000, .val = 0x60 }, + { .rfmax = 386000, .val = 0x61 }, + { .rfmax = 388000, .val = 0x62 }, + { .rfmax = 390000, .val = 0x63 }, + { .rfmax = 393000, .val = 0x64 }, + { .rfmax = 394000, .val = 0x65 }, + { .rfmax = 396000, .val = 0x66 }, + { .rfmax = 397000, .val = 0x67 }, + { .rfmax = 398000, .val = 0x68 }, + { .rfmax = 400000, .val = 0x69 }, + { .rfmax = 402000, .val = 0x6a }, + { .rfmax = 403000, .val = 0x6b }, + { .rfmax = 407000, .val = 0x6c }, + { .rfmax = 408000, .val = 0x6d }, + { .rfmax = 409000, .val = 0x6e }, + { .rfmax = 410000, .val = 0x6f }, + { .rfmax = 411000, .val = 0x70 }, + { .rfmax = 412000, .val = 0x71 }, + { .rfmax = 413000, .val = 0x72 }, + { .rfmax = 414000, .val = 0x73 }, + { .rfmax = 417000, .val = 0x74 }, + { .rfmax = 418000, .val = 0x75 }, + { .rfmax = 420000, .val = 0x76 }, + { .rfmax = 422000, .val = 0x77 }, + { .rfmax = 423000, .val = 0x78 }, + { .rfmax = 424000, .val = 0x79 }, + { .rfmax = 427000, .val = 0x7a }, + { .rfmax = 428000, .val = 0x7b }, + { .rfmax = 429000, .val = 0x7d }, + { .rfmax = 432000, .val = 0x7f }, + { .rfmax = 434000, .val = 0x80 }, + { .rfmax = 435000, .val = 0x81 }, + { .rfmax = 436000, .val = 0x83 }, + { .rfmax = 437000, .val = 0x84 }, + { .rfmax = 438000, .val = 0x85 }, + { .rfmax = 439000, .val = 0x86 }, + { .rfmax = 440000, .val = 0x87 }, + { .rfmax = 441000, .val = 0x88 }, + { .rfmax = 442000, .val = 0x89 }, + { .rfmax = 445000, .val = 0x8a }, + { .rfmax = 446000, .val = 0x8b }, + { .rfmax = 447000, .val = 0x8c }, + { .rfmax = 448000, .val = 0x8e }, + { .rfmax = 449000, .val = 0x8f }, + { .rfmax = 450000, .val = 0x90 }, + { .rfmax = 452000, .val = 0x91 }, + { .rfmax = 453000, .val = 0x93 }, + { .rfmax = 454000, .val = 0x94 }, + { .rfmax = 456000, .val = 0x96 }, + { .rfmax = 457000, .val = 0x98 }, + { .rfmax = 461000, .val = 0x11 }, + { .rfmax = 468000, .val = 0x12 }, + { .rfmax = 472000, .val = 0x13 }, + { .rfmax = 473000, .val = 0x14 }, + { .rfmax = 474000, .val = 0x15 }, + { .rfmax = 481000, .val = 0x16 }, + { .rfmax = 486000, .val = 0x17 }, + { .rfmax = 491000, .val = 0x18 }, + { .rfmax = 498000, .val = 0x19 }, + { .rfmax = 499000, .val = 0x1a }, + { .rfmax = 501000, .val = 0x1b }, + { .rfmax = 506000, .val = 0x1c }, + { .rfmax = 511000, .val = 0x1d }, + { .rfmax = 516000, .val = 0x1e }, + { .rfmax = 520000, .val = 0x1f }, + { .rfmax = 521000, .val = 0x20 }, + { .rfmax = 525000, .val = 0x21 }, + { .rfmax = 529000, .val = 0x22 }, + { .rfmax = 533000, .val = 0x23 }, + { .rfmax = 539000, .val = 0x24 }, + { .rfmax = 541000, .val = 0x25 }, + { .rfmax = 547000, .val = 0x26 }, + { .rfmax = 549000, .val = 0x27 }, + { .rfmax = 551000, .val = 0x28 }, + { .rfmax = 556000, .val = 0x29 }, + { .rfmax = 561000, .val = 0x2a }, + { .rfmax = 563000, .val = 0x2b }, + { .rfmax = 565000, .val = 0x2c }, + { .rfmax = 569000, .val = 0x2d }, + { .rfmax = 571000, .val = 0x2e }, + { .rfmax = 577000, .val = 0x2f }, + { .rfmax = 580000, .val = 0x30 }, + { .rfmax = 582000, .val = 0x31 }, + { .rfmax = 584000, .val = 0x32 }, + { .rfmax = 588000, .val = 0x33 }, + { .rfmax = 591000, .val = 0x34 }, + { .rfmax = 596000, .val = 0x35 }, + { .rfmax = 598000, .val = 0x36 }, + { .rfmax = 603000, .val = 0x37 }, + { .rfmax = 604000, .val = 0x38 }, + { .rfmax = 606000, .val = 0x39 }, + { .rfmax = 612000, .val = 0x3a }, + { .rfmax = 615000, .val = 0x3b }, + { .rfmax = 617000, .val = 0x3c }, + { .rfmax = 621000, .val = 0x3d }, + { .rfmax = 622000, .val = 0x3e }, + { .rfmax = 625000, .val = 0x3f }, + { .rfmax = 632000, .val = 0x40 }, + { .rfmax = 633000, .val = 0x41 }, + { .rfmax = 634000, .val = 0x42 }, + { .rfmax = 642000, .val = 0x43 }, + { .rfmax = 643000, .val = 0x44 }, + { .rfmax = 647000, .val = 0x45 }, + { .rfmax = 650000, .val = 0x46 }, + { .rfmax = 652000, .val = 0x47 }, + { .rfmax = 657000, .val = 0x48 }, + { .rfmax = 661000, .val = 0x49 }, + { .rfmax = 662000, .val = 0x4a }, + { .rfmax = 665000, .val = 0x4b }, + { .rfmax = 667000, .val = 0x4c }, + { .rfmax = 670000, .val = 0x4d }, + { .rfmax = 673000, .val = 0x4e }, + { .rfmax = 676000, .val = 0x4f }, + { .rfmax = 677000, .val = 0x50 }, + { .rfmax = 681000, .val = 0x51 }, + { .rfmax = 683000, .val = 0x52 }, + { .rfmax = 686000, .val = 0x53 }, + { .rfmax = 688000, .val = 0x54 }, + { .rfmax = 689000, .val = 0x55 }, + { .rfmax = 691000, .val = 0x56 }, + { .rfmax = 695000, .val = 0x57 }, + { .rfmax = 698000, .val = 0x58 }, + { .rfmax = 703000, .val = 0x59 }, + { .rfmax = 704000, .val = 0x5a }, + { .rfmax = 705000, .val = 0x5b }, + { .rfmax = 707000, .val = 0x5c }, + { .rfmax = 710000, .val = 0x5d }, + { .rfmax = 712000, .val = 0x5e }, + { .rfmax = 717000, .val = 0x5f }, + { .rfmax = 718000, .val = 0x60 }, + { .rfmax = 721000, .val = 0x61 }, + { .rfmax = 722000, .val = 0x62 }, + { .rfmax = 723000, .val = 0x63 }, + { .rfmax = 725000, .val = 0x64 }, + { .rfmax = 727000, .val = 0x65 }, + { .rfmax = 730000, .val = 0x66 }, + { .rfmax = 732000, .val = 0x67 }, + { .rfmax = 735000, .val = 0x68 }, + { .rfmax = 740000, .val = 0x69 }, + { .rfmax = 741000, .val = 0x6a }, + { .rfmax = 742000, .val = 0x6b }, + { .rfmax = 743000, .val = 0x6c }, + { .rfmax = 745000, .val = 0x6d }, + { .rfmax = 747000, .val = 0x6e }, + { .rfmax = 748000, .val = 0x6f }, + { .rfmax = 750000, .val = 0x70 }, + { .rfmax = 752000, .val = 0x71 }, + { .rfmax = 754000, .val = 0x72 }, + { .rfmax = 757000, .val = 0x73 }, + { .rfmax = 758000, .val = 0x74 }, + { .rfmax = 760000, .val = 0x75 }, + { .rfmax = 763000, .val = 0x76 }, + { .rfmax = 764000, .val = 0x77 }, + { .rfmax = 766000, .val = 0x78 }, + { .rfmax = 767000, .val = 0x79 }, + { .rfmax = 768000, .val = 0x7a }, + { .rfmax = 773000, .val = 0x7b }, + { .rfmax = 774000, .val = 0x7c }, + { .rfmax = 776000, .val = 0x7d }, + { .rfmax = 777000, .val = 0x7e }, + { .rfmax = 778000, .val = 0x7f }, + { .rfmax = 779000, .val = 0x80 }, + { .rfmax = 781000, .val = 0x81 }, + { .rfmax = 783000, .val = 0x82 }, + { .rfmax = 784000, .val = 0x83 }, + { .rfmax = 785000, .val = 0x84 }, + { .rfmax = 786000, .val = 0x85 }, + { .rfmax = 793000, .val = 0x86 }, + { .rfmax = 794000, .val = 0x87 }, + { .rfmax = 795000, .val = 0x88 }, + { .rfmax = 797000, .val = 0x89 }, + { .rfmax = 799000, .val = 0x8a }, + { .rfmax = 801000, .val = 0x8b }, + { .rfmax = 802000, .val = 0x8c }, + { .rfmax = 803000, .val = 0x8d }, + { .rfmax = 804000, .val = 0x8e }, + { .rfmax = 810000, .val = 0x90 }, + { .rfmax = 811000, .val = 0x91 }, + { .rfmax = 812000, .val = 0x92 }, + { .rfmax = 814000, .val = 0x93 }, + { .rfmax = 816000, .val = 0x94 }, + { .rfmax = 817000, .val = 0x96 }, + { .rfmax = 818000, .val = 0x97 }, + { .rfmax = 820000, .val = 0x98 }, + { .rfmax = 821000, .val = 0x99 }, + { .rfmax = 822000, .val = 0x9a }, + { .rfmax = 828000, .val = 0x9b }, + { .rfmax = 829000, .val = 0x9d }, + { .rfmax = 830000, .val = 0x9f }, + { .rfmax = 831000, .val = 0xa0 }, + { .rfmax = 833000, .val = 0xa1 }, + { .rfmax = 835000, .val = 0xa2 }, + { .rfmax = 836000, .val = 0xa3 }, + { .rfmax = 837000, .val = 0xa4 }, + { .rfmax = 838000, .val = 0xa6 }, + { .rfmax = 840000, .val = 0xa8 }, + { .rfmax = 842000, .val = 0xa9 }, + { .rfmax = 845000, .val = 0xaa }, + { .rfmax = 846000, .val = 0xab }, + { .rfmax = 847000, .val = 0xad }, + { .rfmax = 848000, .val = 0xae }, + { .rfmax = 852000, .val = 0xaf }, + { .rfmax = 853000, .val = 0xb0 }, + { .rfmax = 858000, .val = 0xb1 }, + { .rfmax = 860000, .val = 0xb2 }, + { .rfmax = 861000, .val = 0xb3 }, + { .rfmax = 862000, .val = 0xb4 }, + { .rfmax = 863000, .val = 0xb6 }, + { .rfmax = 864000, .val = 0xb8 }, + { .rfmax = 865000, .val = 0xb9 }, + { .rfmax = 0, .val = 0x00 }, /* end */ +}; + static struct tda18271_map tda18271_ir_measure[] = { { .rfmax = 30000, .val = 4 }, { .rfmax = 200000, .val = 5 }, @@ -265,23 +796,293 @@ static struct tda18271_map tda18271_ir_measure[] = { { .rfmax = 0, .val = 0 }, /* end */ }; +static struct tda18271_map tda18271_rf_cal_dc_over_dt[] = { + { .rfmax = 47900, .val = 0x00 }, + { .rfmax = 55000, .val = 0x00 }, + { .rfmax = 61100, .val = 0x0a }, + { .rfmax = 64000, .val = 0x0a }, + { .rfmax = 82000, .val = 0x14 }, + { .rfmax = 84000, .val = 0x19 }, + { .rfmax = 119000, .val = 0x1c }, + { .rfmax = 124000, .val = 0x20 }, + { .rfmax = 129000, .val = 0x2a }, + { .rfmax = 134000, .val = 0x32 }, + { .rfmax = 139000, .val = 0x39 }, + { .rfmax = 144000, .val = 0x3e }, + { .rfmax = 149000, .val = 0x3f }, + { .rfmax = 152600, .val = 0x40 }, + { .rfmax = 154000, .val = 0x40 }, + { .rfmax = 164700, .val = 0x41 }, + { .rfmax = 203500, .val = 0x32 }, + { .rfmax = 353000, .val = 0x19 }, + { .rfmax = 356000, .val = 0x1a }, + { .rfmax = 359000, .val = 0x1b }, + { .rfmax = 363000, .val = 0x1c }, + { .rfmax = 366000, .val = 0x1d }, + { .rfmax = 369000, .val = 0x1e }, + { .rfmax = 373000, .val = 0x1f }, + { .rfmax = 376000, .val = 0x20 }, + { .rfmax = 379000, .val = 0x21 }, + { .rfmax = 383000, .val = 0x22 }, + { .rfmax = 386000, .val = 0x23 }, + { .rfmax = 389000, .val = 0x24 }, + { .rfmax = 393000, .val = 0x25 }, + { .rfmax = 396000, .val = 0x26 }, + { .rfmax = 399000, .val = 0x27 }, + { .rfmax = 402000, .val = 0x28 }, + { .rfmax = 404000, .val = 0x29 }, + { .rfmax = 407000, .val = 0x2a }, + { .rfmax = 409000, .val = 0x2b }, + { .rfmax = 412000, .val = 0x2c }, + { .rfmax = 414000, .val = 0x2d }, + { .rfmax = 417000, .val = 0x2e }, + { .rfmax = 419000, .val = 0x2f }, + { .rfmax = 422000, .val = 0x30 }, + { .rfmax = 424000, .val = 0x31 }, + { .rfmax = 427000, .val = 0x32 }, + { .rfmax = 429000, .val = 0x33 }, + { .rfmax = 432000, .val = 0x34 }, + { .rfmax = 434000, .val = 0x35 }, + { .rfmax = 437000, .val = 0x36 }, + { .rfmax = 439000, .val = 0x37 }, + { .rfmax = 442000, .val = 0x38 }, + { .rfmax = 444000, .val = 0x39 }, + { .rfmax = 447000, .val = 0x3a }, + { .rfmax = 449000, .val = 0x3b }, + { .rfmax = 457800, .val = 0x3c }, + { .rfmax = 465000, .val = 0x0f }, + { .rfmax = 477000, .val = 0x12 }, + { .rfmax = 483000, .val = 0x14 }, + { .rfmax = 502000, .val = 0x19 }, + { .rfmax = 508000, .val = 0x1b }, + { .rfmax = 519000, .val = 0x1c }, + { .rfmax = 522000, .val = 0x1d }, + { .rfmax = 524000, .val = 0x1e }, + { .rfmax = 534000, .val = 0x1f }, + { .rfmax = 549000, .val = 0x20 }, + { .rfmax = 554000, .val = 0x22 }, + { .rfmax = 584000, .val = 0x24 }, + { .rfmax = 589000, .val = 0x26 }, + { .rfmax = 658000, .val = 0x27 }, + { .rfmax = 664000, .val = 0x2c }, + { .rfmax = 669000, .val = 0x2d }, + { .rfmax = 699000, .val = 0x2e }, + { .rfmax = 704000, .val = 0x30 }, + { .rfmax = 709000, .val = 0x31 }, + { .rfmax = 714000, .val = 0x32 }, + { .rfmax = 724000, .val = 0x33 }, + { .rfmax = 729000, .val = 0x36 }, + { .rfmax = 739000, .val = 0x38 }, + { .rfmax = 744000, .val = 0x39 }, + { .rfmax = 749000, .val = 0x3b }, + { .rfmax = 754000, .val = 0x3c }, + { .rfmax = 759000, .val = 0x3d }, + { .rfmax = 764000, .val = 0x3e }, + { .rfmax = 769000, .val = 0x3f }, + { .rfmax = 774000, .val = 0x40 }, + { .rfmax = 779000, .val = 0x41 }, + { .rfmax = 784000, .val = 0x43 }, + { .rfmax = 789000, .val = 0x46 }, + { .rfmax = 794000, .val = 0x48 }, + { .rfmax = 799000, .val = 0x4b }, + { .rfmax = 804000, .val = 0x4f }, + { .rfmax = 809000, .val = 0x54 }, + { .rfmax = 814000, .val = 0x59 }, + { .rfmax = 819000, .val = 0x5d }, + { .rfmax = 824000, .val = 0x61 }, + { .rfmax = 829000, .val = 0x68 }, + { .rfmax = 834000, .val = 0x6e }, + { .rfmax = 839000, .val = 0x75 }, + { .rfmax = 844000, .val = 0x7e }, + { .rfmax = 849000, .val = 0x82 }, + { .rfmax = 854000, .val = 0x84 }, + { .rfmax = 859000, .val = 0x8f }, + { .rfmax = 865000, .val = 0x9a }, + { .rfmax = 0, .val = 0x00 }, /* end */ +}; + +/*---------------------------------------------------------------------*/ + +struct tda18271_thermo_map { + u8 d; + u8 r0; + u8 r1; +}; + +static struct tda18271_thermo_map tda18271_thermometer[] = { + { .d = 0x00, .r0 = 60, .r1 = 92 }, + { .d = 0x01, .r0 = 62, .r1 = 94 }, + { .d = 0x02, .r0 = 66, .r1 = 98 }, + { .d = 0x03, .r0 = 64, .r1 = 96 }, + { .d = 0x04, .r0 = 74, .r1 = 106 }, + { .d = 0x05, .r0 = 72, .r1 = 104 }, + { .d = 0x06, .r0 = 68, .r1 = 100 }, + { .d = 0x07, .r0 = 70, .r1 = 102 }, + { .d = 0x08, .r0 = 90, .r1 = 122 }, + { .d = 0x09, .r0 = 88, .r1 = 120 }, + { .d = 0x0a, .r0 = 84, .r1 = 116 }, + { .d = 0x0b, .r0 = 86, .r1 = 118 }, + { .d = 0x0c, .r0 = 76, .r1 = 108 }, + { .d = 0x0d, .r0 = 78, .r1 = 110 }, + { .d = 0x0e, .r0 = 82, .r1 = 114 }, + { .d = 0x0f, .r0 = 80, .r1 = 112 }, + { .d = 0x00, .r0 = 0, .r1 = 0 }, /* end */ +}; + +int tda18271_lookup_thermometer(struct dvb_frontend *fe) +{ + struct tda18271_priv *priv = fe->tuner_priv; + unsigned char *regs = priv->tda18271_regs; + int val, i = 0; + + while (tda18271_thermometer[i].d < (regs[R_TM] & 0x0f)) { + if (tda18271_thermometer[i + 1].d == 0) + break; + i++; + } + + if ((regs[R_TM] & 0x20) == 0x20) + val = tda18271_thermometer[i].r1; + else + val = tda18271_thermometer[i].r0; + + tda_map("(%d) tm = %d\n", i, val); + + return val; +} + +/*---------------------------------------------------------------------*/ + +struct tda18271_cid_target_map { + u32 rfmax; + u8 target; + u16 limit; +}; + +static struct tda18271_cid_target_map tda18271_cid_target[] = { + { .rfmax = 46000, .target = 0x04, .limit = 1800 }, + { .rfmax = 52200, .target = 0x0a, .limit = 1500 }, + { .rfmax = 79100, .target = 0x01, .limit = 4000 }, + { .rfmax = 136800, .target = 0x18, .limit = 4000 }, + { .rfmax = 156700, .target = 0x18, .limit = 4000 }, + { .rfmax = 156700, .target = 0x18, .limit = 4000 }, + { .rfmax = 186250, .target = 0x0a, .limit = 4000 }, + { .rfmax = 230000, .target = 0x0a, .limit = 4000 }, + { .rfmax = 345000, .target = 0x18, .limit = 4000 }, + { .rfmax = 426000, .target = 0x0e, .limit = 4000 }, + { .rfmax = 489500, .target = 0x1e, .limit = 4000 }, + { .rfmax = 697500, .target = 0x32, .limit = 4000 }, + { .rfmax = 842000, .target = 0x3a, .limit = 4000 }, + { .rfmax = 0, .target = 0x00, .limit = 0 }, /* end */ +}; + +int tda18271_lookup_cid_target(struct dvb_frontend *fe, + u32 *freq, u8 *cid_target, u16 *count_limit) +{ + int i = 0; + + while ((tda18271_cid_target[i].rfmax * 1000) < *freq) { + if (tda18271_cid_target[i + 1].rfmax == 0) + break; + i++; + } + *cid_target = tda18271_cid_target[i].target; + *count_limit = tda18271_cid_target[i].limit; + + tda_map("(%d) cid_target = %02x, count_limit = %d\n", i, + tda18271_cid_target[i].target, tda18271_cid_target[i].limit); + + return 0; +} + +/*---------------------------------------------------------------------*/ + +static struct tda18271_rf_tracking_filter_cal tda18271_rf_band_template[] = { + { .rfmax = 47900, .rfband = 0x00, + .rf1_def = 46000, .rf2_def = 0, .rf3_def = 0 }, + { .rfmax = 61100, .rfband = 0x01, + .rf1_def = 52200, .rf2_def = 0, .rf3_def = 0 }, + { .rfmax = 152600, .rfband = 0x02, + .rf1_def = 70100, .rf2_def = 136800, .rf3_def = 0 }, + { .rfmax = 164700, .rfband = 0x03, + .rf1_def = 156700, .rf2_def = 0, .rf3_def = 0 }, + { .rfmax = 203500, .rfband = 0x04, + .rf1_def = 186250, .rf2_def = 0, .rf3_def = 0 }, + { .rfmax = 457800, .rfband = 0x05, + .rf1_def = 230000, .rf2_def = 345000, .rf3_def = 426000 }, + { .rfmax = 865000, .rfband = 0x06, + .rf1_def = 489500, .rf2_def = 697500, .rf3_def = 842000 }, + { .rfmax = 0, .rfband = 0x00, + .rf1_def = 0, .rf2_def = 0, .rf3_def = 0 }, /* end */ +}; + +int tda18271_lookup_rf_band(struct dvb_frontend *fe, u32 *freq, u8 *rf_band) +{ + struct tda18271_priv *priv = fe->tuner_priv; + struct tda18271_rf_tracking_filter_cal *map = priv->rf_cal_state; + int i = 0; + + while ((map[i].rfmax * 1000) < *freq) { + if (tda18271_debug & DBG_ADV) + tda_map("(%d) rfmax = %d < freq = %d, " + "rf1_def = %d, rf2_def = %d, rf3_def = %d, " + "rf1 = %d, rf2 = %d, rf3 = %d, " + "rf_a1 = %d, rf_a2 = %d, " + "rf_b1 = %d, rf_b2 = %d\n", + i, map[i].rfmax * 1000, *freq, + map[i].rf1_def, map[i].rf2_def, map[i].rf3_def, + map[i].rf1, map[i].rf2, map[i].rf3, + map[i].rf_a1, map[i].rf_a2, + map[i].rf_b1, map[i].rf_b2); + if (map[i].rfmax == 0) + return -EINVAL; + i++; + } + if (rf_band) + *rf_band = map[i].rfband; + + tda_map("(%d) rf_band = %02x\n", i, map[i].rfband); + + return i; +} + /*---------------------------------------------------------------------*/ -int tda18271_lookup_pll_map(enum tda18271_map_type map_type, +struct tda18271_map_layout { + struct tda18271_pll_map *main_pll; + struct tda18271_pll_map *cal_pll; + + struct tda18271_map *rf_cal; + struct tda18271_map *rf_cal_kmco; + struct tda18271_map *rf_cal_dc_over_dt; + + struct tda18271_map *bp_filter; + struct tda18271_map *rf_band; + struct tda18271_map *gain_taper; + struct tda18271_map *ir_measure; +}; + +/*---------------------------------------------------------------------*/ + +int tda18271_lookup_pll_map(struct dvb_frontend *fe, + enum tda18271_map_type map_type, u32 *freq, u8 *post_div, u8 *div) { + struct tda18271_priv *priv = fe->tuner_priv; struct tda18271_pll_map *map = NULL; unsigned int i = 0; char *map_name; int ret = 0; + BUG_ON(!priv->maps); + switch (map_type) { case MAIN_PLL: - map = tda18271_main_pll; + map = priv->maps->main_pll; map_name = "main_pll"; break; case CAL_PLL: - map = tda18271_cal_pll; + map = priv->maps->cal_pll; map_name = "cal_pll"; break; default: @@ -297,7 +1098,7 @@ int tda18271_lookup_pll_map(enum tda18271_map_type map_type, } while ((map[i].lomax * 1000) < *freq) { - if (map[i].lomax == 0) { + if (map[i + 1].lomax == 0) { tda_map("%s: frequency (%d) out of range\n", map_name, *freq); ret = -ERANGE; @@ -308,44 +1109,53 @@ int tda18271_lookup_pll_map(enum tda18271_map_type map_type, *post_div = map[i].pd; *div = map[i].d; - tda_map("%s: post div = 0x%02x, div = 0x%02x\n", - map_name, *post_div, *div); + tda_map("(%d) %s: post div = 0x%02x, div = 0x%02x\n", + i, map_name, *post_div, *div); fail: return ret; } -int tda18271_lookup_map(enum tda18271_map_type map_type, u32 *freq, u8 *val) +int tda18271_lookup_map(struct dvb_frontend *fe, + enum tda18271_map_type map_type, + u32 *freq, u8 *val) { + struct tda18271_priv *priv = fe->tuner_priv; struct tda18271_map *map = NULL; unsigned int i = 0; char *map_name; int ret = 0; + BUG_ON(!priv->maps); + switch (map_type) { case BP_FILTER: - map = tda18271_bp_filter; + map = priv->maps->bp_filter; map_name = "bp_filter"; break; case RF_CAL_KMCO: - map = tda18271_km; + map = priv->maps->rf_cal_kmco; map_name = "km"; break; case RF_BAND: - map = tda18271_rf_band; + map = priv->maps->rf_band; map_name = "rf_band"; break; case GAIN_TAPER: - map = tda18271_gain_taper; + map = priv->maps->gain_taper; map_name = "gain_taper"; break; case RF_CAL: - map = tda18271_rf_cal; + map = priv->maps->rf_cal; map_name = "rf_cal"; break; case IR_MEASURE: - map = tda18271_ir_measure; + map = priv->maps->ir_measure; map_name = "ir_measure"; break; + case RF_CAL_DC_OVER_DT: + map = priv->maps->rf_cal_dc_over_dt; + map_name = "rf_cal_dc_over_dt"; + break; default: /* we should never get here */ map_name = "undefined"; @@ -359,7 +1169,7 @@ int tda18271_lookup_map(enum tda18271_map_type map_type, u32 *freq, u8 *val) } while ((map[i].rfmax * 1000) < *freq) { - if (map[i].rfmax == 0) { + if (map[i + 1].rfmax == 0) { tda_map("%s: frequency (%d) out of range\n", map_name, *freq); ret = -ERANGE; @@ -369,11 +1179,107 @@ int tda18271_lookup_map(enum tda18271_map_type map_type, u32 *freq, u8 *val) } *val = map[i].val; - tda_map("%s: 0x%02x\n", map_name, *val); + tda_map("(%d) %s: 0x%02x\n", i, map_name, *val); fail: return ret; } +/*---------------------------------------------------------------------*/ + +static struct tda18271_std_map tda18271c1_std_map = { +#if 0 + .fm_radio = { .if_freq = 1250, .std_bits = 0x18 }, +#endif + .atv_b = { .if_freq = 6750, .std_bits = 0x0e }, + .atv_dk = { .if_freq = 7750, .std_bits = 0x0f }, + .atv_gh = { .if_freq = 7750, .std_bits = 0x0f }, + .atv_i = { .if_freq = 7750, .std_bits = 0x0f }, + .atv_l = { .if_freq = 7750, .std_bits = 0x0f }, + .atv_lc = { .if_freq = 1250, .std_bits = 0x0f }, + .atv_mn = { .if_freq = 5750, .std_bits = 0x0d }, + .atsc_6 = { .if_freq = 3250, .std_bits = 0x1c }, + .dvbt_6 = { .if_freq = 3300, .std_bits = 0x1c }, + .dvbt_7 = { .if_freq = 3800, .std_bits = 0x1d }, + .dvbt_8 = { .if_freq = 4300, .std_bits = 0x1e }, + .qam_6 = { .if_freq = 4000, .std_bits = 0x1d }, + .qam_8 = { .if_freq = 5000, .std_bits = 0x1f }, +}; + +static struct tda18271_std_map tda18271c2_std_map = { +#if 0 + .fm_radio = { .if_freq = 1250, .std_bits = 0x18 }, +#endif + .atv_b = { .if_freq = 6000, .std_bits = 0x0d }, + .atv_dk = { .if_freq = 6900, .std_bits = 0x0e }, + .atv_gh = { .if_freq = 7100, .std_bits = 0x0e }, + .atv_i = { .if_freq = 7250, .std_bits = 0x0e }, + .atv_l = { .if_freq = 6900, .std_bits = 0x0e }, + .atv_lc = { .if_freq = 1250, .std_bits = 0x0e }, + .atv_mn = { .if_freq = 5400, .std_bits = 0x0c }, + .atsc_6 = { .if_freq = 3250, .std_bits = 0x1c }, + .dvbt_6 = { .if_freq = 3300, .std_bits = 0x1c }, + .dvbt_7 = { .if_freq = 3500, .std_bits = 0x1c }, + .dvbt_8 = { .if_freq = 4000, .std_bits = 0x1d }, + .qam_6 = { .if_freq = 4000, .std_bits = 0x1d }, + .qam_8 = { .if_freq = 5000, .std_bits = 0x1f }, +}; + +/*---------------------------------------------------------------------*/ + +static struct tda18271_map_layout tda18271c1_map_layout = { + .main_pll = tda18271c1_main_pll, + .cal_pll = tda18271c1_cal_pll, + + .rf_cal = tda18271c1_rf_cal, + .rf_cal_kmco = tda18271c1_km, + + .bp_filter = tda18271_bp_filter, + .rf_band = tda18271_rf_band, + .gain_taper = tda18271_gain_taper, + .ir_measure = tda18271_ir_measure, +}; + +static struct tda18271_map_layout tda18271c2_map_layout = { + .main_pll = tda18271c2_main_pll, + .cal_pll = tda18271c2_cal_pll, + + .rf_cal = tda18271c2_rf_cal, + .rf_cal_kmco = tda18271c2_km, + + .rf_cal_dc_over_dt = tda18271_rf_cal_dc_over_dt, + + .bp_filter = tda18271_bp_filter, + .rf_band = tda18271_rf_band, + .gain_taper = tda18271_gain_taper, + .ir_measure = tda18271_ir_measure, +}; + +int tda18271_assign_map_layout(struct dvb_frontend *fe) +{ + struct tda18271_priv *priv = fe->tuner_priv; + int ret = 0; + + switch (priv->id) { + case TDA18271HDC1: + priv->maps = &tda18271c1_map_layout; + memcpy(&priv->std, &tda18271c1_std_map, + sizeof(struct tda18271_std_map)); + break; + case TDA18271HDC2: + priv->maps = &tda18271c2_map_layout; + memcpy(&priv->std, &tda18271c2_std_map, + sizeof(struct tda18271_std_map)); + break; + default: + ret = -EINVAL; + break; + } + memcpy(priv->rf_cal_state, &tda18271_rf_band_template, + sizeof(tda18271_rf_band_template)); + + return ret; +} + /* * Overrides for Emacs so that we follow Linus's tabbing style. * --------------------------------------------------------------------------- diff --git a/linux/drivers/media/dvb/frontends/tda18271.h b/linux/drivers/media/dvb/frontends/tda18271.h index b98a9331c..51bb344c7 100644 --- a/linux/drivers/media/dvb/frontends/tda18271.h +++ b/linux/drivers/media/dvb/frontends/tda18271.h @@ -1,7 +1,7 @@ /* tda18271.h - header for the Philips / NXP TDA18271 silicon tuner - Copyright (C) 2007 Michael Krufky (mkrufky@linuxtv.org) + Copyright (C) 2007, 2008 Michael Krufky <mkrufky@linuxtv.org> 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 @@ -24,21 +24,53 @@ #include <linux/i2c.h> #include "dvb_frontend.h" +struct tda18271_std_map_item { + u16 if_freq; + u8 std_bits; +}; + +struct tda18271_std_map { +#if 0 + tda18271_std_map_item fm_radio; +#endif + struct tda18271_std_map_item atv_b; + struct tda18271_std_map_item atv_dk; + struct tda18271_std_map_item atv_gh; + struct tda18271_std_map_item atv_i; + struct tda18271_std_map_item atv_l; + struct tda18271_std_map_item atv_lc; + struct tda18271_std_map_item atv_mn; + struct tda18271_std_map_item atsc_6; + struct tda18271_std_map_item dvbt_6; + struct tda18271_std_map_item dvbt_7; + struct tda18271_std_map_item dvbt_8; + struct tda18271_std_map_item qam_6; + struct tda18271_std_map_item qam_8; +}; + enum tda18271_i2c_gate { TDA18271_GATE_AUTO = 0, TDA18271_GATE_ANALOG, TDA18271_GATE_DIGITAL, }; +struct tda18271_config { + /* override default if freq / std settings (optional) */ + struct tda18271_std_map *std_map; + + /* use i2c gate provided by analog or digital demod */ + enum tda18271_i2c_gate gate; +}; + #if defined(CONFIG_DVB_TDA18271) || (defined(CONFIG_DVB_TDA18271_MODULE) && defined(MODULE)) extern struct dvb_frontend *tda18271_attach(struct dvb_frontend *fe, u8 addr, struct i2c_adapter *i2c, - enum tda18271_i2c_gate gate); + struct tda18271_config *cfg); #else static inline struct dvb_frontend *tda18271_attach(struct dvb_frontend *fe, u8 addr, struct i2c_adapter *i2c, - enum tda18271_i2c_gate gate) + struct tda18271_config *cfg) { printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __FUNCTION__); return NULL; diff --git a/linux/drivers/media/video/cx23885/cx23885-dvb.c b/linux/drivers/media/video/cx23885/cx23885-dvb.c index 3ec5e425c..84598c514 100644 --- a/linux/drivers/media/video/cx23885/cx23885-dvb.c +++ b/linux/drivers/media/video/cx23885/cx23885-dvb.c @@ -184,6 +184,16 @@ static struct tda829x_config tda829x_no_probe = { .probe_tuner = TDA829X_DONT_PROBE, }; +static struct tda18271_std_map hauppauge_tda18271_std_map = { + .atsc_6 = { .if_freq = 5380, .std_bits = 0x1b }, + .qam_6 = { .if_freq = 4000, .std_bits = 0x18 }, +}; + +static struct tda18271_config hauppauge_tda18271_config = { + .std_map = &hauppauge_tda18271_std_map, + .gate = TDA18271_GATE_ANALOG, +}; + static int cx23885_hvr1500_xc3028_callback(void *ptr, int command, int arg) { struct cx23885_tsport *port = ptr; @@ -249,7 +259,7 @@ static int dvb_register(struct cx23885_tsport *port) &tda829x_no_probe); dvb_attach(tda18271_attach, port->dvb.frontend, 0x60, &dev->i2c_bus[1].i2c_adap, - TDA18271_GATE_ANALOG); + &hauppauge_tda18271_config); } break; case 0: diff --git a/linux/drivers/media/video/em28xx/Kconfig b/linux/drivers/media/video/em28xx/Kconfig index 813077b6e..abbd38c1e 100644 --- a/linux/drivers/media/video/em28xx/Kconfig +++ b/linux/drivers/media/video/em28xx/Kconfig @@ -11,3 +11,18 @@ config VIDEO_EM28XX To compile this driver as a module, choose M here: the module will be called em28xx + +config VIDEO_EM28XX_ALSA + depends on VIDEO_EM28XX + tristate "Empia EM28xx ALSA audio module" + ---help--- + This is an ALSA driver for some Empia 28xx based TV cards. + + This is not required for em2800/em2820/em2821 boards. However, + newer em28xx devices uses Vendor Class for audio, instead of + implementing the USB Audio Class. For those chips, this module + will enable digital audio. + + To compile this driver as a module, choose M here: the + module will be called em28xx-alsa + diff --git a/linux/drivers/media/video/em28xx/Makefile b/linux/drivers/media/video/em28xx/Makefile index 7e7a93d75..092455099 100644 --- a/linux/drivers/media/video/em28xx/Makefile +++ b/linux/drivers/media/video/em28xx/Makefile @@ -1,7 +1,10 @@ em28xx-objs := em28xx-video.o em28xx-i2c.o em28xx-cards.o em28xx-core.o \ em28xx-input.o +em28xx-alsa-objs := em28xx-audio.o + obj-$(CONFIG_VIDEO_EM28XX) += em28xx.o +obj-$(CONFIG_VIDEO_EM28XX_ALSA) += em28xx-alsa.o EXTRA_CFLAGS += -Idrivers/media/video EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core diff --git a/linux/drivers/media/video/em28xx/em28xx-audio.c b/linux/drivers/media/video/em28xx/em28xx-audio.c new file mode 100644 index 000000000..901eeeb5c --- /dev/null +++ b/linux/drivers/media/video/em28xx/em28xx-audio.c @@ -0,0 +1,559 @@ +/* + * Empiatech em28x1 audio extension + * + * Copyright (C) 2006 Markus Rechberger <mrechberger@gmail.com> + * + * Copyright (C) 2007 Mauro Carvalho Chehab <mchehab@infradead.org> + * - Port to work with the in-kernel driver + * - Several cleanups + * + * This driver is based on my previous au600 usb pstn audio driver + * and inherits all the copyrights + * + * 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, or + * (at your option) any later version. + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/kernel.h> +#include <linux/usb.h> +#include <linux/init.h> +#include <linux/sound.h> +#include <linux/spinlock.h> +#include <linux/soundcard.h> +#include <linux/slab.h> +#include <linux/vmalloc.h> +#include <linux/proc_fs.h> +#include <linux/module.h> +#include <sound/driver.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/info.h> +#include <sound/initval.h> +#include <sound/control.h> +#include <media/v4l2-common.h> +#include "em28xx.h" + +static int debug; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "activates debug info"); + +#define dprintk(fmt, arg...) do { \ + if (debug) \ + printk(KERN_INFO "em28xx-audio %s: " fmt, \ + __FUNCTION__, ##arg); \ + } while (0) + +static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; + +static int em28xx_isoc_audio_deinit(struct em28xx *dev) +{ + int i; + + dprintk("Stopping isoc\n"); + for (i = 0; i < EM28XX_AUDIO_BUFS; i++) { + usb_kill_urb(dev->adev->urb[i]); + usb_free_urb(dev->adev->urb[i]); + dev->adev->urb[i] = NULL; + } + + return 0; +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19) +static void em28xx_audio_isocirq(struct urb *urb, struct pt_regs *regs) +#else +static void em28xx_audio_isocirq(struct urb *urb) +#endif +{ + struct em28xx *dev = urb->context; + int i; + unsigned int oldptr; + unsigned long flags; + int period_elapsed = 0; + int status; + unsigned char *cp; + unsigned int stride; +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 16) + snd_pcm_substream_t *substream; + snd_pcm_runtime_t *runtime; +#else + struct snd_pcm_substream *substream; + struct snd_pcm_runtime *runtime; +#endif + if (dev->adev->capture_pcm_substream) { + substream = dev->adev->capture_pcm_substream; + runtime = substream->runtime; + stride = runtime->frame_bits >> 3; + + for (i = 0; i < urb->number_of_packets; i++) { + int length = + urb->iso_frame_desc[i].actual_length / stride; + cp = (unsigned char *)urb->transfer_buffer + + urb->iso_frame_desc[i].offset; + + if (!length) + continue; + + spin_lock_irqsave(&dev->adev->slock, flags); + + oldptr = dev->adev->hwptr_done_capture; + dev->adev->hwptr_done_capture += length; + if (dev->adev->hwptr_done_capture >= + runtime->buffer_size) + dev->adev->hwptr_done_capture -= + runtime->buffer_size; + + dev->adev->capture_transfer_done += length; + if (dev->adev->capture_transfer_done >= + runtime->period_size) { + dev->adev->capture_transfer_done -= + runtime->period_size; + period_elapsed = 1; + } + + spin_unlock_irqrestore(&dev->adev->slock, flags); + + if (oldptr + length >= runtime->buffer_size) { + unsigned int cnt = + runtime->buffer_size - oldptr - 1; + memcpy(runtime->dma_area + oldptr * stride, cp, + cnt * stride); + memcpy(runtime->dma_area, cp + cnt, + length * stride - cnt * stride); + } else { + memcpy(runtime->dma_area + oldptr * stride, cp, + length * stride); + } + } + if (period_elapsed) + snd_pcm_period_elapsed(substream); + } + urb->status = 0; + + if (dev->adev->shutdown) + return; + + status = usb_submit_urb(urb, GFP_ATOMIC); + if (status < 0) { + em28xx_errdev("resubmit of audio urb failed (error=%i)\n", + status); + } + return; +} + +static int em28xx_init_audio_isoc(struct em28xx *dev) +{ + int i, errCode; + const int sb_size = EM28XX_NUM_AUDIO_PACKETS * + EM28XX_AUDIO_MAX_PACKET_SIZE; + + dprintk("Starting isoc transfers\n"); + + for (i = 0; i < EM28XX_AUDIO_BUFS; i++) { + struct urb *urb; + int j, k; + + dev->adev->transfer_buffer[i] = kmalloc(sb_size, GFP_ATOMIC); + if (!dev->adev->transfer_buffer[i]) + return -ENOMEM; + + memset(dev->adev->transfer_buffer[i], 0x80, sb_size); + urb = usb_alloc_urb(EM28XX_NUM_AUDIO_PACKETS, GFP_ATOMIC); + if (!urb) + return -ENOMEM; + + urb->dev = dev->udev; + urb->context = dev; + urb->pipe = usb_rcvisocpipe(dev->udev, 0x83); + urb->transfer_flags = URB_ISO_ASAP; + urb->transfer_buffer = dev->adev->transfer_buffer[i]; + urb->interval = 1; + urb->complete = em28xx_audio_isocirq; + urb->number_of_packets = EM28XX_NUM_AUDIO_PACKETS; + urb->transfer_buffer_length = sb_size; + + for (j = k = 0; j < EM28XX_NUM_AUDIO_PACKETS; + j++, k += EM28XX_AUDIO_MAX_PACKET_SIZE) { + urb->iso_frame_desc[j].offset = k; + urb->iso_frame_desc[j].length = + EM28XX_AUDIO_MAX_PACKET_SIZE; + } + dev->adev->urb[i] = urb; + } + + for (i = 0; i < EM28XX_AUDIO_BUFS; i++) { + errCode = usb_submit_urb(dev->adev->urb[i], GFP_ATOMIC); + if (errCode) { + em28xx_isoc_audio_deinit(dev); + + return errCode; + } + } + + return 0; +} + +static int em28xx_cmd(struct em28xx *dev, int cmd, int arg) +{ + dprintk("%s transfer\n", (dev->adev->capture_stream == STREAM_ON)? + "stop" : "start"); + + switch (cmd) { + case EM28XX_CAPTURE_STREAM_EN: + if (dev->adev->capture_stream == STREAM_OFF && arg == 1) { + dev->adev->capture_stream = STREAM_ON; + em28xx_init_audio_isoc(dev); + } else if (dev->adev->capture_stream == STREAM_ON && arg == 0) { + dev->adev->capture_stream = STREAM_OFF; + em28xx_isoc_audio_deinit(dev); + } else { + printk(KERN_ERR "An underrun very likely occurred. " + "Ignoring it.\n"); + } + return 0; + default: + return -EINVAL; + } +} + +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 16) +static int snd_pcm_alloc_vmalloc_buffer(snd_pcm_substream_t *subs, + size_t size) +#else +static int snd_pcm_alloc_vmalloc_buffer(struct snd_pcm_substream *subs, + size_t size) +#endif +{ +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 16) + snd_pcm_runtime_t *runtime = subs->runtime; +#else + struct snd_pcm_runtime *runtime = subs->runtime; +#endif + + dprintk("Alocating vbuffer\n"); + if (runtime->dma_area) { + if (runtime->dma_bytes > size) + return 0; + + vfree(runtime->dma_area); + } + runtime->dma_area = vmalloc(size); + if (!runtime->dma_area) + return -ENOMEM; + + runtime->dma_bytes = size; + + return 0; +} + +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 16) +static snd_pcm_hardware_t snd_em28xx_hw_capture = { +#else +static struct snd_pcm_hardware snd_em28xx_hw_capture = { +#endif + .info = SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_MMAP_VALID, + + .formats = SNDRV_PCM_FMTBIT_S16_LE, + + .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_KNOT, + + .rate_min = 48000, + .rate_max = 48000, + .channels_min = 2, + .channels_max = 2, + .buffer_bytes_max = 62720 * 8, /* just about the value in usbaudio.c */ + .period_bytes_min = 64, /* 12544/2, */ + .period_bytes_max = 12544, + .periods_min = 2, + .periods_max = 98, /* 12544, */ +}; + +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 16) +static int snd_em28xx_capture_open(snd_pcm_substream_t *substream) +#else +static int snd_em28xx_capture_open(struct snd_pcm_substream *substream) +#endif +{ + struct em28xx *dev = snd_pcm_substream_chip(substream); +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 16) + snd_pcm_runtime_t *runtime = substream->runtime; +#else + struct snd_pcm_runtime *runtime = substream->runtime; +#endif + int ret = 0; + + dprintk("opening device and trying to acquire exclusive lock\n"); + + /* Sets volume, mute, etc */ + dev->mute = 0; + ret = em28xx_audio_analog_set(dev); + if (ret < 0) + goto err; + + runtime->hw = snd_em28xx_hw_capture; + if (dev->alt == 0 && dev->adev->users == 0) { + int errCode; + dev->alt = 7; + errCode = usb_set_interface(dev->udev, 0, 7); + dprintk("changing alternate number to 7\n"); + } + + dev->adev->users++; + + snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); + dev->adev->capture_pcm_substream = substream; + runtime->private_data = dev; + + return 0; +err: + printk(KERN_ERR "Error while configuring em28xx mixer\n"); + return ret; +} + +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 16) +static int snd_em28xx_pcm_close(snd_pcm_substream_t *substream) +#else +static int snd_em28xx_pcm_close(struct snd_pcm_substream *substream) +#endif +{ + struct em28xx *dev = snd_pcm_substream_chip(substream); + dev->adev->users--; + + dprintk("closing device\n"); + + dev->mute = 1; + em28xx_audio_analog_set(dev); + + if (dev->adev->users == 0 && dev->adev->shutdown == 1) { + dprintk("audio users: %d\n", dev->adev->users); + dprintk("disabling audio stream!\n"); + dev->adev->shutdown = 0; + dprintk("released lock\n"); + em28xx_cmd(dev, EM28XX_CAPTURE_STREAM_EN, 0); + } + return 0; +} + +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 16) +static int snd_em28xx_hw_capture_params(snd_pcm_substream_t *substream, + snd_pcm_hw_params_t *hw_params) +#else +static int snd_em28xx_hw_capture_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params) +#endif +{ + unsigned int channels, rate, format; + int ret; + + dprintk("Setting capture parameters\n"); + + ret = snd_pcm_alloc_vmalloc_buffer(substream, + params_buffer_bytes(hw_params)); + format = params_format(hw_params); + rate = params_rate(hw_params); + channels = params_channels(hw_params); + + /* TODO: set up em28xx audio chip to deliver the correct audio format, + current default is 48000hz multiplexed => 96000hz mono + which shouldn't matter since analogue TV only supports mono */ + return 0; +} + +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 16) +static int snd_em28xx_hw_capture_free(snd_pcm_substream_t *substream) +#else +static int snd_em28xx_hw_capture_free(struct snd_pcm_substream *substream) +#endif +{ + struct em28xx *dev = snd_pcm_substream_chip(substream); + + dprintk("Stop capture, if needed\n"); + + if (dev->adev->capture_stream == STREAM_ON) + em28xx_cmd(dev, EM28XX_CAPTURE_STREAM_EN, 0); + + return 0; +} + +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 16) +static int snd_em28xx_prepare(snd_pcm_substream_t *substream) +#else +static int snd_em28xx_prepare(struct snd_pcm_substream *substream) +#endif +{ + return 0; +} + +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 16) +static int snd_em28xx_capture_trigger(snd_pcm_substream_t *substream, int cmd) +#else +static int snd_em28xx_capture_trigger(struct snd_pcm_substream *substream, + int cmd) +#endif +{ + struct em28xx *dev = snd_pcm_substream_chip(substream); + + dprintk("Should %s capture\n", (cmd == SNDRV_PCM_TRIGGER_START)? + "start": "stop"); + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + em28xx_cmd(dev, EM28XX_CAPTURE_STREAM_EN, 1); + return 0; + case SNDRV_PCM_TRIGGER_STOP: + dev->adev->shutdown = 1; + return 0; + default: + return -EINVAL; + } +} + +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 16) +static snd_pcm_uframes_t snd_em28xx_capture_pointer(snd_pcm_substream_t + *substream) +#else +static snd_pcm_uframes_t snd_em28xx_capture_pointer(struct snd_pcm_substream + *substream) +#endif +{ + struct em28xx *dev; + + snd_pcm_uframes_t hwptr_done; + dev = snd_pcm_substream_chip(substream); + hwptr_done = dev->adev->hwptr_done_capture; + + return hwptr_done; +} + +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 16) +static struct page *snd_pcm_get_vmalloc_page(snd_pcm_substream_t *subs, + unsigned long offset) +#else +static struct page *snd_pcm_get_vmalloc_page(struct snd_pcm_substream *subs, + unsigned long offset) +#endif +{ + void *pageptr = subs->runtime->dma_area + offset; + + return vmalloc_to_page(pageptr); +} + +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 16) +static snd_pcm_ops_t snd_em28xx_pcm_capture = { +#else +static struct snd_pcm_ops snd_em28xx_pcm_capture = { +#endif + .open = snd_em28xx_capture_open, + .close = snd_em28xx_pcm_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_em28xx_hw_capture_params, + .hw_free = snd_em28xx_hw_capture_free, + .prepare = snd_em28xx_prepare, + .trigger = snd_em28xx_capture_trigger, + .pointer = snd_em28xx_capture_pointer, + .page = snd_pcm_get_vmalloc_page, +}; + +static int em28xx_audio_init(struct em28xx *dev) +{ + struct em28xx_audio *adev; +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 16) + snd_pcm_t *pcm; + snd_card_t *card; +#else + struct snd_pcm *pcm; + struct snd_card *card; +#endif + static int devnr; + int ret, err; + + printk(KERN_INFO "em28xx-audio.c: probing for em28x1 " + "non standard usbaudio\n"); + printk(KERN_INFO "em28xx-audio.c: Copyright (C) 2006 Markus " + "Rechberger\n"); + + adev = kzalloc(sizeof(*adev), GFP_KERNEL); + if (!adev) { + printk(KERN_ERR "em28xx-audio.c: out of memory\n"); + return -1; + } + card = snd_card_new(index[devnr], "Em28xx Audio", THIS_MODULE, 0); + if (card == NULL) { + kfree(adev); + return -ENOMEM; + } + + spin_lock_init(&adev->slock); + ret = snd_pcm_new(card, "Em28xx Audio", 0, 0, 1, &pcm); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_em28xx_pcm_capture); + pcm->info_flags = 0; + pcm->private_data = dev; + strcpy(pcm->name, "Empia 28xx Capture"); + strcpy(card->driver, "Empia Em28xx Audio"); + strcpy(card->shortname, "Em28xx Audio"); + strcpy(card->longname, "Empia Em28xx Audio"); + + err = snd_card_register(card); + if (err < 0) { + snd_card_free(card); + return -ENOMEM; + } + adev->sndcard = card; + adev->udev = dev->udev; + dev->adev = adev; + + return 0; +} + +static int em28xx_audio_fini(struct em28xx *dev) +{ + if (dev == NULL) + return 0; + + if (dev->adev) { + snd_card_free(dev->adev->sndcard); + kfree(dev->adev); + dev->adev = NULL; + } + + return 0; +} + +static struct em28xx_ops audio_ops = { + .id = EM28XX_AUDIO, + .name = "Em28xx Audio Extension", + .init = em28xx_audio_init, + .fini = em28xx_audio_fini, +}; + +static int __init em28xx_alsa_register(void) +{ + return em28xx_register_extension(&audio_ops); +} + +static void __exit em28xx_alsa_unregister(void) +{ + em28xx_unregister_extension(&audio_ops); +} + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Markus Rechberger <mrechberger@gmail.com>"); +MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@infradead.org>"); +MODULE_DESCRIPTION("Em28xx Audio driver"); + +module_init(em28xx_alsa_register); +module_exit(em28xx_alsa_unregister); diff --git a/linux/drivers/media/video/em28xx/em28xx-cards.c b/linux/drivers/media/video/em28xx/em28xx-cards.c index 809cc0a4a..25344980a 100644 --- a/linux/drivers/media/video/em28xx/em28xx-cards.c +++ b/linux/drivers/media/video/em28xx/em28xx-cards.c @@ -175,12 +175,14 @@ struct em28xx_board em28xx_boards[] = { } }, }, [EM2880_BOARD_HAUPPAUGE_WINTV_HVR_950] = { - .name = "Hauppauge WinTV HVR 950", - .vchannels = 3, - .tda9887_conf = TDA9887_PRESENT, - .tuner_type = TUNER_XC2028, - .has_tuner = 1, - .decoder = EM28XX_TVP5150, + .name = "Hauppauge WinTV HVR 950", + .vchannels = 3, + .tda9887_conf = TDA9887_PRESENT, + .tuner_type = TUNER_XC2028, + .has_tuner = 1, + .mts_firmware = 1, + .has_12mhz_i2s = 1, + .decoder = EM28XX_TVP5150, .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = TVP5150_COMPOSITE0, @@ -194,6 +196,9 @@ struct em28xx_board em28xx_boards[] = { .vmux = TVP5150_SVIDEO, .amux = 1, } }, + + /* gpio's 4, 1, 0 */ + .analog_gpio = 0x003d2d, }, [EM2880_BOARD_TERRATEC_HYBRID_XS] = { .name = "Terratec Hybrid XS", @@ -454,8 +459,18 @@ void em28xx_pre_card_setup(struct em28xx *dev) case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900: case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_950: case EM2880_BOARD_TERRATEC_HYBRID_XS: - /* reset through GPIO? */ - em28xx_write_regs_req(dev, 0x00, 0x08, "\x7d", 1); + em28xx_write_regs(dev, XCLK_REG, "\x27", 1); + em28xx_write_regs(dev, I2C_CLK_REG, "\x40", 1); + em28xx_write_regs(dev, 0x08, "\xff", 1); + em28xx_write_regs(dev, 0x04, "\x00", 1); + msleep(100); + em28xx_write_regs(dev, 0x04, "\x08", 1); + msleep(100); + em28xx_write_regs(dev, 0x08, "\xff", 1); + msleep(50); + em28xx_write_regs(dev, 0x08, "\x2d", 1); + msleep(50); + em28xx_write_regs(dev, 0x08, "\x3d", 1); break; } } @@ -470,13 +485,31 @@ static int em28xx_tuner_callback(void *ptr, int command, int arg) switch (command) { case XC2028_TUNER_RESET: - /* FIXME: This is device-dependent */ + { + char gpio0, gpio1, gpio4; + + /* GPIO and initialization codes for analog TV */ + gpio0 = dev->analog_gpio & 0xff; + gpio1 = (dev->analog_gpio >> 8) & 0xff; + gpio4 = dev->analog_gpio >> 24; + dev->em28xx_write_regs_req(dev, 0x00, 0x48, "\x00", 1); dev->em28xx_write_regs_req(dev, 0x00, 0x12, "\x67", 1); - msleep(140); + if (gpio4) { + dev->em28xx_write_regs(dev, 0x04, &gpio4, 1); + msleep(140); + } + + msleep(6); + dev->em28xx_write_regs(dev, 0x08, &gpio0, 1); + msleep(10); + dev->em28xx_write_regs(dev, 0x08, &gpio1, 1); + msleep(5); + break; } + } return rc; } @@ -609,6 +642,8 @@ static void em28xx_set_model(struct em28xx *dev) dev->tda9887_conf = em28xx_boards[dev->model].tda9887_conf; dev->decoder = em28xx_boards[dev->model].decoder; dev->video_inputs = em28xx_boards[dev->model].vchannels; + dev->analog_gpio = em28xx_boards[dev->model].analog_gpio; + dev->has_12mhz_i2s = em28xx_boards[dev->model].has_12mhz_i2s; if (!em28xx_boards[dev->model].has_tuner) dev->tuner_type = UNSET; @@ -644,8 +679,6 @@ void em28xx_card_setup(struct em28xx *dev) if (tv.has_ir) request_module("ir-kbd-i2c"); #endif - /* FIXME: Should also retrieve decoder processor type */ - break; } case EM2820_BOARD_KWORLD_PVRTV2800RF: diff --git a/linux/drivers/media/video/em28xx/em28xx-core.c b/linux/drivers/media/video/em28xx/em28xx-core.c index f229a83da..cb87e7e41 100644 --- a/linux/drivers/media/video/em28xx/em28xx-core.c +++ b/linux/drivers/media/video/em28xx/em28xx-core.c @@ -303,7 +303,7 @@ int em28xx_write_reg_bits(struct em28xx *dev, u16 reg, u8 val, * em28xx_write_ac97() * write a 16 bit value to the specified AC97 address (LSB first!) */ -int em28xx_write_ac97(struct em28xx *dev, u8 reg, u8 * val) +static int em28xx_write_ac97(struct em28xx *dev, u8 reg, u8 *val) { int ret; u8 addr = reg & 0x7f; @@ -319,24 +319,98 @@ int em28xx_write_ac97(struct em28xx *dev, u8 reg, u8 * val) return 0; } +int em28xx_set_audio_source(struct em28xx *dev) +{ + static char *enable = "\x08\x08"; + static char *disable = "\x08\x88"; + char *video = enable, *line = disable; + int ret, no_ac97; + u8 input; + + if (dev->is_em2800) { + if (dev->ctl_ainput) + input = EM2800_AUDIO_SRC_LINE; + else + input = EM2800_AUDIO_SRC_TUNER; + + ret = em28xx_write_regs(dev, EM2800_AUDIOSRC_REG, &input, 1); + if (ret < 0) + return ret; + } + + if (dev->has_msp34xx) + input = EM28XX_AUDIO_SRC_TUNER; + else { + switch (dev->ctl_ainput) { + case EM28XX_AMUX_VIDEO: + input = EM28XX_AUDIO_SRC_TUNER; + no_ac97 = 1; + break; + case EM28XX_AMUX_LINE_IN: + input = EM28XX_AUDIO_SRC_LINE; + no_ac97 = 1; + break; + case EM28XX_AMUX_AC97_VIDEO: + input = EM28XX_AUDIO_SRC_LINE; + break; + case EM28XX_AMUX_AC97_LINE_IN: + input = EM28XX_AUDIO_SRC_LINE; + video = disable; + line = enable; + break; + } + } + + ret = em28xx_write_reg_bits(dev, AUDIOSRC_REG, input, 0xc0); + if (ret < 0) + return ret; + + if (no_ac97) + return 0; + + /* Sets AC97 mixer registers */ + + ret = em28xx_write_ac97(dev, VIDEO_AC97, video); + if (ret < 0) + return ret; + + ret = em28xx_write_ac97(dev, LINE_IN_AC97, line); + + return ret; +} + int em28xx_audio_analog_set(struct em28xx *dev) { + int ret; char s[2] = { 0x00, 0x00 }; + u8 xclk = 0x07; + s[0] |= 0x1f - dev->volume; s[1] |= 0x1f - dev->volume; + if (dev->mute) s[1] |= 0x80; - return em28xx_write_ac97(dev, MASTER_AC97, s); -} + ret = em28xx_write_ac97(dev, MASTER_AC97, s); + if (ret < 0) + return ret; -#if 0 -int em28xx_audio_analog_mute(struct em28xx *dev, int mute) -{ - /* (un)mute master mixer with maximum volume level */ - return em28xx_write_ac97(dev, MASTER_AC97, - mute ? "\x00\x80" : "\x00\x00"); + if (dev->has_12mhz_i2s) + xclk |= 0x20; + + if (!dev->mute) + xclk |= 0x80; + + ret = em28xx_write_reg_bits(dev, XCLK_REG, xclk, 0xa7); + if (ret < 0) + return ret; + msleep(10); + + /* Selects the proper audio input */ + ret = em28xx_set_audio_source(dev); + + return ret; } -#endif +EXPORT_SYMBOL_GPL(em28xx_audio_analog_set); int em28xx_colorlevels_set_default(struct em28xx *dev) { diff --git a/linux/drivers/media/video/em28xx/em28xx-video.c b/linux/drivers/media/video/em28xx/em28xx-video.c index 44621bafc..77d547f81 100644 --- a/linux/drivers/media/video/em28xx/em28xx-video.c +++ b/linux/drivers/media/video/em28xx/em28xx-video.c @@ -66,26 +66,32 @@ static LIST_HEAD(em28xx_devlist); static unsigned int card[] = {[0 ... (EM28XX_MAXBOARDS - 1)] = UNSET }; static unsigned int video_nr[] = {[0 ... (EM28XX_MAXBOARDS - 1)] = UNSET }; -static unsigned int vbi_nr[] = {[0 ... (EM28XX_MAXBOARDS - 1)] = UNSET }; +static unsigned int vbi_nr[] = {[0 ... (EM28XX_MAXBOARDS - 1)] = UNSET }; +static unsigned int radio_nr[] = {[0 ... (EM28XX_MAXBOARDS - 1)] = UNSET }; + #if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0) -MODULE_PARM(card,"1-" __stringify(EM28XX_MAXBOARDS) "i"); -MODULE_PARM(video_nr,"1-" __stringify(EM28XX_MAXBOARDS) "i"); -MODULE_PARM(vbi_nr,"1-" __stringify(EM28XX_MAXBOARDS) "i"); +MODULE_PARM(card, "1-" __stringify(EM28XX_MAXBOARDS) "i"); +MODULE_PARM(video_nr, "1-" __stringify(EM28XX_MAXBOARDS) "i"); +MODULE_PARM(vbi_nr, "1-" __stringify(EM28XX_MAXBOARDS) "i"); +MODULE_PARM(radio_nr, "1-" __stringify(EM28XX_MAXBOARDS) "i"); #else #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,10) static int dummy; module_param_array(card, int, dummy, 0444); module_param_array(video_nr, int, dummy, 0444); module_param_array(vbi_nr, int, dummy, 0444); +module_param_array(radio_nr, int, dummy, 0444); #else module_param_array(card, int, NULL, 0444); module_param_array(video_nr, int, NULL, 0444); module_param_array(vbi_nr, int, NULL, 0444); +module_param_array(radio_nr, int, NULL, 0444); #endif #endif -MODULE_PARM_DESC(card,"card type"); -MODULE_PARM_DESC(video_nr,"video device numbers"); -MODULE_PARM_DESC(vbi_nr,"vbi device numbers"); +MODULE_PARM_DESC(card, "card type"); +MODULE_PARM_DESC(video_nr, "video device numbers"); +MODULE_PARM_DESC(vbi_nr, "vbi device numbers"); +MODULE_PARM_DESC(radio_nr, "radio device numbers"); static unsigned int video_debug = 0; module_param(video_debug,int,0644); @@ -142,11 +148,9 @@ static int em28xx_config(struct em28xx *dev) em28xx_write_regs_req(dev,0x00,0x11,"\x51",1); #endif - em28xx_audio_usb_mute(dev, 1); dev->mute = 1; /* maybe not the right place... */ dev->volume = 0x1f; - em28xx_audio_analog_set(dev); - em28xx_audio_analog_setup(dev); + em28xx_outfmt_set_yuv422(dev); em28xx_colorlevels_set_default(dev); em28xx_compression_disable(dev); @@ -188,7 +192,6 @@ static void em28xx_empty_framequeues(struct em28xx *dev) static void video_mux(struct em28xx *dev, int index) { - int ainput; struct v4l2_routing route; route.input = INPUT(index)->vmux; @@ -205,18 +208,9 @@ static void video_mux(struct em28xx *dev, int index) route.output = MSP_OUTPUT(MSP_SC_IN_DSP_SCART1); /* Note: this is msp3400 specific */ em28xx_i2c_call_clients(dev, VIDIOC_INT_S_AUDIO_ROUTING, &route); - ainput = EM28XX_AUDIO_SRC_TUNER; - em28xx_audio_source(dev, ainput); - } else { - switch (dev->ctl_ainput) { - case 0: - ainput = EM28XX_AUDIO_SRC_TUNER; - break; - default: - ainput = EM28XX_AUDIO_SRC_LINE; - } - em28xx_audio_source(dev, ainput); } + + em28xx_set_audio_source(dev); } /* Usage lock check functions */ @@ -312,7 +306,6 @@ static int em28xx_set_ctrl(struct em28xx *dev, const struct v4l2_control *ctrl) case V4L2_CID_AUDIO_MUTE: if (ctrl->value != dev->mute) { dev->mute = ctrl->value; - em28xx_audio_usb_mute(dev, ctrl->value); return em28xx_audio_analog_set(dev); } return 0; @@ -824,7 +817,7 @@ static int vidioc_g_frequency(struct file *file, void *priv, struct em28xx_fh *fh = priv; struct em28xx *dev = fh->dev; - f->type = V4L2_TUNER_ANALOG_TV; + f->type = fh->radio ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV; f->frequency = dev->ctl_freq; return 0; @@ -844,7 +837,9 @@ static int vidioc_s_frequency(struct file *file, void *priv, if (0 != f->tuner) return -EINVAL; - if (V4L2_TUNER_ANALOG_TV != f->type) + if (unlikely(0 == fh->radio && f->type != V4L2_TUNER_ANALOG_TV)) + return -EINVAL; + if (unlikely(1 == fh->radio && f->type != V4L2_TUNER_RADIO)) return -EINVAL; mutex_lock(&dev->lock); @@ -1218,6 +1213,102 @@ static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b) return 0; } +/* ----------------------------------------------------------- */ +/* RADIO ESPECIFIC IOCTLS */ +/* ----------------------------------------------------------- */ + +static int radio_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + struct em28xx *dev = ((struct em28xx_fh *)priv)->dev; + + strlcpy(cap->driver, "em28xx", sizeof(cap->driver)); + strlcpy(cap->card, em28xx_boards[dev->model].name, sizeof(cap->card)); + strlcpy(cap->bus_info, dev->udev->dev.bus_id, sizeof(cap->bus_info)); + + cap->version = EM28XX_VERSION_CODE; + cap->capabilities = V4L2_CAP_TUNER; + return 0; +} + +static int radio_g_tuner(struct file *file, void *priv, + struct v4l2_tuner *t) +{ + struct em28xx *dev = ((struct em28xx_fh *)priv)->dev; + + if (unlikely(t->index > 0)) + return -EINVAL; + + strcpy(t->name, "Radio"); + t->type = V4L2_TUNER_RADIO; + + em28xx_i2c_call_clients(dev, VIDIOC_G_TUNER, t); + return 0; +} + +static int radio_enum_input(struct file *file, void *priv, + struct v4l2_input *i) +{ + if (i->index != 0) + return -EINVAL; + strcpy(i->name, "Radio"); + i->type = V4L2_INPUT_TYPE_TUNER; + + return 0; +} + +static int radio_g_audio(struct file *file, void *priv, struct v4l2_audio *a) +{ + if (unlikely(a->index)) + return -EINVAL; + + strcpy(a->name, "Radio"); + return 0; +} + +static int radio_s_tuner(struct file *file, void *priv, + struct v4l2_tuner *t) +{ + struct em28xx *dev = ((struct em28xx_fh *)priv)->dev; + + if (0 != t->index) + return -EINVAL; + + em28xx_i2c_call_clients(dev, VIDIOC_S_TUNER, t); + + return 0; +} + +static int radio_s_audio(struct file *file, void *fh, + struct v4l2_audio *a) +{ + return 0; +} + +static int radio_s_input(struct file *file, void *fh, unsigned int i) +{ + return 0; +} + +static int radio_queryctrl(struct file *file, void *priv, + struct v4l2_queryctrl *qc) +{ + int i; + + if (qc->id < V4L2_CID_BASE || + qc->id >= V4L2_CID_LASTP1) + return -EINVAL; + + for (i = 0; i < ARRAY_SIZE(em28xx_qctrl); i++) { + if (qc->id && qc->id == em28xx_qctrl[i].id) { + memcpy(qc, &(em28xx_qctrl[i]), sizeof(*qc)); + return 0; + } + } + + return -EINVAL; +} + /* * em28xx_v4l2_open() * inits the device and starts isoc transfer @@ -1225,7 +1316,7 @@ static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b) static int em28xx_v4l2_open(struct inode *inode, struct file *filp) { int minor = iminor(inode); - int errCode = 0; + int errCode = 0, radio = 0; struct em28xx *h,*dev = NULL; struct em28xx_fh *fh; @@ -1238,6 +1329,11 @@ static int em28xx_v4l2_open(struct inode *inode, struct file *filp) dev = h; dev->type = V4L2_BUF_TYPE_VBI_CAPTURE; } + if (h->radio_dev && + h->radio_dev->minor == minor) { + radio = 1; + dev = h; + } } if (NULL == dev) return -ENODEV; @@ -1253,6 +1349,7 @@ static int em28xx_v4l2_open(struct inode *inode, struct file *filp) } mutex_lock(&dev->lock); fh->dev = dev; + fh->radio = radio; filp->private_data = fh; if (dev->type == V4L2_BUF_TYPE_VIDEO_CAPTURE && dev->users == 0) { @@ -1281,6 +1378,13 @@ static int em28xx_v4l2_open(struct inode *inode, struct file *filp) em28xx_empty_framequeues(dev); } + if (fh->radio) { + em28xx_videodbg("video_open: setting radio device\n"); +#if 0 + em28xx_start_radio(dev); +#endif + em28xx_i2c_call_clients(dev, AUDC_SET_RADIO, NULL); + } dev->users++; @@ -1303,16 +1407,30 @@ static void em28xx_release_resources(struct em28xx *dev) dev->vdev->minor-MINOR_VFL_TYPE_GRABBER_MIN, dev->vbi_dev->minor-MINOR_VFL_TYPE_VBI_MIN); list_del(&dev->devlist); - video_unregister_device(dev->vdev); - video_unregister_device(dev->vbi_dev); + if (dev->radio_dev) { + if (-1 != dev->radio_dev->minor) + video_unregister_device(dev->radio_dev); + else + video_device_release(dev->radio_dev); + dev->radio_dev = NULL; + } + if (dev->vbi_dev) { + if (-1 != dev->vbi_dev->minor) + video_unregister_device(dev->vbi_dev); + else + video_device_release(dev->vbi_dev); + dev->vbi_dev = NULL; + } + if (dev->vdev) { + if (-1 != dev->vdev->minor) + video_unregister_device(dev->vdev); + else + video_device_release(dev->vdev); + dev->vdev = NULL; + } em28xx_i2c_unregister(dev); usb_put_dev(dev->udev); -#if 0 /* Fixme: disallocating these generates kernel hang */ - kfree (dev->vdev); - kfree (dev->vbi_dev); -#endif - /* Mark device as unused */ em28xx_devused&=~(1<<dev->devno); } @@ -1635,6 +1753,17 @@ static const struct file_operations em28xx_v4l_fops = { #endif }; +static const struct file_operations radio_fops = { + .owner = THIS_MODULE, + .open = em28xx_v4l2_open, + .release = em28xx_v4l2_close, + .ioctl = video_ioctl2, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 11) + .compat_ioctl = v4l_compat_ioctl32, +#endif + .llseek = no_llseek, +}; + static const struct video_device em28xx_video_template = { .fops = &em28xx_v4l_fops, .release = video_device_release, @@ -1680,9 +1809,92 @@ static const struct video_device em28xx_video_template = { .current_norm = V4L2_STD_PAL, }; +static struct video_device em28xx_radio_template = { + .name = "em28xx-radio", + .type = VID_TYPE_TUNER, + .fops = &radio_fops, + .minor = -1, + .vidioc_querycap = radio_querycap, + .vidioc_g_tuner = radio_g_tuner, + .vidioc_enum_input = radio_enum_input, + .vidioc_g_audio = radio_g_audio, + .vidioc_s_tuner = radio_s_tuner, + .vidioc_s_audio = radio_s_audio, + .vidioc_s_input = radio_s_input, + .vidioc_queryctrl = radio_queryctrl, + .vidioc_g_ctrl = vidioc_g_ctrl, + .vidioc_s_ctrl = vidioc_s_ctrl, + .vidioc_g_frequency = vidioc_g_frequency, + .vidioc_s_frequency = vidioc_s_frequency, +}; /******************************** usb interface *****************************************/ + +static LIST_HEAD(em28xx_extension_devlist); +static DEFINE_MUTEX(em28xx_extension_devlist_lock); + +int em28xx_register_extension(struct em28xx_ops *ops) +{ + struct em28xx *h, *dev = NULL; + + list_for_each_entry(h, &em28xx_devlist, devlist) + dev = h; + + mutex_lock(&em28xx_extension_devlist_lock); + list_add_tail(&ops->next, &em28xx_extension_devlist); + if (dev) + ops->init(dev); + + printk(KERN_INFO "Em28xx: Initialized (%s) extension\n", ops->name); + mutex_unlock(&em28xx_extension_devlist_lock); + + return 0; +} +EXPORT_SYMBOL(em28xx_register_extension); + +void em28xx_unregister_extension(struct em28xx_ops *ops) +{ + struct em28xx *h, *dev = NULL; + + list_for_each_entry(h, &em28xx_devlist, devlist) + dev = h; + + if (dev) + ops->fini(dev); + + mutex_lock(&em28xx_extension_devlist_lock); + printk(KERN_INFO "Em28xx: Removed (%s) extension\n", ops->name); + list_del(&ops->next); + mutex_unlock(&em28xx_extension_devlist_lock); +} +EXPORT_SYMBOL(em28xx_unregister_extension); + +struct video_device *em28xx_vdev_init(struct em28xx *dev, + const struct video_device *template, + const int type, + const char *type_name) +{ + struct video_device *vfd; + + vfd = video_device_alloc(); + if (NULL == vfd) + return NULL; + *vfd = *template; + vfd->minor = -1; +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0) + vfd->dev = &dev->udev->dev; + vfd->release = video_device_release; +#endif + vfd->type = type; + + snprintf(vfd->name, sizeof(vfd->name), "%s %s", + dev->name, type_name); + + return vfd; +} + + /* * em28xx_init_dev() * allocates and inits the device structs, registers i2c bus and v4l device @@ -1690,6 +1902,7 @@ static const struct video_device em28xx_video_template = { static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev, int minor) { + struct em28xx_ops *ops = NULL; struct em28xx *dev = *devhandle; int retval = -ENOMEM; int errCode; @@ -1709,6 +1922,10 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev, dev->em28xx_read_reg_req = em28xx_read_reg_req; dev->is_em2800 = em28xx_boards[dev->model].is_em2800; + errCode = em28xx_read_reg(dev, CHIPID_REG); + if (errCode >= 0) + em28xx_info("em28xx chip ID = %d\n", errCode); + em28xx_pre_card_setup(dev); errCode = em28xx_config(dev); @@ -1725,6 +1942,9 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev, /* Do board specific init and eeprom reading */ em28xx_card_setup(dev); + /* Configure audio */ + em28xx_audio_analog_set(dev); + /* configure the device */ em28xx_config_i2c(dev); @@ -1748,40 +1968,55 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev, errCode = em28xx_config(dev); + list_add_tail(&dev->devlist, &em28xx_devlist); + /* allocate and fill video video_device struct */ - dev->vdev = video_device_alloc(); + dev->vdev = em28xx_vdev_init(dev, &em28xx_video_template, + VID_TYPE_CAPTURE, "video"); if (NULL == dev->vdev) { em28xx_errdev("cannot allocate video_device.\n"); - em28xx_devused&=~(1<<dev->devno); - kfree(dev); - return -ENOMEM; + goto fail_unreg; } - memcpy(dev->vdev, &em28xx_video_template, - sizeof(em28xx_video_template)); - dev->vdev->type = VID_TYPE_CAPTURE; if (dev->has_tuner) dev->vdev->type |= VID_TYPE_TUNER; - dev->vdev->dev = &dev->udev->dev; - snprintf(dev->vdev->name, sizeof(dev->vbi_dev->name), - "%s#%d %s", "em28xx", dev->devno, "video"); + + /* register v4l2 video video_device */ + retval = video_register_device(dev->vdev, VFL_TYPE_GRABBER, + video_nr[dev->devno]); + if (retval) { + em28xx_errdev("unable to register video device (error=%i).\n", + retval); + goto fail_unreg; + } /* Allocate and fill vbi video_device struct */ - dev->vbi_dev = video_device_alloc(); - if (NULL == dev->vbi_dev) { - em28xx_errdev("cannot allocate video_device.\n"); - kfree(dev->vdev); - em28xx_devused&=~(1<<dev->devno); - kfree(dev); - return -ENOMEM; + dev->vbi_dev = em28xx_vdev_init(dev, &em28xx_video_template, + VFL_TYPE_VBI, "vbi"); + /* register v4l2 vbi video_device */ + if (video_register_device(dev->vbi_dev, VFL_TYPE_VBI, + vbi_nr[dev->devno]) < 0) { + em28xx_errdev("unable to register vbi device\n"); + retval = -ENODEV; + goto fail_unreg; + } + + if (em28xx_boards[dev->model].radio.type == EM28XX_RADIO) { + dev->radio_dev = em28xx_vdev_init(dev, &em28xx_radio_template, + VFL_TYPE_RADIO, "radio"); + if (NULL == dev->radio_dev) { + em28xx_errdev("cannot allocate video_device.\n"); + goto fail_unreg; + } + retval = video_register_device(dev->radio_dev, VFL_TYPE_RADIO, + radio_nr[dev->devno]); + if (retval < 0) { + em28xx_errdev("can't register radio device\n"); + goto fail_unreg; + } + em28xx_info("Registered radio device as /dev/radio%d\n", + dev->radio_dev->minor & 0x1f); } - memcpy(dev->vbi_dev, &em28xx_video_template, - sizeof(em28xx_video_template)); - dev->vbi_dev->type = VFL_TYPE_VBI; - dev->vbi_dev->dev = &dev->udev->dev; - snprintf(dev->vbi_dev->name, sizeof(dev->vbi_dev->name), - "%s#%d %s", "em28xx", dev->devno, "vbi"); - list_add_tail(&dev->devlist,&em28xx_devlist); #if 0 video_set_drvdata(dev->vbi_dev, dev); #endif @@ -1796,39 +2031,62 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev, video_mux(dev, 0); - /* register v4l2 video video_device */ - if ((retval = video_register_device(dev->vdev, VFL_TYPE_GRABBER, - video_nr[dev->devno]))) { - em28xx_errdev("unable to register video device (error=%i).\n", - retval); - mutex_unlock(&dev->lock); - list_del(&dev->devlist); - video_device_release(dev->vdev); - em28xx_devused&=~(1<<dev->devno); - kfree(dev); - return -ENODEV; - } - - /* register v4l2 vbi video_device */ - if (video_register_device(dev->vbi_dev, VFL_TYPE_VBI, - vbi_nr[dev->devno]) < 0) { - printk("unable to register vbi device\n"); - mutex_unlock(&dev->lock); - list_del(&dev->devlist); - video_device_release(dev->vbi_dev); - video_device_release(dev->vdev); - em28xx_devused&=~(1<<dev->devno); - kfree(dev); - return -ENODEV; - } - em28xx_info("V4L2 device registered as /dev/video%d and /dev/vbi%d\n", dev->vdev->minor-MINOR_VFL_TYPE_GRABBER_MIN, dev->vbi_dev->minor-MINOR_VFL_TYPE_VBI_MIN); + mutex_lock(&em28xx_extension_devlist_lock); + if (!list_empty(&em28xx_extension_devlist)) { + list_for_each_entry(ops, &em28xx_extension_devlist, next) { + if (ops->id) + ops->init(dev); + } + } + mutex_unlock(&em28xx_extension_devlist_lock); + return 0; + +fail_unreg: + em28xx_release_resources(dev); + mutex_unlock(&dev->lock); + kfree(dev); + return retval; } +#if defined(CONFIG_MODULES) && defined(MODULE) +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20) +static void request_module_async(void *ptr) +{ + struct em28xx *dev = (struct em28xx *)ptr; +#else +static void request_module_async(struct work_struct *work) +{ + struct em28xx *dev = container_of(work, + struct em28xx, request_module_wk); +#endif + + if (!dev->has_audio_class) + request_module("em28xx-alsa"); +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 0) +#define request_modules(dev) +#else +static void request_modules(struct em28xx *dev) +{ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20) + INIT_WORK(&dev->request_module_wk, request_module_async, + (void *)dev); +#else + INIT_WORK(&dev->request_module_wk, request_module_async); +#endif + schedule_work(&dev->request_module_wk); +} +#endif +#else +#define request_modules(dev) +#endif /* CONFIG_MODULES */ + /* * em28xx_usb_probe() * checks for supported devices @@ -1899,6 +2157,18 @@ static int em28xx_usb_probe(struct usb_interface *interface, dev->devno = nr; dev->model = id->driver_info; + /* Checks if audio is provided by some interface */ + for (i = 0; i < udev->config->desc.bNumInterfaces; i++) { + uif = udev->config->interface[i]; + if (uif->altsetting[0].desc.bInterfaceClass == USB_CLASS_AUDIO) { + dev->has_audio_class = 1; + break; + } + } + + printk(KERN_INFO DRIVER_NAME " %s usb audio class\n", + dev->has_audio_class ? "Has" : "Doesn't have"); + /* compute alternate max packet sizes */ uif = udev->actconfig->interface[0]; @@ -1935,6 +2205,9 @@ static int em28xx_usb_probe(struct usb_interface *interface, /* save our data pointer in this interface device */ usb_set_intfdata(interface, dev); + + request_modules(dev); + return 0; } @@ -1946,6 +2219,7 @@ static int em28xx_usb_probe(struct usb_interface *interface, static void em28xx_usb_disconnect(struct usb_interface *interface) { struct em28xx *dev; + struct em28xx_ops *ops = NULL; dev = usb_get_intfdata(interface); usb_set_intfdata(interface, NULL); @@ -1975,15 +2249,20 @@ static void em28xx_usb_disconnect(struct usb_interface *interface) dev->state |= DEV_DISCONNECTED; em28xx_release_resources(dev); } - - mutex_unlock(&dev->lock); + mutex_lock(&em28xx_extension_devlist_lock); + if (!list_empty(&em28xx_extension_devlist)) { + list_for_each_entry(ops, &em28xx_extension_devlist, next) { + ops->fini(dev); + } + } + mutex_unlock(&em28xx_extension_devlist_lock); + if (!dev->users) { kfree(dev->alt_max_pkt_size); kfree(dev); } - } static struct usb_driver em28xx_usb_driver = { diff --git a/linux/drivers/media/video/em28xx/em28xx.h b/linux/drivers/media/video/em28xx/em28xx.h index 5e5f4e7a9..a483a3668 100644 --- a/linux/drivers/media/video/em28xx/em28xx.h +++ b/linux/drivers/media/video/em28xx/em28xx.h @@ -154,10 +154,17 @@ enum enum28xx_itype { EM28XX_RADIO, }; +enum em28xx_amux { + EM28XX_AMUX_VIDEO, + EM28XX_AMUX_LINE_IN, + EM28XX_AMUX_AC97_VIDEO, + EM28XX_AMUX_AC97_LINE_IN, +}; + struct em28xx_input { enum enum28xx_itype type; unsigned int vmux; - unsigned int amux; + enum em28xx_amux amux; }; #define INPUT(nr) (&em28xx_boards[dev->model].input[nr]) @@ -174,16 +181,20 @@ struct em28xx_board { int tuner_type; /* i2c flags */ - unsigned int is_em2800; unsigned int tda9887_conf; + unsigned int is_em2800:1; unsigned int has_tuner:1; unsigned int has_msp34xx:1; unsigned int mts_firmware:1; + unsigned int has_12mhz_i2s:1; + + unsigned int analog_gpio; enum em28xx_decoder decoder; struct em28xx_input input[MAX_EM28XX_INPUT]; + struct em28xx_input radio; }; struct em28xx_eeprom { @@ -207,20 +218,53 @@ enum em28xx_dev_state { DEV_MISCONFIGURED = 0x04, }; +#define EM28XX_AUDIO_BUFS 5 +#define EM28XX_NUM_AUDIO_PACKETS 64 +#define EM28XX_AUDIO_MAX_PACKET_SIZE 196 /* static value */ +#define EM28XX_CAPTURE_STREAM_EN 1 +#define EM28XX_AUDIO 0x10 + +struct em28xx_audio { + char name[50]; + char *transfer_buffer[EM28XX_AUDIO_BUFS]; + struct urb *urb[EM28XX_AUDIO_BUFS]; + struct usb_device *udev; + unsigned int capture_transfer_done; +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 16) + snd_pcm_substream_t *capture_pcm_substream; +#else + struct snd_pcm_substream *capture_pcm_substream; +#endif + + unsigned int hwptr_done_capture; +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 16) + snd_card_t *sndcard; +#else + struct snd_card *sndcard; +#endif + + int users, shutdown; + enum em28xx_stream_state capture_stream; + spinlock_t slock; +}; + /* main device struct */ struct em28xx { /* generic device properties */ char name[30]; /* name (including minor) of the device */ int model; /* index in the device_data struct */ int devno; /* marks the number of this device */ - unsigned int is_em2800; - int video_inputs; /* number of video inputs */ - struct list_head devlist; + unsigned int analog_gpio; + unsigned int is_em2800:1; unsigned int has_tuner:1; unsigned int has_msp34xx:1; unsigned int has_tda9887:1; - unsigned int stream_on:1; /* Locks streams */ + unsigned int has_audio_class:1; + unsigned int has_12mhz_i2s:1; + + int video_inputs; /* number of video inputs */ + struct list_head devlist; u32 i2s_speed; /* I2S speed for audio digital stream */ @@ -260,11 +304,15 @@ struct em28xx { unsigned long hash; /* eeprom hash - for boards with generic ID */ unsigned long i2c_hash; /* i2c devicelist hash - for boards with generic ID */ + struct em28xx_audio *adev; + /* states */ enum em28xx_dev_state state; enum em28xx_stream_state stream; enum em28xx_io_method io; + struct work_struct request_module_wk; + /* locks */ #if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,15) struct mutex lock; @@ -275,6 +323,7 @@ struct em28xx { struct list_head inqueue, outqueue; wait_queue_head_t open, wait_frame, wait_stream; struct video_device *vbi_dev; + struct video_device *radio_dev; unsigned char eedata[256]; @@ -300,6 +349,15 @@ struct em28xx { struct em28xx_fh { struct em28xx *dev; unsigned int stream_on:1; /* Locks streams */ + int radio; +}; + +struct em28xx_ops { + struct list_head next; + char *name; + int id; + int (*init)(struct em28xx *); + int (*fini)(struct em28xx *); }; /* Provided by em28xx-i2c.c */ @@ -328,8 +386,9 @@ int em28xx_write_regs_req(struct em28xx *dev, u8 req, u16 reg, char *buf, int em28xx_write_regs(struct em28xx *dev, u16 reg, char *buf, int len); int em28xx_write_reg_bits(struct em28xx *dev, u16 reg, u8 val, u8 bitmask); -int em28xx_write_ac97(struct em28xx *dev, u8 reg, u8 * val); +int em28xx_set_audio_source(struct em28xx *dev); int em28xx_audio_analog_set(struct em28xx *dev); + int em28xx_colorlevels_set_default(struct em28xx *dev); int em28xx_capture_start(struct em28xx *dev, int start); int em28xx_outfmt_set_yuv422(struct em28xx *dev); @@ -338,6 +397,10 @@ int em28xx_init_isoc(struct em28xx *dev); void em28xx_uninit_isoc(struct em28xx *dev); int em28xx_set_alternate(struct em28xx *dev); +/* Provided by em28xx-video.c */ +int em28xx_register_extension(struct em28xx_ops *dev); +void em28xx_unregister_extension(struct em28xx_ops *dev); + /* Provided by em28xx-cards.c */ extern int em2800_variant_detect(struct usb_device* udev,int model); extern void em28xx_pre_card_setup(struct em28xx *dev); @@ -350,6 +413,7 @@ extern const unsigned int em28xx_bcount; #define EM2800_AUDIOSRC_REG 0x08 /* em28xx registers */ +#define I2C_CLK_REG 0x06 #define CHIPID_REG 0x0a #define USBSUSP_REG 0x0c /* */ @@ -401,6 +465,7 @@ extern const unsigned int em28xx_bcount; /* em202 registers */ #define MASTER_AC97 0x02 +#define LINE_IN_AC97 0x10 #define VIDEO_AC97 0x14 /* register settings */ @@ -425,28 +490,6 @@ extern const unsigned int em28xx_bcount; printk(KERN_WARNING "%s: "fmt,\ dev->name , ##arg); } while (0) -inline static int em28xx_audio_source(struct em28xx *dev, int input) -{ - if(dev->is_em2800){ - u8 tmp = EM2800_AUDIO_SRC_TUNER; - if(input == EM28XX_AUDIO_SRC_LINE) - tmp = EM2800_AUDIO_SRC_LINE; - em28xx_write_regs(dev, EM2800_AUDIOSRC_REG, &tmp, 1); - } - return em28xx_write_reg_bits(dev, AUDIOSRC_REG, input, 0xc0); -} - -inline static int em28xx_audio_usb_mute(struct em28xx *dev, int mute) -{ - return em28xx_write_reg_bits(dev, XCLK_REG, mute ? 0x00 : 0x80, 0x80); -} - -inline static int em28xx_audio_analog_setup(struct em28xx *dev) -{ - /* unmute video mixer with default volume level */ - return em28xx_write_ac97(dev, VIDEO_AC97, "\x08\x08"); -} - inline static int em28xx_compression_disable(struct em28xx *dev) { /* side effect of disabling scaler and mixer */ diff --git a/linux/drivers/media/video/ivtv/Makefile b/linux/drivers/media/video/ivtv/Makefile index e8eefd96d..a0389014f 100644 --- a/linux/drivers/media/video/ivtv/Makefile +++ b/linux/drivers/media/video/ivtv/Makefile @@ -6,3 +6,8 @@ ivtv-objs := ivtv-routing.o ivtv-cards.o ivtv-controls.o \ obj-$(CONFIG_VIDEO_IVTV) += ivtv.o obj-$(CONFIG_VIDEO_FB_IVTV) += ivtvfb.o + +EXTRA_CFLAGS += -Idrivers/media/video +EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core +EXTRA_CFLAGS += -Idrivers/media/dvb/frontends + diff --git a/linux/drivers/media/video/tda8290.c b/linux/drivers/media/video/tda8290.c index 960cd2664..fb95df7d3 100644 --- a/linux/drivers/media/video/tda8290.c +++ b/linux/drivers/media/video/tda8290.c @@ -535,6 +535,10 @@ static void tda829x_release(struct dvb_frontend *fe) fe->analog_demod_priv = NULL; } +static struct tda18271_config tda829x_tda18271_config = { + .gate = TDA18271_GATE_ANALOG, +}; + static int tda829x_find_tuner(struct dvb_frontend *fe) { struct tda8290_priv *priv = fe->analog_demod_priv; @@ -596,11 +600,11 @@ static int tda829x_find_tuner(struct dvb_frontend *fe) return -EREMOTEIO; } - if (data == 0x83) { + if ((data == 0x83) || (data == 0x84)) { priv->ver |= TDA18271; tda18271_attach(fe, priv->tda827x_addr, priv->i2c_props.adap, - TDA18271_GATE_ANALOG); + &tda829x_tda18271_config); } else { if ((data & 0x3c) == 0) priv->ver |= TDA8275; diff --git a/linux/drivers/media/video/tuner-core.c b/linux/drivers/media/video/tuner-core.c index f6608f1b2..d93a764aa 100644 --- a/linux/drivers/media/video/tuner-core.c +++ b/linux/drivers/media/video/tuner-core.c @@ -276,7 +276,7 @@ static void set_radio_freq(struct i2c_client *c, unsigned int freq) tuner_warn ("tuner type not set\n"); return; } - if (analog_ops->set_params) { + if (NULL == analog_ops->set_params) { tuner_warn ("tuner has no way to set radio frequency\n"); return; } diff --git a/linux/drivers/media/video/tuner-xc2028.c b/linux/drivers/media/video/tuner-xc2028.c index cf54d24bb..dbaf0ce8d 100644 --- a/linux/drivers/media/video/tuner-xc2028.c +++ b/linux/drivers/media/video/tuner-xc2028.c @@ -678,7 +678,7 @@ static int check_firmware(struct dvb_frontend *fe, unsigned int type, return rc; } - if (priv->ctrl.mts) + if (priv->ctrl.mts && ! (type & FM) ) type |= MTS; retry: diff --git a/v4l2-apps/util/qv4l2/general-tab.cpp b/v4l2-apps/util/qv4l2/general-tab.cpp index 93ab329ba..d7f5a98d2 100644 --- a/v4l2-apps/util/qv4l2/general-tab.cpp +++ b/v4l2-apps/util/qv4l2/general-tab.cpp @@ -31,7 +31,7 @@ #include <sys/ioctl.h> #include <errno.h> -GeneralTab::GeneralTab(int _fd, int n, QWidget *parent) : +GeneralTab::GeneralTab(const char *device, int _fd, int n, QWidget *parent) : QGrid(n, parent), fd(_fd) { @@ -39,6 +39,28 @@ GeneralTab::GeneralTab(int _fd, int n, QWidget *parent) : setSpacing(3); + memset(&querycap, 0, sizeof(querycap)); + if (ioctl(fd, VIDIOC_QUERYCAP, &querycap) >=0) { + QLabel *l1 = new QLabel("Device:", this); + QLabel *l1t = new QLabel(device, this); + l1->setAlignment(Qt::AlignRight); + + QLabel *l2 = new QLabel("Driver:", this); + l2->setAlignment(Qt::AlignRight); + + QLabel *l2t = new QLabel((char *)querycap.driver, this); + + QLabel *l3 = new QLabel("Card:", this); + l3->setAlignment(Qt::AlignRight); + + QLabel *l3t = new QLabel((char *)querycap.card, this); + + QLabel *l4 = new QLabel("Bus:", this); + l4->setAlignment(Qt::AlignRight); + + QLabel *l4t = new QLabel((char *)querycap.bus_info, this); + } + memset(&tuner, 0, sizeof(tuner)); ioctl(fd, VIDIOC_G_TUNER, &tuner); if (tuner.rangehigh>INT_MAX) diff --git a/v4l2-apps/util/qv4l2/general-tab.h b/v4l2-apps/util/qv4l2/general-tab.h index 110632014..8130e11e1 100644 --- a/v4l2-apps/util/qv4l2/general-tab.h +++ b/v4l2-apps/util/qv4l2/general-tab.h @@ -33,7 +33,7 @@ class GeneralTab: public QGrid Q_OBJECT public: - GeneralTab(int fd, int n, QWidget *parent = 0); + GeneralTab(const char *device, int fd, int n, QWidget *parent = 0); virtual ~GeneralTab() {} private slots: @@ -57,6 +57,7 @@ private: int fd; struct v4l2_tuner tuner; + struct v4l2_capability querycap; // General tab QComboBox *videoInput; diff --git a/v4l2-apps/util/qv4l2/qv4l2.cpp b/v4l2-apps/util/qv4l2/qv4l2.cpp index f9fb07e09..a93608af2 100644 --- a/v4l2-apps/util/qv4l2/qv4l2.cpp +++ b/v4l2-apps/util/qv4l2/qv4l2.cpp @@ -118,7 +118,7 @@ void ApplicationWindow::setDevice(const QString &device) fd = ::open(device, O_RDONLY); if (fd >= 0) { - tabs->addTab(new GeneralTab(fd, 4, tabs), "General"); + tabs->addTab(new GeneralTab(device, fd, 4, tabs), "General"); addTabs(); } if (QWidget *current = tabs->currentPage()) { diff --git a/v4l2-apps/util/xc3028-firmware/extract_head.h b/v4l2-apps/util/xc3028-firmware/extract_head.h new file mode 100644 index 000000000..373ac78ba --- /dev/null +++ b/v4l2-apps/util/xc3028-firmware/extract_head.h @@ -0,0 +1,161 @@ +char *extract_header = "#!/usr/bin/perl\n" + "#use strict;\n" + "use IO::Handle;\n" + "\n" + "my $debug=0;\n" + "\n" + "sub verify ($$)\n" + "{\n" + " my ($filename, $hash) = @_;\n" + " my ($testhash);\n" + "\n" + " if (system(\"which md5sum > /dev/null 2>&1\")) {\n" + " die \"This firmware requires the md5sum command - see http://www.gnu.org/software/coreutils/\\n\";\n" + " }\n" + "\n" + " open(CMD, \"md5sum \".$filename.\"|\");\n" + " $testhash = <CMD>;\n" + " $testhash =~ /([a-zA-Z0-9]*)/;\n" + " $testhash = $1;\n" + " close CMD;\n" + " die \"Hash of extracted file does not match (found $testhash, expected $hash!\\n\" if ($testhash ne $hash);\n" + "}\n" + "\n" + "sub get_hunk ($$)\n" + "{\n" + " my ($offset, $length) = @_;\n" + " my ($chunklength, $buf, $rcount, $out);\n" + "\n" + " sysseek(INFILE, $offset, SEEK_SET);\n" + " while ($length > 0) {\n" + " # Calc chunk size\n" + " $chunklength = 2048;\n" + " $chunklength = $length if ($chunklength > $length);\n" + "\n" + " $rcount = sysread(INFILE, $buf, $chunklength);\n" + " die \"Ran out of data\\n\" if ($rcount != $chunklength);\n" + " $out .= $buf;\n" + " $length -= $rcount;\n" + " }\n" + " return $out;\n" + "}\n" + "\n" + "sub write_le16($)\n" + "{\n" + " my $val = shift;\n" + " my $msb = ($val >> 8) &0xff;\n" + " my $lsb = $val & 0xff;\n" + "\n" + " syswrite(OUTFILE, chr($lsb).chr($msb));\n" + "}\n" + "\n" + "sub write_le32($)\n" + "{\n" + " my $val = shift;\n" + " my $l3 = ($val >> 24) & 0xff;\n" + " my $l2 = ($val >> 16) & 0xff;\n" + " my $l1 = ($val >> 8) & 0xff;\n" + " my $l0 = $val & 0xff;\n" + "\n" + " syswrite(OUTFILE, chr($l0).chr($l1).chr($l2).chr($l3));\n" + "}\n" + "\n" + "sub write_le64($$)\n" + "{\n" + " my $msb_val = shift;\n" + " my $lsb_val = shift;\n" + " my $l7 = ($msb_val >> 24) & 0xff;\n" + " my $l6 = ($msb_val >> 16) & 0xff;\n" + " my $l5 = ($msb_val >> 8) & 0xff;\n" + " my $l4 = $msb_val & 0xff;\n\n" + " my $l3 = ($lsb_val >> 24) & 0xff;\n" + " my $l2 = ($lsb_val >> 16) & 0xff;\n" + " my $l1 = ($lsb_val >> 8) & 0xff;\n" + " my $l0 = $lsb_val & 0xff;\n" + "\n" + " syswrite(OUTFILE,\n" + " chr($l0).chr($l1).chr($l2).chr($l3).\n" + " chr($l4).chr($l5).chr($l6).chr($l7));\n" + "}\n" + "\n" + "sub write_hunk($$)\n" + "{\n" + " my ($offset, $length) = @_;\n" + " my $out = get_hunk($offset, $length);\n" + "\n" + " printf \"(len %d) \",$length if ($debug);\n" + "\n" + " for (my $i=0;$i<$length;$i++) {\n" + " printf \"%02x \",ord(substr($out,$i,1)) if ($debug);\n" + " }\n" + " printf \"\\n\" if ($debug);\n" + "\n" + " syswrite(OUTFILE, $out);\n" + "}\n" + "\n" + "sub write_hunk_fix_endian($$)\n" + "{\n" + " my ($offset, $length) = @_;\n" + " my $out = get_hunk($offset, $length);\n" + "\n" + " printf \"(len_fix %d) \",$length if ($debug);\n" + "\n" + " for (my $i=0;$i<$length;$i++) {\n" + " printf \"%02x \",ord(substr($out,$i,1)) if ($debug);\n" + " }\n" + " printf \"\\n\" if ($debug);\n" + "\n" + " $i=0;\n" + " while ($i<$length) {\n" + " my $size = ord(substr($out,$i,1))*256+ord(substr($out,$i+1,1));\n" + " syswrite(OUTFILE, substr($out,$i+1,1));\n" + " syswrite(OUTFILE, substr($out,$i,1));\n" + " $i+=2;\n" + " if ($size>0 && $size <0x8000) {\n" + " for (my $j=0;$j<$size;$j++) {\n" + " syswrite(OUTFILE, substr($out,$j+$i,1));\n" + " }\n" + " $i+=$size;\n" + " }\n" + " }\n" + "}\n" + "\n" + "sub main_firmware($$$$)\n" + "{\n" + " my $out;\n" + " my $j=0;\n" + " my $outfile = shift;\n" + " my $name = shift;\n" + " my $version = shift;\n" + " my $nr_desc = shift;\n" + "\n" + " for ($j = length($name); $j <32; $j++) {\n" + " $name = $name.chr(0);\n" + "}\n\n" + " open OUTFILE, \">$outfile\";\n" + " syswrite(OUTFILE, $name);\n" + " write_le16($version);\n" + " write_le16($nr_desc);\n"; + +char *write_hunk = "\twrite_hunk(%d, %d);\n"; +char *write_hunk_fix_endian = "\twrite_hunk_fix_endian(%d, %d);\n"; + +// Parameters: file windows filename, hash, outfile, version, name +char *end_extract = "}\n\nsub extract_firmware {\n" + " my $sourcefile = \"%s\";\n" + " my $hash = \"%s\";\n" + " my $outfile = \"%s\";\n" + " my $name = \"%s\";\n" + " my $version = %d;\n" + " my $nr_desc = %d;\n" + " my $out;\n" + "\n" + " verify($sourcefile, $hash);\n" + "\n" + " open INFILE, \"<$sourcefile\";\n" + " main_firmware($outfile, $name, $version, $nr_desc);\n" + " close INFILE;\n" + "}\n" + "\n" + "extract_firmware;\n" + "printf \"Firmwares generated.\\n\";\n"; diff --git a/v4l2-apps/util/xc3028-firmware/firmware-tool.c b/v4l2-apps/util/xc3028-firmware/firmware-tool.c index a8e6d0c01..28ae70bb2 100644 --- a/v4l2-apps/util/xc3028-firmware/firmware-tool.c +++ b/v4l2-apps/util/xc3028-firmware/firmware-tool.c @@ -3,6 +3,10 @@ Copyright (C) 2007 Michel Ludwig <michel.ludwig@gmail.com> + Copyright (C) 2007, 2008 Mauro Carvalho Chehab <mchehab@infradead.org> + - Improve --list command + - Add --seek command + 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 version 2 @@ -31,6 +35,7 @@ #include "../../../linux/drivers/media/video/tuner-xc2028-types.h" #include "../../../linux/include/linux/videodev2.h" +#include "extract_head.h" #include "standards.h" #define LIST_ACTION (1<<0) @@ -38,6 +43,7 @@ #define DELETE_ACTION (1<<2) #define SET_TYPE_ACTION (1<<3) #define SET_ID_ACTION (1<<4) +#define SEEK_FIRM_ACTION (1<<5) struct firmware_description { __u32 type; @@ -287,74 +293,74 @@ void write_firmware_file(const char* filename, struct firmware *f) { close(fd); } -void dump_firm_type(unsigned int type) +void dump_firm_type(FILE *fp, unsigned int type) { if (type & SCODE) - printf("SCODE FW "); + fprintf(fp, "SCODE FW "); else if (type & BASE) - printf("BASE FW "); + fprintf(fp, "BASE FW "); else - printf("STD FW "); + fprintf(fp, "STD FW "); if (type & F8MHZ) - printf("F8MHZ "); + fprintf(fp, "F8MHZ "); if (type & MTS) - printf("MTS "); + fprintf(fp, "MTS "); if (type & D2620) - printf("D2620 "); + fprintf(fp, "D2620 "); if (type & D2633) - printf("D2633 "); + fprintf(fp, "D2633 "); if (type & DTV6) - printf("DTV6 "); + fprintf(fp, "DTV6 "); if (type & QAM) - printf("QAM "); + fprintf(fp, "QAM "); if (type & DTV7) - printf("DTV7 "); + fprintf(fp, "DTV7 "); if (type & DTV78) - printf("DTV78 "); + fprintf(fp, "DTV78 "); if (type & DTV8) - printf("DTV8 "); + fprintf(fp, "DTV8 "); if (type & FM) - printf("FM "); + fprintf(fp, "FM "); if (type & INPUT1) - printf("INPUT1 "); + fprintf(fp, "INPUT1 "); if (type & LCD) - printf("LCD "); + fprintf(fp, "LCD "); if (type & NOGD) - printf("NOGD "); + fprintf(fp, "NOGD "); if (type & MONO) - printf("MONO "); + fprintf(fp, "MONO "); if (type & ATSC) - printf("ATSC "); + fprintf(fp, "ATSC "); if (type & IF) - printf("IF "); + fprintf(fp, "IF "); if (type & LG60) - printf("LG60 "); + fprintf(fp, "LG60 "); if (type & ATI638) - printf("ATI638 "); + fprintf(fp, "ATI638 "); if (type & OREN538) - printf("OREN538 "); + fprintf(fp, "OREN538 "); if (type & OREN36) - printf("OREN36 "); + fprintf(fp, "OREN36 "); if (type & TOYOTA388) - printf("TOYOTA388 "); + fprintf(fp, "TOYOTA388 "); if (type & TOYOTA794) - printf("TOYOTA794 "); + fprintf(fp, "TOYOTA794 "); if (type & DIBCOM52) - printf("DIBCOM52 "); + fprintf(fp, "DIBCOM52 "); if (type & ZARLINK456) - printf("ZARLINK456 "); + fprintf(fp, "ZARLINK456 "); if (type & CHINA) - printf("CHINA "); + fprintf(fp, "CHINA "); if (type & F6MHZ) - printf("F6MHZ "); + fprintf(fp, "F6MHZ "); if (type & INPUT2) - printf("INPUT2 "); + fprintf(fp, "INPUT2 "); if (type & HAS_IF) - printf("HAS IF "); + fprintf(fp, "HAS IF "); } -void dump_firm_std(v4l2_std_id id) +void dump_firm_std(FILE *fp, v4l2_std_id id) { v4l2_std_id old=-1, curr_id; @@ -362,127 +368,127 @@ void dump_firm_std(v4l2_std_id id) while (old!=id) { old=id; if ( (id & V4L2_STD_PAL) == V4L2_STD_PAL) { - printf ("PAL "); + fprintf (fp, "PAL "); curr_id = V4L2_STD_PAL; } else if ( (id & V4L2_STD_MN) == V4L2_STD_MN) { - printf ("NTSC PAL/M PAL/N "); + fprintf (fp, "NTSC PAL/M PAL/N "); curr_id = V4L2_STD_PAL; } else if ( (id & V4L2_STD_PAL_BG) == V4L2_STD_PAL_BG) { - printf ("PAL/BG "); + fprintf (fp, "PAL/BG "); curr_id = V4L2_STD_PAL_BG; } else if ( (id & V4L2_STD_PAL_DK) == V4L2_STD_PAL_DK) { - printf ("PAL/DK "); + fprintf (fp, "PAL/DK "); curr_id = V4L2_STD_PAL_DK; } else if ( (id & V4L2_STD_PAL_B) == V4L2_STD_PAL_B) { - printf ("PAL/B "); + fprintf (fp, "PAL/B "); curr_id = V4L2_STD_PAL_B; } else if ( (id & V4L2_STD_PAL_B1) == V4L2_STD_PAL_B1) { - printf ("PAL/B1 "); + fprintf (fp, "PAL/B1 "); curr_id = V4L2_STD_PAL_B1; } else if ( (id & V4L2_STD_PAL_G) == V4L2_STD_PAL_G) { - printf ("PAL/G "); + fprintf (fp, "PAL/G "); curr_id = V4L2_STD_PAL_G; } else if ( (id & V4L2_STD_PAL_H) == V4L2_STD_PAL_H) { - printf ("PAL/H "); + fprintf (fp, "PAL/H "); curr_id = V4L2_STD_PAL_H; } else if ( (id & V4L2_STD_PAL_I) == V4L2_STD_PAL_I) { - printf ("PAL/I "); + fprintf (fp, "PAL/I "); curr_id = V4L2_STD_PAL_I; } else if ( (id & V4L2_STD_PAL_D) == V4L2_STD_PAL_D) { - printf ("PAL/D "); + fprintf (fp, "PAL/D "); curr_id = V4L2_STD_PAL_D; } else if ( (id & V4L2_STD_PAL_D1) == V4L2_STD_PAL_D1) { - printf ("PAL/D1 "); + fprintf (fp, "PAL/D1 "); curr_id = V4L2_STD_PAL_D1; } else if ( (id & V4L2_STD_PAL_K) == V4L2_STD_PAL_K) { - printf ("PAL/K "); + fprintf (fp, "PAL/K "); curr_id = V4L2_STD_PAL_K; } else if ( (id & V4L2_STD_PAL_M) == V4L2_STD_PAL_M) { - printf ("PAL/M "); + fprintf (fp, "PAL/M "); curr_id = V4L2_STD_PAL_M; } else if ( (id & V4L2_STD_PAL_N) == V4L2_STD_PAL_N) { - printf ("PAL/N "); + fprintf (fp, "PAL/N "); curr_id = V4L2_STD_PAL_N; } else if ( (id & V4L2_STD_PAL_Nc) == V4L2_STD_PAL_Nc) { - printf ("PAL/Nc "); + fprintf (fp, "PAL/Nc "); curr_id = V4L2_STD_PAL_Nc; } else if ( (id & V4L2_STD_PAL_60) == V4L2_STD_PAL_60) { - printf ("PAL/60 "); + fprintf (fp, "PAL/60 "); curr_id = V4L2_STD_PAL_60; } else if ( (id & V4L2_STD_NTSC) == V4L2_STD_NTSC) { - printf ("NTSC "); + fprintf (fp, "NTSC "); curr_id = V4L2_STD_NTSC; } else if ( (id & V4L2_STD_NTSC_M) == V4L2_STD_NTSC_M) { - printf ("NTSC/M "); + fprintf (fp, "NTSC/M "); curr_id = V4L2_STD_NTSC_M; } else if ( (id & V4L2_STD_NTSC_M_JP) == V4L2_STD_NTSC_M_JP) { - printf ("NTSC/M Jp "); + fprintf (fp, "NTSC/M Jp "); curr_id = V4L2_STD_NTSC_M_JP; } else if ( (id & V4L2_STD_NTSC_443) == V4L2_STD_NTSC_443) { - printf ("NTSC 443 "); + fprintf (fp, "NTSC 443 "); curr_id = V4L2_STD_NTSC_443; } else if ( (id & V4L2_STD_NTSC_M_KR) == V4L2_STD_NTSC_M_KR) { - printf ("NTSC/M Kr "); + fprintf (fp, "NTSC/M Kr "); curr_id = V4L2_STD_NTSC_M_KR; } else if ( (id & V4L2_STD_SECAM) == V4L2_STD_SECAM) { - printf ("SECAM "); + fprintf (fp, "SECAM "); curr_id = V4L2_STD_SECAM; } else if ( (id & V4L2_STD_SECAM_DK) == V4L2_STD_SECAM_DK) { - printf ("SECAM/DK "); + fprintf (fp, "SECAM/DK "); curr_id = V4L2_STD_SECAM_DK; } else if ( (id & V4L2_STD_SECAM_B) == V4L2_STD_SECAM_B) { - printf ("SECAM/B "); + fprintf (fp, "SECAM/B "); curr_id = V4L2_STD_SECAM_B; } else if ( (id & V4L2_STD_SECAM_D) == V4L2_STD_SECAM_D) { - printf ("SECAM/D "); + fprintf (fp, "SECAM/D "); curr_id = V4L2_STD_SECAM_D; } else if ( (id & V4L2_STD_SECAM_G) == V4L2_STD_SECAM_G) { - printf ("SECAM/G "); + fprintf (fp, "SECAM/G "); curr_id = V4L2_STD_SECAM_G; } else if ( (id & V4L2_STD_SECAM_H) == V4L2_STD_SECAM_H) { - printf ("SECAM/H "); + fprintf (fp, "SECAM/H "); curr_id = V4L2_STD_SECAM_H; } else if ( (id & V4L2_STD_SECAM_K) == V4L2_STD_SECAM_K) { - printf ("SECAM/K "); + fprintf (fp, "SECAM/K "); curr_id = V4L2_STD_SECAM_K; } else if ( (id & V4L2_STD_SECAM_K1) == V4L2_STD_SECAM_K1) { - printf ("SECAM/K1 "); + fprintf (fp, "SECAM/K1 "); curr_id = V4L2_STD_SECAM_K1; } else if ( (id & V4L2_STD_SECAM_K3) == V4L2_STD_SECAM_K3) { - printf ("SECAM/K3 "); + fprintf (fp, "SECAM/K3 "); curr_id = V4L2_STD_SECAM_K3; } else if ( (id & V4L2_STD_SECAM_L) == V4L2_STD_SECAM_L) { - printf ("SECAM/L "); + fprintf (fp, "SECAM/L "); curr_id = V4L2_STD_SECAM_L; } else if ( (id & V4L2_STD_SECAM_LC) == V4L2_STD_SECAM_LC) { - printf ("SECAM/Lc "); + fprintf (fp, "SECAM/Lc "); curr_id = V4L2_STD_SECAM_LC; } else if ( (id & V4L2_STD_A2) == V4L2_STD_A2) { - printf ("A2 "); + fprintf (fp, "A2 "); curr_id = V4L2_STD_A2; } else if ( (id & V4L2_STD_A2_A) == V4L2_STD_A2_A) { - printf ("A2/A "); + fprintf (fp, "A2/A "); curr_id = V4L2_STD_A2_A; } else if ( (id & V4L2_STD_A2_B) == V4L2_STD_A2_B) { - printf ("A2/B "); + fprintf (fp, "A2/B "); curr_id = V4L2_STD_A2_B; } else if ( (id & V4L2_STD_NICAM) == V4L2_STD_NICAM) { - printf ("NICAM "); + fprintf (fp, "NICAM "); curr_id = V4L2_STD_NICAM; } else if ( (id & V4L2_STD_NICAM_A) == V4L2_STD_NICAM_A) { - printf ("NICAM/A "); + fprintf (fp, "NICAM/A "); curr_id = V4L2_STD_NICAM_A; } else if ( (id & V4L2_STD_NICAM_B) == V4L2_STD_NICAM_B) { - printf ("NICAM/B "); + fprintf (fp, "NICAM/B "); curr_id = V4L2_STD_NICAM_B; } else if ( (id & V4L2_STD_AM) == V4L2_STD_AM) { - printf ("AM "); + fprintf (fp, "AM "); curr_id = V4L2_STD_AM; } else if ( (id & V4L2_STD_BTSC) == V4L2_STD_BTSC) { - printf ("BTSC "); + fprintf (fp, "BTSC "); curr_id = V4L2_STD_BTSC; } else if ( (id & V4L2_STD_EIAJ) == V4L2_STD_EIAJ) { - printf ("EIAJ "); + fprintf (fp, "EIAJ "); curr_id = V4L2_STD_EIAJ; } else { curr_id = 0; @@ -492,6 +498,19 @@ void dump_firm_std(v4l2_std_id id) } } +void list_firmware_desc(FILE *fp, struct firmware_description *desc) +{ + fprintf(fp, "type: "); + dump_firm_type(fp, desc->type); + fprintf(fp, "(0x%08x), ", desc->type); + if (desc->type & HAS_IF) + fprintf(fp, "IF = %.2f MHz ", desc->int_freq/1000.0); + fprintf(fp, "id: "); + dump_firm_std(fp, desc->id); + fprintf(fp, "(%016llx), ", desc->id); + fprintf(fp, "size: %u\n", desc->size); +} + void list_firmware(struct firmware *f) { unsigned int i = 0; @@ -501,15 +520,7 @@ void list_firmware(struct firmware *f) { printf("standards:\t%u\n", f->nr_desc); for(i = 0; i < f->nr_desc; ++i) { printf("Firmware %2u, ", i); - printf("type: "); - dump_firm_type(f->desc[i].type); - printf("(0x%08x), ", f->desc[i].type); - if (f->desc[i].type & HAS_IF) - printf("IF = %.2f MHz ", f->desc[i].int_freq/1000.0); - printf("id: "); - dump_firm_std(f->desc[i].id); - printf("(%016llx), ", f->desc[i].id); - printf("size: %u\n", f->desc[i].size); + list_firmware_desc(stdout, &f->desc[i]); } } @@ -552,6 +563,285 @@ void set_standard_id(struct firmware* f, char* firmware_file, __u16 i, __u32 id) write_firmware_file(firmware_file, f); } +struct chunk_hunk; + +struct chunk_hunk { + unsigned char *data; + long pos; + int size; + int need_fix_endian; + struct chunk_hunk *next; +}; + +int seek_chunks(struct chunk_hunk *hunk, + unsigned char *seek, unsigned char *endp, /* File to seek */ + unsigned char *fdata, unsigned char *endf) /* Firmware */ +{ + unsigned char *fpos, *p, *p2, *lastp; + int rc, fsize; + unsigned char *temp_data; + + /* Method 1a: Seek for a complete firmware */ + for (p = seek; p < endp; p++) { + fpos = p; + for (p2 = fdata; p2 < endf; p2++, fpos++) { + if (*fpos != *p2) + break; + } + if (p2 == endf) { + hunk->data = NULL; + hunk->pos = p - seek; + hunk->size = endf - fdata; + hunk->next = NULL; + hunk->need_fix_endian = 0; + + return 1; + } + } + + fsize = endf - fdata; + temp_data = malloc(fsize); + memcpy(temp_data, fdata, fsize); + + /* Try again, changing endian */ + for (p2 = temp_data; p2 < temp_data + fsize;) { + unsigned char c; + int size = *p2 + (*(p2 + 1) << 8); + c = *p2; + *p2 = *(p2 + 1); + *(p2 + 1) = c; + p2+=2; + if ((size > 0) && (size < 0x8000)) + p2 += size; + } + + /* Method 1b: Seek for a complete firmware with changed endians */ + for (p = seek; p < endp; p++) { + fpos = p; + for (p2 = temp_data; p2 < temp_data + fsize; p2++, fpos++) { + if (*fpos != *p2) + break; + } + if (p2 == temp_data + fsize) { + hunk->data = NULL; + hunk->pos = p - seek; + hunk->size = endf - fdata; + hunk->next = NULL; + hunk->need_fix_endian = 1; + return 1; + } + } + + free(temp_data); + + /* Method 2: Seek for each firmware chunk */ + p = seek; + for (p2 = fdata; p2 < endf;) { + int size = *p2 + (*(p2 + 1) << 8); + + /* Encode size/reset/sleep directly */ + hunk->size = 2; + hunk->data = malloc(hunk->size); + memcpy(hunk->data, p2, hunk->size); + hunk->pos = -1; + hunk->next = calloc(1, sizeof(hunk)); + hunk->need_fix_endian = 0; + hunk = hunk->next; + p2 += 2; + + if ((size > 0) && (size < 0x8000)) { + unsigned char *ep; + int found = 0; + ep = p2 + size; + /////////////////// + for (; p < endp; p++) { + unsigned char *p3; + fpos = p; + for (p3 = p2; p3 < ep; p3++, fpos++) + if (*fpos != *p3) + break; + if (p3 == ep) { + found = 1; + hunk->pos = p - seek; + hunk->size = size; + hunk->next = calloc(1, sizeof(hunk)); + hunk->need_fix_endian = 0; + hunk = hunk->next; + + break; + } + } + if (!found) { + goto not_found; + } + p2 += size; + } + } + + return 2; + +not_found: + printf("Couldn't find firmware\n"); +return 0; + + /* Method 3: Seek for first firmware chunks */ +#if 0 +seek_next: + for (p = seek; p < endp; p++) { + fpos = p; + for (p2 = fdata; p2 < endf; p2++, fpos++) { + if (*fpos != *p2) + break; + } + if (p2 > fdata + 2) { + int i = 0; + printf("Found %ld equal bytes at %ld:\n", + p2 - fdata, p - seek); + fpos = p; + lastp = fpos; + for (p2 = fdata; p2 < endf; p2++, fpos++) { + if (*fpos != *p2) + break; + printf("%02x ",*p2); + } + for (i=0; p2 < endf && i <5 ; p2++, fpos++, i++) { + printf("%02x(%02x) ",*p2 , *fpos); + } + printf("\n"); + /* Seek for the next chunk */ + fdata = p2; + + if (fdata == endf) { + printf ("Found all chunks.\n"); + return 1; + } + } + } + + printf ("NOT FOUND: %02x\n", *fdata); + fdata++; + goto seek_next; +#endif +} + +void seek_firmware(struct firmware *f, char *seek_file, char *write_file) { + unsigned int i = 0, nfound = 0; + long size, rd = 0; + unsigned char *seek, *p, *endp, *p2, *endp2, *fpos; + /*FIXME: Calculate it, instead of using a hardcode value */ + char *md5 = "0e44dbf63bb0169d57446aec21881ff2"; + FILE *fp; + + struct chunk_hunk hunks[f->nr_desc]; + memset (hunks, 0, sizeof(hunks)); + + fp=fopen(seek_file, "r"); + if (!fp) { + perror("Opening seek file"); + exit(-1); + } + fseek(fp, 0L, SEEK_END); + size = ftell(fp); + rewind(fp); + seek = malloc(size); + p = seek; + + do { + i = fread(p, 1, 16768, fp); + if (i > 0) { + rd += i; + p += i; + } + } while (i > 0); + + fclose(fp); + + if (rd != size) { + fprintf(stderr, "Error while reading the seek file: " + "should read %ld, instead of %ld ", size, rd); + exit (-1); + } + endp = p; + + printf("firmware name:\t%s\n", f->name); + printf("version:\t%d.%d (%u)\n", f->version >> 8, f->version & 0xff, + f->version); + printf("number of standards:\t%u\n", f->nr_desc); + for(i = 0; i < f->nr_desc; ++i) { + int found; + + endp2 = f->desc[i].data + f->desc[i].size; + + found = seek_chunks (&hunks[i], + seek, endp, f->desc[i].data, endp2); + + if (!found) { + printf("NOT FOUND: Firmware %d ", i); + list_firmware_desc(stdout, &f->desc[i]); + } else { + nfound++; + printf("Found with method %d: Firmware %d ", found, i); + list_firmware_desc(stdout, &f->desc[i]); + } + } + printf ("Found %d complete firmwares\n", nfound); + + if (!write_file) + return; + + fp = fopen(write_file, "w"); + if (!fp) { + perror("Writing firmware file"); + exit(-1); + } + + fprintf(fp, "%s", extract_header); + for (i = 0; i < f->nr_desc; ++i) { + struct chunk_hunk *hunk = &hunks[i]; + + fprintf(fp, "\n\t#\n\t# Firmware %d, ", i); + list_firmware_desc(fp, &f->desc[i]); + fprintf(fp, "\t#\n\n"); + + fprintf(fp, "\twrite_le32(0x%08x);\t\t\t# Type\n", + f->desc[i].type); + fprintf(fp, "\twrite_le64(0x%08Lx, 0x%08Lx);\t# ID\n", + f->desc[i].id>>32, f->desc[i].id & 0xffffffff); + if (f->desc[i].type & HAS_IF) + fprintf(fp, "\twrite_le16(%d);\t\t\t# IF\n", + f->desc[i].int_freq); + fprintf(fp, "\twrite_le32(%d);\t\t\t# Size\n", + f->desc[i].size); + + while (hunk) { + if (hunk->data) { + int j; + fprintf(fp, "\tsyswrite(OUTFILE, "); + for (j = 0; j < hunk->size; j++) { + fprintf(fp, "chr(%d)", hunk->data[j]); + if (j < hunk->size-1) + fprintf(fp,"."); + } + fprintf(fp,");\n"); + } else { + if (!hunk->size) + break; + + if (hunk->need_fix_endian) + fprintf(fp, write_hunk_fix_endian, + hunk->pos, hunk->size); + else + fprintf(fp, write_hunk, + hunk->pos, hunk->size); + } + hunk = hunk->next; + } + } + + fprintf(fp, end_extract, seek_file, md5, "xc3028-v27.fw", + f->name, f->version, f->nr_desc); +} + void print_usage(void) { printf("firmware-tool usage:\n"); @@ -560,6 +850,7 @@ void print_usage(void) printf("\t firmware-tool --delete <index> <firmware-file>\n"); printf("\t firmware-tool --type <type> --index <i> <firmware-file>\n"); printf("\t firmware-tool --id <type> --index <i> <firmware-file>\n"); + printf("\t firmware-tool --seek <seek-file> [--write <write-file>] <firmware-file>\n"); } int main(int argc, char* argv[]) @@ -568,6 +859,7 @@ int main(int argc, char* argv[]) int nr_args; unsigned int action = 0; char* firmware_file, *file = NULL, *nr_str = NULL, *index_str = NULL; + char *seek_file = NULL, *write_file = NULL; struct firmware *f; __u64 nr; @@ -579,6 +871,8 @@ int main(int argc, char* argv[]) {"type", required_argument, 0, 't'}, {"id", required_argument, 0, 's'}, {"index", required_argument, 0, 'i'}, + {"seek", required_argument, 0, 'k'}, + {"write", required_argument , 0, 'w'}, {0, 0, 0, 0} }; int option_index = 0; @@ -632,6 +926,14 @@ int main(int argc, char* argv[]) case 'i': index_str = optarg; break; + case 'k': + puts("seek firmwares\n"); + action = SEEK_FIRM_ACTION; + seek_file = optarg; + break; + case 'w': + write_file = optarg; + break; default: print_usage(); return 0; @@ -680,6 +982,9 @@ int main(int argc, char* argv[]) case SET_ID_ACTION: set_standard_id(f, firmware_file, strtoul(index_str, NULL, 10), strtoul(nr_str, NULL, 10)); + + case SEEK_FIRM_ACTION: + seek_firmware(f, seek_file, write_file); break; } return 0; |