10c0d06caSMauro Carvalho Chehab /* 20c0d06caSMauro Carvalho Chehab * 30c0d06caSMauro Carvalho Chehab * 40c0d06caSMauro Carvalho Chehab * Copyright (C) 2005 Mike Isely <isely@pobox.com> 50c0d06caSMauro Carvalho Chehab * Copyright (C) 2004 Aurelien Alleaume <slts@free.fr> 60c0d06caSMauro Carvalho Chehab * 70c0d06caSMauro Carvalho Chehab * This program is free software; you can redistribute it and/or modify 80c0d06caSMauro Carvalho Chehab * it under the terms of the GNU General Public License as published by 90c0d06caSMauro Carvalho Chehab * the Free Software Foundation; either version 2 of the License 100c0d06caSMauro Carvalho Chehab * 110c0d06caSMauro Carvalho Chehab * This program is distributed in the hope that it will be useful, 120c0d06caSMauro Carvalho Chehab * but WITHOUT ANY WARRANTY; without even the implied warranty of 130c0d06caSMauro Carvalho Chehab * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 140c0d06caSMauro Carvalho Chehab * GNU General Public License for more details. 150c0d06caSMauro Carvalho Chehab * 160c0d06caSMauro Carvalho Chehab */ 170c0d06caSMauro Carvalho Chehab 180c0d06caSMauro Carvalho Chehab #include <linux/device.h> // for linux/firmware.h 190c0d06caSMauro Carvalho Chehab #include <linux/firmware.h> 200c0d06caSMauro Carvalho Chehab #include "pvrusb2-util.h" 210c0d06caSMauro Carvalho Chehab #include "pvrusb2-encoder.h" 220c0d06caSMauro Carvalho Chehab #include "pvrusb2-hdw-internal.h" 230c0d06caSMauro Carvalho Chehab #include "pvrusb2-debug.h" 240c0d06caSMauro Carvalho Chehab #include "pvrusb2-fx2-cmd.h" 250c0d06caSMauro Carvalho Chehab 260c0d06caSMauro Carvalho Chehab 270c0d06caSMauro Carvalho Chehab 280c0d06caSMauro Carvalho Chehab /* Firmware mailbox flags - definitions found from ivtv */ 290c0d06caSMauro Carvalho Chehab #define IVTV_MBOX_FIRMWARE_DONE 0x00000004 300c0d06caSMauro Carvalho Chehab #define IVTV_MBOX_DRIVER_DONE 0x00000002 310c0d06caSMauro Carvalho Chehab #define IVTV_MBOX_DRIVER_BUSY 0x00000001 320c0d06caSMauro Carvalho Chehab 330c0d06caSMauro Carvalho Chehab #define MBOX_BASE 0x44 340c0d06caSMauro Carvalho Chehab 350c0d06caSMauro Carvalho Chehab 360c0d06caSMauro Carvalho Chehab static int pvr2_encoder_write_words(struct pvr2_hdw *hdw, 370c0d06caSMauro Carvalho Chehab unsigned int offs, 380c0d06caSMauro Carvalho Chehab const u32 *data, unsigned int dlen) 390c0d06caSMauro Carvalho Chehab { 400c0d06caSMauro Carvalho Chehab unsigned int idx,addr; 410c0d06caSMauro Carvalho Chehab unsigned int bAddr; 420c0d06caSMauro Carvalho Chehab int ret; 430c0d06caSMauro Carvalho Chehab unsigned int chunkCnt; 440c0d06caSMauro Carvalho Chehab 450c0d06caSMauro Carvalho Chehab /* 460c0d06caSMauro Carvalho Chehab 470c0d06caSMauro Carvalho Chehab Format: First byte must be 0x01. Remaining 32 bit words are 480c0d06caSMauro Carvalho Chehab spread out into chunks of 7 bytes each, with the first 4 bytes 490c0d06caSMauro Carvalho Chehab being the data word (little endian), and the next 3 bytes 500c0d06caSMauro Carvalho Chehab being the address where that data word is to be written (big 510c0d06caSMauro Carvalho Chehab endian). Repeat request for additional words, with offset 520c0d06caSMauro Carvalho Chehab adjusted accordingly. 530c0d06caSMauro Carvalho Chehab 540c0d06caSMauro Carvalho Chehab */ 550c0d06caSMauro Carvalho Chehab while (dlen) { 560c0d06caSMauro Carvalho Chehab chunkCnt = 8; 570c0d06caSMauro Carvalho Chehab if (chunkCnt > dlen) chunkCnt = dlen; 580c0d06caSMauro Carvalho Chehab memset(hdw->cmd_buffer,0,sizeof(hdw->cmd_buffer)); 590c0d06caSMauro Carvalho Chehab bAddr = 0; 600c0d06caSMauro Carvalho Chehab hdw->cmd_buffer[bAddr++] = FX2CMD_MEM_WRITE_DWORD; 610c0d06caSMauro Carvalho Chehab for (idx = 0; idx < chunkCnt; idx++) { 620c0d06caSMauro Carvalho Chehab addr = idx + offs; 630c0d06caSMauro Carvalho Chehab hdw->cmd_buffer[bAddr+6] = (addr & 0xffu); 640c0d06caSMauro Carvalho Chehab hdw->cmd_buffer[bAddr+5] = ((addr>>8) & 0xffu); 650c0d06caSMauro Carvalho Chehab hdw->cmd_buffer[bAddr+4] = ((addr>>16) & 0xffu); 660c0d06caSMauro Carvalho Chehab PVR2_DECOMPOSE_LE(hdw->cmd_buffer, bAddr,data[idx]); 670c0d06caSMauro Carvalho Chehab bAddr += 7; 680c0d06caSMauro Carvalho Chehab } 690c0d06caSMauro Carvalho Chehab ret = pvr2_send_request(hdw, 700c0d06caSMauro Carvalho Chehab hdw->cmd_buffer,1+(chunkCnt*7), 710c0d06caSMauro Carvalho Chehab NULL,0); 720c0d06caSMauro Carvalho Chehab if (ret) return ret; 730c0d06caSMauro Carvalho Chehab data += chunkCnt; 740c0d06caSMauro Carvalho Chehab dlen -= chunkCnt; 750c0d06caSMauro Carvalho Chehab offs += chunkCnt; 760c0d06caSMauro Carvalho Chehab } 770c0d06caSMauro Carvalho Chehab 780c0d06caSMauro Carvalho Chehab return 0; 790c0d06caSMauro Carvalho Chehab } 800c0d06caSMauro Carvalho Chehab 810c0d06caSMauro Carvalho Chehab 820c0d06caSMauro Carvalho Chehab static int pvr2_encoder_read_words(struct pvr2_hdw *hdw, 830c0d06caSMauro Carvalho Chehab unsigned int offs, 840c0d06caSMauro Carvalho Chehab u32 *data, unsigned int dlen) 850c0d06caSMauro Carvalho Chehab { 860c0d06caSMauro Carvalho Chehab unsigned int idx; 870c0d06caSMauro Carvalho Chehab int ret; 880c0d06caSMauro Carvalho Chehab unsigned int chunkCnt; 890c0d06caSMauro Carvalho Chehab 900c0d06caSMauro Carvalho Chehab /* 910c0d06caSMauro Carvalho Chehab 920c0d06caSMauro Carvalho Chehab Format: First byte must be 0x02 (status check) or 0x28 (read 930c0d06caSMauro Carvalho Chehab back block of 32 bit words). Next 6 bytes must be zero, 940c0d06caSMauro Carvalho Chehab followed by a single byte of MBOX_BASE+offset for portion to 950c0d06caSMauro Carvalho Chehab be read. Returned data is packed set of 32 bits words that 960c0d06caSMauro Carvalho Chehab were read. 970c0d06caSMauro Carvalho Chehab 980c0d06caSMauro Carvalho Chehab */ 990c0d06caSMauro Carvalho Chehab 1000c0d06caSMauro Carvalho Chehab while (dlen) { 1010c0d06caSMauro Carvalho Chehab chunkCnt = 16; 1020c0d06caSMauro Carvalho Chehab if (chunkCnt > dlen) chunkCnt = dlen; 1030c0d06caSMauro Carvalho Chehab if (chunkCnt < 16) chunkCnt = 1; 1040c0d06caSMauro Carvalho Chehab hdw->cmd_buffer[0] = 1050c0d06caSMauro Carvalho Chehab ((chunkCnt == 1) ? 1060c0d06caSMauro Carvalho Chehab FX2CMD_MEM_READ_DWORD : FX2CMD_MEM_READ_64BYTES); 1070c0d06caSMauro Carvalho Chehab hdw->cmd_buffer[1] = 0; 1080c0d06caSMauro Carvalho Chehab hdw->cmd_buffer[2] = 0; 1090c0d06caSMauro Carvalho Chehab hdw->cmd_buffer[3] = 0; 1100c0d06caSMauro Carvalho Chehab hdw->cmd_buffer[4] = 0; 1110c0d06caSMauro Carvalho Chehab hdw->cmd_buffer[5] = ((offs>>16) & 0xffu); 1120c0d06caSMauro Carvalho Chehab hdw->cmd_buffer[6] = ((offs>>8) & 0xffu); 1130c0d06caSMauro Carvalho Chehab hdw->cmd_buffer[7] = (offs & 0xffu); 1140c0d06caSMauro Carvalho Chehab ret = pvr2_send_request(hdw, 1150c0d06caSMauro Carvalho Chehab hdw->cmd_buffer,8, 1160c0d06caSMauro Carvalho Chehab hdw->cmd_buffer, 1170c0d06caSMauro Carvalho Chehab (chunkCnt == 1 ? 4 : 16 * 4)); 1180c0d06caSMauro Carvalho Chehab if (ret) return ret; 1190c0d06caSMauro Carvalho Chehab 1200c0d06caSMauro Carvalho Chehab for (idx = 0; idx < chunkCnt; idx++) { 1210c0d06caSMauro Carvalho Chehab data[idx] = PVR2_COMPOSE_LE(hdw->cmd_buffer,idx*4); 1220c0d06caSMauro Carvalho Chehab } 1230c0d06caSMauro Carvalho Chehab data += chunkCnt; 1240c0d06caSMauro Carvalho Chehab dlen -= chunkCnt; 1250c0d06caSMauro Carvalho Chehab offs += chunkCnt; 1260c0d06caSMauro Carvalho Chehab } 1270c0d06caSMauro Carvalho Chehab 1280c0d06caSMauro Carvalho Chehab return 0; 1290c0d06caSMauro Carvalho Chehab } 1300c0d06caSMauro Carvalho Chehab 1310c0d06caSMauro Carvalho Chehab 1320c0d06caSMauro Carvalho Chehab /* This prototype is set up to be compatible with the 1330c0d06caSMauro Carvalho Chehab cx2341x_mbox_func prototype in cx2341x.h, which should be in 1340c0d06caSMauro Carvalho Chehab kernels 2.6.18 or later. We do this so that we can enable 1350c0d06caSMauro Carvalho Chehab cx2341x.ko to write to our encoder (by handing it a pointer to this 1360c0d06caSMauro Carvalho Chehab function). For earlier kernels this doesn't really matter. */ 1370c0d06caSMauro Carvalho Chehab static int pvr2_encoder_cmd(void *ctxt, 1380c0d06caSMauro Carvalho Chehab u32 cmd, 1390c0d06caSMauro Carvalho Chehab int arg_cnt_send, 1400c0d06caSMauro Carvalho Chehab int arg_cnt_recv, 1410c0d06caSMauro Carvalho Chehab u32 *argp) 1420c0d06caSMauro Carvalho Chehab { 1430c0d06caSMauro Carvalho Chehab unsigned int poll_count; 1440c0d06caSMauro Carvalho Chehab unsigned int try_count = 0; 1450c0d06caSMauro Carvalho Chehab int retry_flag; 1460c0d06caSMauro Carvalho Chehab int ret = 0; 1470c0d06caSMauro Carvalho Chehab unsigned int idx; 1480c0d06caSMauro Carvalho Chehab /* These sizes look to be limited by the FX2 firmware implementation */ 1490c0d06caSMauro Carvalho Chehab u32 wrData[16]; 1500c0d06caSMauro Carvalho Chehab u32 rdData[16]; 1510c0d06caSMauro Carvalho Chehab struct pvr2_hdw *hdw = (struct pvr2_hdw *)ctxt; 1520c0d06caSMauro Carvalho Chehab 1530c0d06caSMauro Carvalho Chehab 1540c0d06caSMauro Carvalho Chehab /* 1550c0d06caSMauro Carvalho Chehab 1560c0d06caSMauro Carvalho Chehab The encoder seems to speak entirely using blocks 32 bit words. 1570c0d06caSMauro Carvalho Chehab In ivtv driver terms, this is a mailbox at MBOX_BASE which we 1580c0d06caSMauro Carvalho Chehab populate with data and watch what the hardware does with it. 1590c0d06caSMauro Carvalho Chehab The first word is a set of flags used to control the 1600c0d06caSMauro Carvalho Chehab transaction, the second word is the command to execute, the 1610c0d06caSMauro Carvalho Chehab third byte is zero (ivtv driver suggests that this is some 1620c0d06caSMauro Carvalho Chehab kind of return value), and the fourth byte is a specified 1630c0d06caSMauro Carvalho Chehab timeout (windows driver always uses 0x00060000 except for one 1640c0d06caSMauro Carvalho Chehab case when it is zero). All successive words are the argument 1650c0d06caSMauro Carvalho Chehab words for the command. 1660c0d06caSMauro Carvalho Chehab 1670c0d06caSMauro Carvalho Chehab First, write out the entire set of words, with the first word 1680c0d06caSMauro Carvalho Chehab being zero. 1690c0d06caSMauro Carvalho Chehab 1700c0d06caSMauro Carvalho Chehab Next, write out just the first word again, but set it to 1710c0d06caSMauro Carvalho Chehab IVTV_MBOX_DRIVER_DONE | IVTV_DRIVER_BUSY this time (which 1720c0d06caSMauro Carvalho Chehab probably means "go"). 1730c0d06caSMauro Carvalho Chehab 1740c0d06caSMauro Carvalho Chehab Next, read back the return count words. Check the first word, 1750c0d06caSMauro Carvalho Chehab which should have IVTV_MBOX_FIRMWARE_DONE set. If however 1760c0d06caSMauro Carvalho Chehab that bit is not set, then the command isn't done so repeat the 1770c0d06caSMauro Carvalho Chehab read until it is set. 1780c0d06caSMauro Carvalho Chehab 1790c0d06caSMauro Carvalho Chehab Finally, write out just the first word again, but set it to 1800c0d06caSMauro Carvalho Chehab 0x0 this time (which probably means "idle"). 1810c0d06caSMauro Carvalho Chehab 1820c0d06caSMauro Carvalho Chehab */ 1830c0d06caSMauro Carvalho Chehab 1840c0d06caSMauro Carvalho Chehab if (arg_cnt_send > (ARRAY_SIZE(wrData) - 4)) { 1850c0d06caSMauro Carvalho Chehab pvr2_trace( 1860c0d06caSMauro Carvalho Chehab PVR2_TRACE_ERROR_LEGS, 18796292c89SMauro Carvalho Chehab "Failed to write cx23416 command - too many input arguments (was given %u limit %lu)", 1880c0d06caSMauro Carvalho Chehab arg_cnt_send, (long unsigned) ARRAY_SIZE(wrData) - 4); 1890c0d06caSMauro Carvalho Chehab return -EINVAL; 1900c0d06caSMauro Carvalho Chehab } 1910c0d06caSMauro Carvalho Chehab 1920c0d06caSMauro Carvalho Chehab if (arg_cnt_recv > (ARRAY_SIZE(rdData) - 4)) { 1930c0d06caSMauro Carvalho Chehab pvr2_trace( 1940c0d06caSMauro Carvalho Chehab PVR2_TRACE_ERROR_LEGS, 19596292c89SMauro Carvalho Chehab "Failed to write cx23416 command - too many return arguments (was given %u limit %lu)", 1960c0d06caSMauro Carvalho Chehab arg_cnt_recv, (long unsigned) ARRAY_SIZE(rdData) - 4); 1970c0d06caSMauro Carvalho Chehab return -EINVAL; 1980c0d06caSMauro Carvalho Chehab } 1990c0d06caSMauro Carvalho Chehab 2000c0d06caSMauro Carvalho Chehab 201*1ad371deSMauro Carvalho Chehab LOCK_TAKE(hdw->ctl_lock); while (1) { 2020c0d06caSMauro Carvalho Chehab 2030c0d06caSMauro Carvalho Chehab if (!hdw->state_encoder_ok) { 2040c0d06caSMauro Carvalho Chehab ret = -EIO; 2050c0d06caSMauro Carvalho Chehab break; 2060c0d06caSMauro Carvalho Chehab } 2070c0d06caSMauro Carvalho Chehab 2080c0d06caSMauro Carvalho Chehab retry_flag = 0; 2090c0d06caSMauro Carvalho Chehab try_count++; 2100c0d06caSMauro Carvalho Chehab ret = 0; 2110c0d06caSMauro Carvalho Chehab wrData[0] = 0; 2120c0d06caSMauro Carvalho Chehab wrData[1] = cmd; 2130c0d06caSMauro Carvalho Chehab wrData[2] = 0; 2140c0d06caSMauro Carvalho Chehab wrData[3] = 0x00060000; 2150c0d06caSMauro Carvalho Chehab for (idx = 0; idx < arg_cnt_send; idx++) { 2160c0d06caSMauro Carvalho Chehab wrData[idx+4] = argp[idx]; 2170c0d06caSMauro Carvalho Chehab } 2180c0d06caSMauro Carvalho Chehab for (; idx < ARRAY_SIZE(wrData) - 4; idx++) { 2190c0d06caSMauro Carvalho Chehab wrData[idx+4] = 0; 2200c0d06caSMauro Carvalho Chehab } 2210c0d06caSMauro Carvalho Chehab 2220c0d06caSMauro Carvalho Chehab ret = pvr2_encoder_write_words(hdw,MBOX_BASE,wrData,idx); 2230c0d06caSMauro Carvalho Chehab if (ret) break; 2240c0d06caSMauro Carvalho Chehab wrData[0] = IVTV_MBOX_DRIVER_DONE|IVTV_MBOX_DRIVER_BUSY; 2250c0d06caSMauro Carvalho Chehab ret = pvr2_encoder_write_words(hdw,MBOX_BASE,wrData,1); 2260c0d06caSMauro Carvalho Chehab if (ret) break; 2270c0d06caSMauro Carvalho Chehab poll_count = 0; 2280c0d06caSMauro Carvalho Chehab while (1) { 2290c0d06caSMauro Carvalho Chehab poll_count++; 2300c0d06caSMauro Carvalho Chehab ret = pvr2_encoder_read_words(hdw,MBOX_BASE,rdData, 2310c0d06caSMauro Carvalho Chehab arg_cnt_recv+4); 2320c0d06caSMauro Carvalho Chehab if (ret) { 2330c0d06caSMauro Carvalho Chehab break; 2340c0d06caSMauro Carvalho Chehab } 2350c0d06caSMauro Carvalho Chehab if (rdData[0] & IVTV_MBOX_FIRMWARE_DONE) { 2360c0d06caSMauro Carvalho Chehab break; 2370c0d06caSMauro Carvalho Chehab } 2380c0d06caSMauro Carvalho Chehab if (rdData[0] && (poll_count < 1000)) continue; 2390c0d06caSMauro Carvalho Chehab if (!rdData[0]) { 2400c0d06caSMauro Carvalho Chehab retry_flag = !0; 2410c0d06caSMauro Carvalho Chehab pvr2_trace( 2420c0d06caSMauro Carvalho Chehab PVR2_TRACE_ERROR_LEGS, 24396292c89SMauro Carvalho Chehab "Encoder timed out waiting for us; arranging to retry"); 2440c0d06caSMauro Carvalho Chehab } else { 2450c0d06caSMauro Carvalho Chehab pvr2_trace( 2460c0d06caSMauro Carvalho Chehab PVR2_TRACE_ERROR_LEGS, 24796292c89SMauro Carvalho Chehab "***WARNING*** device's encoder appears to be stuck (status=0x%08x)", 24896292c89SMauro Carvalho Chehab rdData[0]); 2490c0d06caSMauro Carvalho Chehab } 2500c0d06caSMauro Carvalho Chehab pvr2_trace( 2510c0d06caSMauro Carvalho Chehab PVR2_TRACE_ERROR_LEGS, 2520c0d06caSMauro Carvalho Chehab "Encoder command: 0x%02x",cmd); 2530c0d06caSMauro Carvalho Chehab for (idx = 4; idx < arg_cnt_send; idx++) { 2540c0d06caSMauro Carvalho Chehab pvr2_trace( 2550c0d06caSMauro Carvalho Chehab PVR2_TRACE_ERROR_LEGS, 2560c0d06caSMauro Carvalho Chehab "Encoder arg%d: 0x%08x", 2570c0d06caSMauro Carvalho Chehab idx-3,wrData[idx]); 2580c0d06caSMauro Carvalho Chehab } 2590c0d06caSMauro Carvalho Chehab ret = -EBUSY; 2600c0d06caSMauro Carvalho Chehab break; 2610c0d06caSMauro Carvalho Chehab } 2620c0d06caSMauro Carvalho Chehab if (retry_flag) { 2630c0d06caSMauro Carvalho Chehab if (try_count < 20) continue; 2640c0d06caSMauro Carvalho Chehab pvr2_trace( 2650c0d06caSMauro Carvalho Chehab PVR2_TRACE_ERROR_LEGS, 2660c0d06caSMauro Carvalho Chehab "Too many retries..."); 2670c0d06caSMauro Carvalho Chehab ret = -EBUSY; 2680c0d06caSMauro Carvalho Chehab } 2690c0d06caSMauro Carvalho Chehab if (ret) { 2700c0d06caSMauro Carvalho Chehab del_timer_sync(&hdw->encoder_run_timer); 2710c0d06caSMauro Carvalho Chehab hdw->state_encoder_ok = 0; 2720c0d06caSMauro Carvalho Chehab pvr2_trace(PVR2_TRACE_STBITS, 2730c0d06caSMauro Carvalho Chehab "State bit %s <-- %s", 2740c0d06caSMauro Carvalho Chehab "state_encoder_ok", 2750c0d06caSMauro Carvalho Chehab (hdw->state_encoder_ok ? "true" : "false")); 2760c0d06caSMauro Carvalho Chehab if (hdw->state_encoder_runok) { 2770c0d06caSMauro Carvalho Chehab hdw->state_encoder_runok = 0; 2780c0d06caSMauro Carvalho Chehab pvr2_trace(PVR2_TRACE_STBITS, 2790c0d06caSMauro Carvalho Chehab "State bit %s <-- %s", 2800c0d06caSMauro Carvalho Chehab "state_encoder_runok", 2810c0d06caSMauro Carvalho Chehab (hdw->state_encoder_runok ? 2820c0d06caSMauro Carvalho Chehab "true" : "false")); 2830c0d06caSMauro Carvalho Chehab } 2840c0d06caSMauro Carvalho Chehab pvr2_trace( 2850c0d06caSMauro Carvalho Chehab PVR2_TRACE_ERROR_LEGS, 28696292c89SMauro Carvalho Chehab "Giving up on command. This is normally recovered via a firmware reload and re-initialization; concern is only warranted if this happens repeatedly and rapidly."); 2870c0d06caSMauro Carvalho Chehab break; 2880c0d06caSMauro Carvalho Chehab } 2890c0d06caSMauro Carvalho Chehab wrData[0] = 0x7; 2900c0d06caSMauro Carvalho Chehab for (idx = 0; idx < arg_cnt_recv; idx++) { 2910c0d06caSMauro Carvalho Chehab argp[idx] = rdData[idx+4]; 2920c0d06caSMauro Carvalho Chehab } 2930c0d06caSMauro Carvalho Chehab 2940c0d06caSMauro Carvalho Chehab wrData[0] = 0x0; 2950c0d06caSMauro Carvalho Chehab ret = pvr2_encoder_write_words(hdw,MBOX_BASE,wrData,1); 296*1ad371deSMauro Carvalho Chehab break; 2970c0d06caSMauro Carvalho Chehab 298*1ad371deSMauro Carvalho Chehab }; LOCK_GIVE(hdw->ctl_lock); 2990c0d06caSMauro Carvalho Chehab 3000c0d06caSMauro Carvalho Chehab return ret; 3010c0d06caSMauro Carvalho Chehab } 3020c0d06caSMauro Carvalho Chehab 3030c0d06caSMauro Carvalho Chehab 3040c0d06caSMauro Carvalho Chehab static int pvr2_encoder_vcmd(struct pvr2_hdw *hdw, int cmd, 3050c0d06caSMauro Carvalho Chehab int args, ...) 3060c0d06caSMauro Carvalho Chehab { 3070c0d06caSMauro Carvalho Chehab va_list vl; 3080c0d06caSMauro Carvalho Chehab unsigned int idx; 3090c0d06caSMauro Carvalho Chehab u32 data[12]; 3100c0d06caSMauro Carvalho Chehab 3110c0d06caSMauro Carvalho Chehab if (args > ARRAY_SIZE(data)) { 3120c0d06caSMauro Carvalho Chehab pvr2_trace( 3130c0d06caSMauro Carvalho Chehab PVR2_TRACE_ERROR_LEGS, 31496292c89SMauro Carvalho Chehab "Failed to write cx23416 command - too many arguments (was given %u limit %lu)", 3150c0d06caSMauro Carvalho Chehab args, (long unsigned) ARRAY_SIZE(data)); 3160c0d06caSMauro Carvalho Chehab return -EINVAL; 3170c0d06caSMauro Carvalho Chehab } 3180c0d06caSMauro Carvalho Chehab 3190c0d06caSMauro Carvalho Chehab va_start(vl, args); 3200c0d06caSMauro Carvalho Chehab for (idx = 0; idx < args; idx++) { 3210c0d06caSMauro Carvalho Chehab data[idx] = va_arg(vl, u32); 3220c0d06caSMauro Carvalho Chehab } 3230c0d06caSMauro Carvalho Chehab va_end(vl); 3240c0d06caSMauro Carvalho Chehab 3250c0d06caSMauro Carvalho Chehab return pvr2_encoder_cmd(hdw,cmd,args,0,data); 3260c0d06caSMauro Carvalho Chehab } 3270c0d06caSMauro Carvalho Chehab 3280c0d06caSMauro Carvalho Chehab 3290c0d06caSMauro Carvalho Chehab /* This implements some extra setup for the encoder that seems to be 3300c0d06caSMauro Carvalho Chehab specific to the PVR USB2 hardware. */ 3310c0d06caSMauro Carvalho Chehab static int pvr2_encoder_prep_config(struct pvr2_hdw *hdw) 3320c0d06caSMauro Carvalho Chehab { 3330c0d06caSMauro Carvalho Chehab int ret = 0; 3340c0d06caSMauro Carvalho Chehab int encMisc3Arg = 0; 3350c0d06caSMauro Carvalho Chehab 3360c0d06caSMauro Carvalho Chehab #if 0 3370c0d06caSMauro Carvalho Chehab /* This inexplicable bit happens in the Hauppauge windows 3380c0d06caSMauro Carvalho Chehab driver (for both 24xxx and 29xxx devices). However I 3390c0d06caSMauro Carvalho Chehab currently see no difference in behavior with or without 3400c0d06caSMauro Carvalho Chehab this stuff. Leave this here as a note of its existence, 3410c0d06caSMauro Carvalho Chehab but don't use it. */ 3420c0d06caSMauro Carvalho Chehab LOCK_TAKE(hdw->ctl_lock); do { 3430c0d06caSMauro Carvalho Chehab u32 dat[1]; 3440c0d06caSMauro Carvalho Chehab dat[0] = 0x80000640; 3450c0d06caSMauro Carvalho Chehab pvr2_encoder_write_words(hdw,0x01fe,dat,1); 3460c0d06caSMauro Carvalho Chehab pvr2_encoder_write_words(hdw,0x023e,dat,1); 3470c0d06caSMauro Carvalho Chehab } while(0); LOCK_GIVE(hdw->ctl_lock); 3480c0d06caSMauro Carvalho Chehab #endif 3490c0d06caSMauro Carvalho Chehab 3500c0d06caSMauro Carvalho Chehab /* Mike Isely <isely@pobox.com> 26-Jan-2006 The windows driver 3510c0d06caSMauro Carvalho Chehab sends the following list of ENC_MISC commands (for both 3520c0d06caSMauro Carvalho Chehab 24xxx and 29xxx devices). Meanings are not entirely clear, 3530c0d06caSMauro Carvalho Chehab however without the ENC_MISC(3,1) command then we risk 3540c0d06caSMauro Carvalho Chehab random perpetual video corruption whenever the video input 3550c0d06caSMauro Carvalho Chehab breaks up for a moment (like when switching channels). */ 3560c0d06caSMauro Carvalho Chehab 3570c0d06caSMauro Carvalho Chehab 3580c0d06caSMauro Carvalho Chehab #if 0 3590c0d06caSMauro Carvalho Chehab /* This ENC_MISC(5,0) command seems to hurt 29xxx sync 3600c0d06caSMauro Carvalho Chehab performance on channel changes, but is not a problem on 3610c0d06caSMauro Carvalho Chehab 24xxx devices. */ 3620c0d06caSMauro Carvalho Chehab ret |= pvr2_encoder_vcmd(hdw, CX2341X_ENC_MISC,4, 5,0,0,0); 3630c0d06caSMauro Carvalho Chehab #endif 3640c0d06caSMauro Carvalho Chehab 3650c0d06caSMauro Carvalho Chehab /* This ENC_MISC(3,encMisc3Arg) command is critical - without 3660c0d06caSMauro Carvalho Chehab it there will eventually be video corruption. Also, the 3670c0d06caSMauro Carvalho Chehab saa7115 case is strange - the Windows driver is passing 1 3680c0d06caSMauro Carvalho Chehab regardless of device type but if we have 1 for saa7115 3690c0d06caSMauro Carvalho Chehab devices the video turns sluggish. */ 3700c0d06caSMauro Carvalho Chehab if (hdw->hdw_desc->flag_has_cx25840) { 3710c0d06caSMauro Carvalho Chehab encMisc3Arg = 1; 3720c0d06caSMauro Carvalho Chehab } else { 3730c0d06caSMauro Carvalho Chehab encMisc3Arg = 0; 3740c0d06caSMauro Carvalho Chehab } 3750c0d06caSMauro Carvalho Chehab ret |= pvr2_encoder_vcmd(hdw, CX2341X_ENC_MISC,4, 3, 3760c0d06caSMauro Carvalho Chehab encMisc3Arg,0,0); 3770c0d06caSMauro Carvalho Chehab 3780c0d06caSMauro Carvalho Chehab ret |= pvr2_encoder_vcmd(hdw, CX2341X_ENC_MISC,4, 8,0,0,0); 3790c0d06caSMauro Carvalho Chehab 3800c0d06caSMauro Carvalho Chehab #if 0 3810c0d06caSMauro Carvalho Chehab /* This ENC_MISC(4,1) command is poisonous, so it is commented 3820c0d06caSMauro Carvalho Chehab out. But I'm leaving it here anyway to document its 3830c0d06caSMauro Carvalho Chehab existence in the Windows driver. The effect of this 3840c0d06caSMauro Carvalho Chehab command is that apps displaying the stream become sluggish 3850c0d06caSMauro Carvalho Chehab with stuttering video. */ 3860c0d06caSMauro Carvalho Chehab ret |= pvr2_encoder_vcmd(hdw, CX2341X_ENC_MISC,4, 4,1,0,0); 3870c0d06caSMauro Carvalho Chehab #endif 3880c0d06caSMauro Carvalho Chehab 3890c0d06caSMauro Carvalho Chehab ret |= pvr2_encoder_vcmd(hdw, CX2341X_ENC_MISC,4, 0,3,0,0); 3900c0d06caSMauro Carvalho Chehab ret |= pvr2_encoder_vcmd(hdw, CX2341X_ENC_MISC,4,15,0,0,0); 3910c0d06caSMauro Carvalho Chehab 3920c0d06caSMauro Carvalho Chehab /* prevent the PTSs from slowly drifting away in the generated 3930c0d06caSMauro Carvalho Chehab MPEG stream */ 3940c0d06caSMauro Carvalho Chehab ret |= pvr2_encoder_vcmd(hdw, CX2341X_ENC_MISC, 2, 4, 1); 3950c0d06caSMauro Carvalho Chehab 3960c0d06caSMauro Carvalho Chehab return ret; 3970c0d06caSMauro Carvalho Chehab } 3980c0d06caSMauro Carvalho Chehab 3990c0d06caSMauro Carvalho Chehab int pvr2_encoder_adjust(struct pvr2_hdw *hdw) 4000c0d06caSMauro Carvalho Chehab { 4010c0d06caSMauro Carvalho Chehab int ret; 4020c0d06caSMauro Carvalho Chehab ret = cx2341x_update(hdw,pvr2_encoder_cmd, 4030c0d06caSMauro Carvalho Chehab (hdw->enc_cur_valid ? &hdw->enc_cur_state : NULL), 4040c0d06caSMauro Carvalho Chehab &hdw->enc_ctl_state); 4050c0d06caSMauro Carvalho Chehab if (ret) { 4060c0d06caSMauro Carvalho Chehab pvr2_trace(PVR2_TRACE_ERROR_LEGS, 4070c0d06caSMauro Carvalho Chehab "Error from cx2341x module code=%d",ret); 4080c0d06caSMauro Carvalho Chehab } else { 4095338c169SEzequiel Garcia hdw->enc_cur_state = hdw->enc_ctl_state; 4100c0d06caSMauro Carvalho Chehab hdw->enc_cur_valid = !0; 4110c0d06caSMauro Carvalho Chehab } 4120c0d06caSMauro Carvalho Chehab return ret; 4130c0d06caSMauro Carvalho Chehab } 4140c0d06caSMauro Carvalho Chehab 4150c0d06caSMauro Carvalho Chehab 4160c0d06caSMauro Carvalho Chehab int pvr2_encoder_configure(struct pvr2_hdw *hdw) 4170c0d06caSMauro Carvalho Chehab { 4180c0d06caSMauro Carvalho Chehab int ret; 4190c0d06caSMauro Carvalho Chehab int val; 42096292c89SMauro Carvalho Chehab pvr2_trace(PVR2_TRACE_ENCODER, "pvr2_encoder_configure (cx2341x module)"); 4210c0d06caSMauro Carvalho Chehab hdw->enc_ctl_state.port = CX2341X_PORT_STREAMING; 4220c0d06caSMauro Carvalho Chehab hdw->enc_ctl_state.width = hdw->res_hor_val; 4230c0d06caSMauro Carvalho Chehab hdw->enc_ctl_state.height = hdw->res_ver_val; 4240c0d06caSMauro Carvalho Chehab hdw->enc_ctl_state.is_50hz = ((hdw->std_mask_cur & V4L2_STD_525_60) ? 4250c0d06caSMauro Carvalho Chehab 0 : 1); 4260c0d06caSMauro Carvalho Chehab 4270c0d06caSMauro Carvalho Chehab ret = 0; 4280c0d06caSMauro Carvalho Chehab 4290c0d06caSMauro Carvalho Chehab ret |= pvr2_encoder_prep_config(hdw); 4300c0d06caSMauro Carvalho Chehab 4310c0d06caSMauro Carvalho Chehab /* saa7115: 0xf0 */ 4320c0d06caSMauro Carvalho Chehab val = 0xf0; 4330c0d06caSMauro Carvalho Chehab if (hdw->hdw_desc->flag_has_cx25840) { 4340c0d06caSMauro Carvalho Chehab /* ivtv cx25840: 0x140 */ 4350c0d06caSMauro Carvalho Chehab val = 0x140; 4360c0d06caSMauro Carvalho Chehab } 4370c0d06caSMauro Carvalho Chehab 4380c0d06caSMauro Carvalho Chehab if (!ret) ret = pvr2_encoder_vcmd( 4390c0d06caSMauro Carvalho Chehab hdw,CX2341X_ENC_SET_NUM_VSYNC_LINES, 2, 4400c0d06caSMauro Carvalho Chehab val, val); 4410c0d06caSMauro Carvalho Chehab 4420c0d06caSMauro Carvalho Chehab /* setup firmware to notify us about some events (don't know why...) */ 4430c0d06caSMauro Carvalho Chehab if (!ret) ret = pvr2_encoder_vcmd( 4440c0d06caSMauro Carvalho Chehab hdw,CX2341X_ENC_SET_EVENT_NOTIFICATION, 4, 4450c0d06caSMauro Carvalho Chehab 0, 0, 0x10000000, 0xffffffff); 4460c0d06caSMauro Carvalho Chehab 4470c0d06caSMauro Carvalho Chehab if (!ret) ret = pvr2_encoder_vcmd( 4480c0d06caSMauro Carvalho Chehab hdw,CX2341X_ENC_SET_VBI_LINE, 5, 4490c0d06caSMauro Carvalho Chehab 0xffffffff,0,0,0,0); 4500c0d06caSMauro Carvalho Chehab 4510c0d06caSMauro Carvalho Chehab if (ret) { 4520c0d06caSMauro Carvalho Chehab pvr2_trace(PVR2_TRACE_ERROR_LEGS, 4530c0d06caSMauro Carvalho Chehab "Failed to configure cx23416"); 4540c0d06caSMauro Carvalho Chehab return ret; 4550c0d06caSMauro Carvalho Chehab } 4560c0d06caSMauro Carvalho Chehab 4570c0d06caSMauro Carvalho Chehab ret = pvr2_encoder_adjust(hdw); 4580c0d06caSMauro Carvalho Chehab if (ret) return ret; 4590c0d06caSMauro Carvalho Chehab 4600c0d06caSMauro Carvalho Chehab ret = pvr2_encoder_vcmd( 4610c0d06caSMauro Carvalho Chehab hdw, CX2341X_ENC_INITIALIZE_INPUT, 0); 4620c0d06caSMauro Carvalho Chehab 4630c0d06caSMauro Carvalho Chehab if (ret) { 4640c0d06caSMauro Carvalho Chehab pvr2_trace(PVR2_TRACE_ERROR_LEGS, 4650c0d06caSMauro Carvalho Chehab "Failed to initialize cx23416 video input"); 4660c0d06caSMauro Carvalho Chehab return ret; 4670c0d06caSMauro Carvalho Chehab } 4680c0d06caSMauro Carvalho Chehab 4690c0d06caSMauro Carvalho Chehab return 0; 4700c0d06caSMauro Carvalho Chehab } 4710c0d06caSMauro Carvalho Chehab 4720c0d06caSMauro Carvalho Chehab 4730c0d06caSMauro Carvalho Chehab int pvr2_encoder_start(struct pvr2_hdw *hdw) 4740c0d06caSMauro Carvalho Chehab { 4750c0d06caSMauro Carvalho Chehab int status; 4760c0d06caSMauro Carvalho Chehab 4770c0d06caSMauro Carvalho Chehab /* unmask some interrupts */ 4780c0d06caSMauro Carvalho Chehab pvr2_write_register(hdw, 0x0048, 0xbfffffff); 4790c0d06caSMauro Carvalho Chehab 4800c0d06caSMauro Carvalho Chehab pvr2_encoder_vcmd(hdw,CX2341X_ENC_MUTE_VIDEO,1, 4810c0d06caSMauro Carvalho Chehab hdw->input_val == PVR2_CVAL_INPUT_RADIO ? 1 : 0); 4820c0d06caSMauro Carvalho Chehab 4830c0d06caSMauro Carvalho Chehab switch (hdw->active_stream_type) { 4840c0d06caSMauro Carvalho Chehab case pvr2_config_vbi: 4850c0d06caSMauro Carvalho Chehab status = pvr2_encoder_vcmd(hdw,CX2341X_ENC_START_CAPTURE,2, 4860c0d06caSMauro Carvalho Chehab 0x01,0x14); 4870c0d06caSMauro Carvalho Chehab break; 4880c0d06caSMauro Carvalho Chehab case pvr2_config_mpeg: 4890c0d06caSMauro Carvalho Chehab status = pvr2_encoder_vcmd(hdw,CX2341X_ENC_START_CAPTURE,2, 4900c0d06caSMauro Carvalho Chehab 0,0x13); 4910c0d06caSMauro Carvalho Chehab break; 4920c0d06caSMauro Carvalho Chehab default: /* Unhandled cases for now */ 4930c0d06caSMauro Carvalho Chehab status = pvr2_encoder_vcmd(hdw,CX2341X_ENC_START_CAPTURE,2, 4940c0d06caSMauro Carvalho Chehab 0,0x13); 4950c0d06caSMauro Carvalho Chehab break; 4960c0d06caSMauro Carvalho Chehab } 4970c0d06caSMauro Carvalho Chehab return status; 4980c0d06caSMauro Carvalho Chehab } 4990c0d06caSMauro Carvalho Chehab 5000c0d06caSMauro Carvalho Chehab int pvr2_encoder_stop(struct pvr2_hdw *hdw) 5010c0d06caSMauro Carvalho Chehab { 5020c0d06caSMauro Carvalho Chehab int status; 5030c0d06caSMauro Carvalho Chehab 5040c0d06caSMauro Carvalho Chehab /* mask all interrupts */ 5050c0d06caSMauro Carvalho Chehab pvr2_write_register(hdw, 0x0048, 0xffffffff); 5060c0d06caSMauro Carvalho Chehab 5070c0d06caSMauro Carvalho Chehab switch (hdw->active_stream_type) { 5080c0d06caSMauro Carvalho Chehab case pvr2_config_vbi: 5090c0d06caSMauro Carvalho Chehab status = pvr2_encoder_vcmd(hdw,CX2341X_ENC_STOP_CAPTURE,3, 5100c0d06caSMauro Carvalho Chehab 0x01,0x01,0x14); 5110c0d06caSMauro Carvalho Chehab break; 5120c0d06caSMauro Carvalho Chehab case pvr2_config_mpeg: 5130c0d06caSMauro Carvalho Chehab status = pvr2_encoder_vcmd(hdw,CX2341X_ENC_STOP_CAPTURE,3, 5140c0d06caSMauro Carvalho Chehab 0x01,0,0x13); 5150c0d06caSMauro Carvalho Chehab break; 5160c0d06caSMauro Carvalho Chehab default: /* Unhandled cases for now */ 5170c0d06caSMauro Carvalho Chehab status = pvr2_encoder_vcmd(hdw,CX2341X_ENC_STOP_CAPTURE,3, 5180c0d06caSMauro Carvalho Chehab 0x01,0,0x13); 5190c0d06caSMauro Carvalho Chehab break; 5200c0d06caSMauro Carvalho Chehab } 5210c0d06caSMauro Carvalho Chehab 5220c0d06caSMauro Carvalho Chehab return status; 5230c0d06caSMauro Carvalho Chehab } 524