Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

V4L/DVB (7002): cx25840: Add basic CX23885 AVCore support

The cx23885/7/8 PCIe bridge has an internal AVCore modelled on
the cx2584x family. Many of the registers positions are identical
but some moved. The register values are also different because
the different bridges run at different clock rates.

Signed-off-by: Steven Toth <stoth@hauppauge.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@infradead.org>

authored by

Steven Toth and committed by
Mauro Carvalho Chehab
f234081b d05051c8

+272 -37
+54 -11
drivers/media/video/cx25840/cx25840-audio.c
··· 32 32 33 33 /* common for all inputs and rates */ 34 34 /* SA_MCLK_SEL=1, SA_MCLK_DIV=0x10 */ 35 - cx25840_write(client, 0x127, 0x50); 35 + if (!state->is_cx23885) 36 + cx25840_write(client, 0x127, 0x50); 36 37 37 38 if (state->aud_input != CX25840_AUDIO_SERIAL) { 38 39 switch (freq) { 39 40 case 32000: 41 + if (state->is_cx23885) { 42 + /* We don't have register values 43 + * so avoid destroying registers. */ 44 + break; 45 + } 40 46 /* VID_PLL and AUX_PLL */ 41 47 cx25840_write4(client, 0x108, 0x1006040f); 42 48 ··· 59 53 break; 60 54 61 55 case 44100: 56 + if (state->is_cx23885) { 57 + /* We don't have register values 58 + * so avoid destroying registers. */ 59 + break; 60 + } 62 61 /* VID_PLL and AUX_PLL */ 63 62 cx25840_write4(client, 0x108, 0x1009040f); 64 63 ··· 80 69 break; 81 70 82 71 case 48000: 72 + if (state->is_cx23885) { 73 + /* We don't have register values 74 + * so avoid destroying registers. */ 75 + break; 76 + } 83 77 /* VID_PLL and AUX_PLL */ 84 78 cx25840_write4(client, 0x108, 0x100a040f); 85 79 ··· 103 87 } else { 104 88 switch (freq) { 105 89 case 32000: 90 + if (state->is_cx23885) { 91 + /* We don't have register values 92 + * so avoid destroying registers. */ 93 + break; 94 + } 106 95 /* VID_PLL and AUX_PLL */ 107 96 cx25840_write4(client, 0x108, 0x1e08040f); 108 97 ··· 130 109 break; 131 110 132 111 case 44100: 112 + if (state->is_cx23885) { 113 + /* We don't have register values 114 + * so avoid destroying registers. */ 115 + break; 116 + } 117 + 133 118 /* VID_PLL and AUX_PLL */ 134 119 cx25840_write4(client, 0x108, 0x1809040f); 135 120 ··· 155 128 break; 156 129 157 130 case 48000: 158 - /* VID_PLL and AUX_PLL */ 159 - cx25840_write4(client, 0x108, 0x180a040f); 131 + if (!state->is_cx23885) { 132 + /* VID_PLL and AUX_PLL */ 133 + cx25840_write4(client, 0x108, 0x180a040f); 160 134 161 - /* AUX_PLL_FRAC */ 162 - cx25840_write4(client, 0x110, 0x0098d6e5); 135 + /* AUX_PLL_FRAC */ 136 + cx25840_write4(client, 0x110, 0x0098d6e5); 137 + } 163 138 164 139 if (state->is_cx25836) 165 140 break; 166 141 167 - /* src1_ctl = 0x08010000 */ 168 - cx25840_write4(client, 0x8f8, 0x08018000); 142 + if (!state->is_cx23885) { 143 + /* src1_ctl */ 144 + cx25840_write4(client, 0x8f8, 0x08018000); 169 145 170 - /* src3/4/6_ctl = 0x08020000 */ 171 - cx25840_write4(client, 0x900, 0x08015555); 172 - cx25840_write4(client, 0x904, 0x08015555); 173 - cx25840_write4(client, 0x90c, 0x08015555); 146 + /* src3/4/6_ctl */ 147 + cx25840_write4(client, 0x900, 0x08015555); 148 + cx25840_write4(client, 0x904, 0x08015555); 149 + cx25840_write4(client, 0x90c, 0x08015555); 150 + } else { 151 + 152 + cx25840_write4(client, 0x8f8, 0x0801867c); 153 + 154 + cx25840_write4(client, 0x900, 0x08014faa); 155 + cx25840_write4(client, 0x904, 0x08014faa); 156 + cx25840_write4(client, 0x90c, 0x08014faa); 157 + } 174 158 break; 175 159 } 176 160 } ··· 226 188 227 189 /* deassert soft reset */ 228 190 cx25840_and_or(client, 0x810, ~0x1, 0x00); 191 + 192 + if (state->is_cx23885) { 193 + /* Ensure the controller is running when we exit */ 194 + cx25840_and_or(client, 0x803, ~0x10, 0x10); 195 + } 229 196 } 230 197 231 198 static int get_volume(struct i2c_client *client)
+193 -26
drivers/media/video/cx25840/cx25840-core.c
··· 13 13 * NTSC sliced VBI support by Christopher Neufeld <television@cneufeld.ca> 14 14 * with additional fixes by Hans Verkuil <hverkuil@xs4all.nl>. 15 15 * 16 + * CX23885 support by Steven Toth <stoth@hauppauge.com>. 17 + * 16 18 * This program is free software; you can redistribute it and/or 17 19 * modify it under the terms of the GNU General Public License 18 20 * as published by the Free Software Foundation; either version 2 ··· 257 255 cx25840_and_or(client, 0x803, ~0x10, 0x10); 258 256 } 259 257 258 + static void cx23885_initialize(struct i2c_client *client) 259 + { 260 + DEFINE_WAIT(wait); 261 + struct cx25840_state *state = i2c_get_clientdata(client); 262 + struct workqueue_struct *q; 263 + 264 + /* Internal Reset */ 265 + cx25840_and_or(client, 0x102, ~0x01, 0x01); 266 + cx25840_and_or(client, 0x102, ~0x01, 0x00); 267 + 268 + /* Stop microcontroller */ 269 + cx25840_and_or(client, 0x803, ~0x10, 0x00); 270 + 271 + /* DIF in reset? */ 272 + cx25840_write(client, 0x398, 0); 273 + 274 + /* Trust the default xtal, no division */ 275 + /* This changes for the cx23888 products */ 276 + cx25840_write(client, 0x2, 0x76); 277 + 278 + /* Bring down the regulator for AUX clk */ 279 + cx25840_write(client, 0x1, 0x40); 280 + 281 + /* Sys PLL frac */ 282 + cx25840_write4(client, 0x11c, 0x01d1744c); 283 + 284 + /* Sys PLL int */ 285 + cx25840_write4(client, 0x118, 0x00000416); 286 + 287 + /* Disable DIF bypass */ 288 + cx25840_write4(client, 0x33c, 0x00000001); 289 + 290 + /* DIF Src phase inc */ 291 + cx25840_write4(client, 0x340, 0x0df7df83); 292 + 293 + /* Vid PLL frac */ 294 + cx25840_write4(client, 0x10c, 0x01b6db7b); 295 + 296 + /* Vid PLL int */ 297 + cx25840_write4(client, 0x108, 0x00000512); 298 + 299 + /* Luma */ 300 + cx25840_write4(client, 0x414, 0x00107d12); 301 + 302 + /* Chroma */ 303 + cx25840_write4(client, 0x420, 0x3d008282); 304 + 305 + /* Aux PLL frac */ 306 + cx25840_write4(client, 0x114, 0x017dbf48); 307 + 308 + /* Aux PLL int */ 309 + cx25840_write4(client, 0x110, 0x000a030e); 310 + 311 + /* ADC2 input select */ 312 + cx25840_write(client, 0x102, 0x10); 313 + 314 + /* VIN1 & VIN5 */ 315 + cx25840_write(client, 0x103, 0x11); 316 + 317 + /* Enable format auto detect */ 318 + cx25840_write(client, 0x400, 0); 319 + /* Fast subchroma lock */ 320 + /* White crush, Chroma AGC & Chroma Killer enabled */ 321 + cx25840_write(client, 0x401, 0xe8); 322 + 323 + /* Select AFE clock pad output source */ 324 + cx25840_write(client, 0x144, 0x05); 325 + 326 + /* Do the firmware load in a work handler to prevent. 327 + Otherwise the kernel is blocked waiting for the 328 + bit-banging i2c interface to finish uploading the 329 + firmware. */ 330 + INIT_WORK(&state->fw_work, cx25840_work_handler); 331 + init_waitqueue_head(&state->fw_wait); 332 + q = create_singlethread_workqueue("cx25840_fw"); 333 + prepare_to_wait(&state->fw_wait, &wait, TASK_UNINTERRUPTIBLE); 334 + queue_work(q, &state->fw_work); 335 + schedule(); 336 + finish_wait(&state->fw_wait, &wait); 337 + destroy_workqueue(q); 338 + 339 + cx25840_vbi_setup(client); 340 + 341 + /* (re)set input */ 342 + set_input(client, state->vid_input, state->aud_input); 343 + 344 + /* start microcontroller */ 345 + cx25840_and_or(client, 0x803, ~0x10, 0x10); 346 + } 347 + 260 348 /* ----------------------------------------------------------------------- */ 261 349 262 350 static void input_change(struct i2c_client *client) ··· 410 318 vid_input <= CX25840_COMPOSITE8); 411 319 u8 reg; 412 320 413 - v4l_dbg(1, cx25840_debug, client, "decoder set video input %d, audio input %d\n", 414 - vid_input, aud_input); 321 + v4l_dbg(1, cx25840_debug, client, 322 + "decoder set video input %d, audio input %d\n", 323 + vid_input, aud_input); 415 324 325 + if (vid_input >= CX25840_VIN1_CH1) { 326 + v4l_dbg(1, cx25840_debug, client, "vid_input 0x%x\n", 327 + vid_input); 328 + reg = vid_input & 0xff; 329 + if ((vid_input & CX25840_SVIDEO_ON) == CX25840_SVIDEO_ON) 330 + is_composite = 0; 331 + else 332 + is_composite = 1; 333 + 334 + v4l_dbg(1, cx25840_debug, client, "mux cfg 0x%x comp=%d\n", 335 + reg, is_composite); 336 + } else 416 337 if (is_composite) { 417 338 reg = 0xf0 + (vid_input - CX25840_COMPOSITE1); 418 339 } else { ··· 435 330 if ((vid_input & ~0xff0) || 436 331 luma < CX25840_SVIDEO_LUMA1 || luma > CX25840_SVIDEO_LUMA4 || 437 332 chroma < CX25840_SVIDEO_CHROMA4 || chroma > CX25840_SVIDEO_CHROMA8) { 438 - v4l_err(client, "0x%04x is not a valid video input!\n", vid_input); 333 + v4l_err(client, "0x%04x is not a valid video input!\n", 334 + vid_input); 439 335 return -EINVAL; 440 336 } 441 337 reg = 0xf0 + ((luma - CX25840_SVIDEO_LUMA1) >> 4); ··· 449 343 } 450 344 } 451 345 452 - switch (aud_input) { 453 - case CX25840_AUDIO_SERIAL: 454 - /* do nothing, use serial audio input */ 455 - break; 456 - case CX25840_AUDIO4: reg &= ~0x30; break; 457 - case CX25840_AUDIO5: reg &= ~0x30; reg |= 0x10; break; 458 - case CX25840_AUDIO6: reg &= ~0x30; reg |= 0x20; break; 459 - case CX25840_AUDIO7: reg &= ~0xc0; break; 460 - case CX25840_AUDIO8: reg &= ~0xc0; reg |= 0x40; break; 346 + /* The caller has previously prepared the correct routing 347 + * configuration in reg (for the cx23885) so we have no 348 + * need to attempt to flip bits for earlier av decoders. 349 + */ 350 + if (!state->is_cx23885) { 351 + switch (aud_input) { 352 + case CX25840_AUDIO_SERIAL: 353 + /* do nothing, use serial audio input */ 354 + break; 355 + case CX25840_AUDIO4: reg &= ~0x30; break; 356 + case CX25840_AUDIO5: reg &= ~0x30; reg |= 0x10; break; 357 + case CX25840_AUDIO6: reg &= ~0x30; reg |= 0x20; break; 358 + case CX25840_AUDIO7: reg &= ~0xc0; break; 359 + case CX25840_AUDIO8: reg &= ~0xc0; reg |= 0x40; break; 461 360 462 - default: 463 - v4l_err(client, "0x%04x is not a valid audio input!\n", aud_input); 464 - return -EINVAL; 361 + default: 362 + v4l_err(client, "0x%04x is not a valid audio input!\n", 363 + aud_input); 364 + return -EINVAL; 365 + } 465 366 } 466 367 467 368 cx25840_write(client, 0x103, reg); 369 + 468 370 /* Set INPUT_MODE to Composite (0) or S-Video (1) */ 469 371 cx25840_and_or(client, 0x401, ~0x6, is_composite ? 0 : 0x02); 470 - /* Set CH_SEL_ADC2 to 1 if input comes from CH3 */ 471 - cx25840_and_or(client, 0x102, ~0x2, (reg & 0x80) == 0 ? 2 : 0); 472 - /* Set DUAL_MODE_ADC2 to 1 if input comes from both CH2 and CH3 */ 473 - if ((reg & 0xc0) != 0xc0 && (reg & 0x30) != 0x30) 474 - cx25840_and_or(client, 0x102, ~0x4, 4); 475 - else 476 - cx25840_and_or(client, 0x102, ~0x4, 0); 372 + 373 + if (!state->is_cx23885) { 374 + /* Set CH_SEL_ADC2 to 1 if input comes from CH3 */ 375 + cx25840_and_or(client, 0x102, ~0x2, (reg & 0x80) == 0 ? 2 : 0); 376 + /* Set DUAL_MODE_ADC2 to 1 if input comes from both CH2&CH3 */ 377 + if ((reg & 0xc0) != 0xc0 && (reg & 0x30) != 0x30) 378 + cx25840_and_or(client, 0x102, ~0x4, 4); 379 + else 380 + cx25840_and_or(client, 0x102, ~0x4, 0); 381 + } else { 382 + if (is_composite) 383 + /* ADC2 input select channel 2 */ 384 + cx25840_and_or(client, 0x102, ~0x2, 0); 385 + else 386 + /* ADC2 input select channel 3 */ 387 + cx25840_and_or(client, 0x102, ~0x2, 2); 388 + } 477 389 478 390 state->vid_input = vid_input; 479 391 state->aud_input = aud_input; ··· 499 375 cx25840_audio_set_path(client); 500 376 input_change(client); 501 377 } 378 + 379 + if (state->is_cx23885) { 380 + /* Audio channel 1 src : Parallel 1 */ 381 + cx25840_write(client, 0x124, 0x03); 382 + 383 + /* Select AFE clock pad output source */ 384 + cx25840_write(client, 0x144, 0x05); 385 + 386 + /* I2S_IN_CTL: I2S_IN_SONY_MODE, LEFT SAMPLE on WS=1 */ 387 + cx25840_write(client, 0x914, 0xa0); 388 + 389 + /* I2S_OUT_CTL: 390 + * I2S_IN_SONY_MODE, LEFT SAMPLE on WS=1 391 + * I2S_OUT_MASTER_MODE = Master 392 + */ 393 + cx25840_write(client, 0x918, 0xa0); 394 + cx25840_write(client, 0x919, 0x01); 395 + } 396 + 502 397 return 0; 503 398 } 504 399 ··· 996 853 state->is_initialized = 1; 997 854 if (state->is_cx25836) 998 855 cx25836_initialize(client); 856 + else if (state->is_cx23885) 857 + cx23885_initialize(client); 999 858 else 1000 859 cx25840_initialize(client); 1001 860 } ··· 1015 870 return -EINVAL; 1016 871 if (!capable(CAP_SYS_ADMIN)) 1017 872 return -EPERM; 873 + 1018 874 if (cmd == VIDIOC_DBG_G_REGISTER) 1019 875 reg->val = cx25840_read(client, reg->reg & 0x0fff); 1020 876 else ··· 1032 886 1033 887 case VIDIOC_STREAMON: 1034 888 v4l_dbg(1, cx25840_debug, client, "enable output\n"); 1035 - cx25840_write(client, 0x115, state->is_cx25836 ? 0x0c : 0x8c); 1036 - cx25840_write(client, 0x116, state->is_cx25836 ? 0x04 : 0x07); 889 + if (state->is_cx23885) { 890 + u8 v = (cx25840_read(client, 0x421) | 0x0b); 891 + cx25840_write(client, 0x421, v); 892 + } else { 893 + cx25840_write(client, 0x115, 894 + state->is_cx25836 ? 0x0c : 0x8c); 895 + cx25840_write(client, 0x116, 896 + state->is_cx25836 ? 0x04 : 0x07); 897 + } 1037 898 break; 1038 899 1039 900 case VIDIOC_STREAMOFF: 1040 901 v4l_dbg(1, cx25840_debug, client, "disable output\n"); 1041 - cx25840_write(client, 0x115, 0x00); 1042 - cx25840_write(client, 0x116, 0x00); 902 + if (state->is_cx23885) { 903 + u8 v = cx25840_read(client, 0x421) & ~(0x0b); 904 + cx25840_write(client, 0x421, v); 905 + } else { 906 + cx25840_write(client, 0x115, 0x00); 907 + cx25840_write(client, 0x116, 0x00); 908 + } 1043 909 break; 1044 910 1045 911 case VIDIOC_LOG_STATUS: ··· 1214 1056 case VIDIOC_INT_RESET: 1215 1057 if (state->is_cx25836) 1216 1058 cx25836_initialize(client); 1059 + else if (state->is_cx23885) 1060 + cx23885_initialize(client); 1217 1061 else 1218 1062 cx25840_initialize(client); 1219 1063 break; ··· 1246 1086 1247 1087 device_id = cx25840_read(client, 0x101) << 8; 1248 1088 device_id |= cx25840_read(client, 0x100); 1089 + v4l_dbg(1, cx25840_debug, client, "device_id = 0x%04x\n", device_id); 1249 1090 1250 1091 /* The high byte of the device ID should be 1251 1092 * 0x83 for the cx2583x and 0x84 for the cx2584x */ ··· 1255 1094 } 1256 1095 else if ((device_id & 0xff00) == 0x8400) { 1257 1096 id = V4L2_IDENT_CX25840 + ((device_id >> 4) & 0xf); 1097 + } else if (device_id == 0x0000) { 1098 + id = V4L2_IDENT_CX25836 + ((device_id >> 4) & 0xf) - 6; 1099 + } else if (device_id == 0x1313) { 1100 + id = V4L2_IDENT_CX25836 + ((device_id >> 4) & 0xf) - 6; 1258 1101 } 1259 1102 else { 1260 1103 v4l_dbg(1, cx25840_debug, client, "cx25840 not found\n"); ··· 1280 1115 i2c_set_clientdata(client, state); 1281 1116 state->c = client; 1282 1117 state->is_cx25836 = ((device_id & 0xff00) == 0x8300); 1118 + state->is_cx23885 = (device_id == 0x0000) || (device_id == 0x1313); 1283 1119 state->vid_input = CX25840_COMPOSITE7; 1284 1120 state->aud_input = CX25840_AUDIO8; 1285 1121 state->audclk_freq = 48000; ··· 1290 1124 state->vbi_line_offset = 8; 1291 1125 state->id = id; 1292 1126 state->rev = device_id; 1127 + 1293 1128 return 0; 1294 1129 } 1295 1130
+1
drivers/media/video/cx25840/cx25840-core.h
··· 47 47 u32 id; 48 48 u32 rev; 49 49 int is_cx25836; 50 + int is_cx23885; 50 51 int is_initialized; 51 52 wait_queue_head_t fw_wait; /* wake up when the fw load is finished */ 52 53 struct work_struct fw_work; /* work entry for fw load */
+5
drivers/media/video/cx25840/cx25840-firmware.c
··· 24 24 #include "cx25840-core.h" 25 25 26 26 #define FWFILE "v4l-cx25840.fw" 27 + #define FWFILE_CX23885 "v4l-cx23885-avcore-01.fw" 27 28 28 29 /* 29 30 * Mike Isely <isely@pobox.com> - The FWSEND parameter controls the ··· 93 92 94 93 int cx25840_loadfw(struct i2c_client *client) 95 94 { 95 + struct cx25840_state *state = i2c_get_clientdata(client); 96 96 const struct firmware *fw = NULL; 97 97 u8 buffer[4], *ptr; 98 98 int size, send, retval; 99 + 100 + if (state->is_cx23885) 101 + firmware = FWFILE_CX23885; 99 102 100 103 if (request_firmware(&fw, firmware, FWDEV(client)) != 0) { 101 104 v4l_err(client, "unable to open firmware %s\n", firmware);
+19
include/media/cx25840.h
··· 49 49 CX25840_SVIDEO2 = 0x620, 50 50 CX25840_SVIDEO3 = 0x730, 51 51 CX25840_SVIDEO4 = 0x840, 52 + 53 + /* Allow frames to specify specific input configurations */ 54 + CX25840_VIN1_CH1 = 0x80000000, 55 + CX25840_VIN2_CH1 = 0x80000001, 56 + CX25840_VIN3_CH1 = 0x80000002, 57 + CX25840_VIN4_CH1 = 0x80000003, 58 + CX25840_VIN5_CH1 = 0x80000004, 59 + CX25840_VIN6_CH1 = 0x80000005, 60 + CX25840_VIN7_CH1 = 0x80000006, 61 + CX25840_VIN8_CH1 = 0x80000007, 62 + CX25840_VIN4_CH2 = 0x80000000, 63 + CX25840_VIN5_CH2 = 0x80000010, 64 + CX25840_VIN6_CH2 = 0x80000020, 65 + CX25840_NONE_CH2 = 0x80000030, 66 + CX25840_VIN7_CH3 = 0x80000000, 67 + CX25840_VIN8_CH3 = 0x80000040, 68 + CX25840_NONE0_CH3 = 0x80000080, 69 + CX25840_NONE1_CH3 = 0x800000c0, 70 + CX25840_SVIDEO_ON = 0x80000100, 52 71 }; 53 72 54 73 enum cx25840_audio_input {