18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Copyright (C) 2005 Mike Isely <isely@pobox.com> 58c2ecf20Sopenharmony_ci * Copyright (C) 2004 Aurelien Alleaume <slts@free.fr> 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/device.h> // for linux/firmware.h 98c2ecf20Sopenharmony_ci#include <linux/firmware.h> 108c2ecf20Sopenharmony_ci#include "pvrusb2-util.h" 118c2ecf20Sopenharmony_ci#include "pvrusb2-encoder.h" 128c2ecf20Sopenharmony_ci#include "pvrusb2-hdw-internal.h" 138c2ecf20Sopenharmony_ci#include "pvrusb2-debug.h" 148c2ecf20Sopenharmony_ci#include "pvrusb2-fx2-cmd.h" 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci/* Firmware mailbox flags - definitions found from ivtv */ 198c2ecf20Sopenharmony_ci#define IVTV_MBOX_FIRMWARE_DONE 0x00000004 208c2ecf20Sopenharmony_ci#define IVTV_MBOX_DRIVER_DONE 0x00000002 218c2ecf20Sopenharmony_ci#define IVTV_MBOX_DRIVER_BUSY 0x00000001 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci#define MBOX_BASE 0x44 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_cistatic int pvr2_encoder_write_words(struct pvr2_hdw *hdw, 278c2ecf20Sopenharmony_ci unsigned int offs, 288c2ecf20Sopenharmony_ci const u32 *data, unsigned int dlen) 298c2ecf20Sopenharmony_ci{ 308c2ecf20Sopenharmony_ci unsigned int idx,addr; 318c2ecf20Sopenharmony_ci unsigned int bAddr; 328c2ecf20Sopenharmony_ci int ret; 338c2ecf20Sopenharmony_ci unsigned int chunkCnt; 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci /* 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci Format: First byte must be 0x01. Remaining 32 bit words are 388c2ecf20Sopenharmony_ci spread out into chunks of 7 bytes each, with the first 4 bytes 398c2ecf20Sopenharmony_ci being the data word (little endian), and the next 3 bytes 408c2ecf20Sopenharmony_ci being the address where that data word is to be written (big 418c2ecf20Sopenharmony_ci endian). Repeat request for additional words, with offset 428c2ecf20Sopenharmony_ci adjusted accordingly. 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci */ 458c2ecf20Sopenharmony_ci while (dlen) { 468c2ecf20Sopenharmony_ci chunkCnt = 8; 478c2ecf20Sopenharmony_ci if (chunkCnt > dlen) chunkCnt = dlen; 488c2ecf20Sopenharmony_ci memset(hdw->cmd_buffer,0,sizeof(hdw->cmd_buffer)); 498c2ecf20Sopenharmony_ci bAddr = 0; 508c2ecf20Sopenharmony_ci hdw->cmd_buffer[bAddr++] = FX2CMD_MEM_WRITE_DWORD; 518c2ecf20Sopenharmony_ci for (idx = 0; idx < chunkCnt; idx++) { 528c2ecf20Sopenharmony_ci addr = idx + offs; 538c2ecf20Sopenharmony_ci hdw->cmd_buffer[bAddr+6] = (addr & 0xffu); 548c2ecf20Sopenharmony_ci hdw->cmd_buffer[bAddr+5] = ((addr>>8) & 0xffu); 558c2ecf20Sopenharmony_ci hdw->cmd_buffer[bAddr+4] = ((addr>>16) & 0xffu); 568c2ecf20Sopenharmony_ci PVR2_DECOMPOSE_LE(hdw->cmd_buffer, bAddr,data[idx]); 578c2ecf20Sopenharmony_ci bAddr += 7; 588c2ecf20Sopenharmony_ci } 598c2ecf20Sopenharmony_ci ret = pvr2_send_request(hdw, 608c2ecf20Sopenharmony_ci hdw->cmd_buffer,1+(chunkCnt*7), 618c2ecf20Sopenharmony_ci NULL,0); 628c2ecf20Sopenharmony_ci if (ret) return ret; 638c2ecf20Sopenharmony_ci data += chunkCnt; 648c2ecf20Sopenharmony_ci dlen -= chunkCnt; 658c2ecf20Sopenharmony_ci offs += chunkCnt; 668c2ecf20Sopenharmony_ci } 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci return 0; 698c2ecf20Sopenharmony_ci} 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_cistatic int pvr2_encoder_read_words(struct pvr2_hdw *hdw, 738c2ecf20Sopenharmony_ci unsigned int offs, 748c2ecf20Sopenharmony_ci u32 *data, unsigned int dlen) 758c2ecf20Sopenharmony_ci{ 768c2ecf20Sopenharmony_ci unsigned int idx; 778c2ecf20Sopenharmony_ci int ret; 788c2ecf20Sopenharmony_ci unsigned int chunkCnt; 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci /* 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci Format: First byte must be 0x02 (status check) or 0x28 (read 838c2ecf20Sopenharmony_ci back block of 32 bit words). Next 6 bytes must be zero, 848c2ecf20Sopenharmony_ci followed by a single byte of MBOX_BASE+offset for portion to 858c2ecf20Sopenharmony_ci be read. Returned data is packed set of 32 bits words that 868c2ecf20Sopenharmony_ci were read. 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci */ 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci while (dlen) { 918c2ecf20Sopenharmony_ci chunkCnt = 16; 928c2ecf20Sopenharmony_ci if (chunkCnt > dlen) chunkCnt = dlen; 938c2ecf20Sopenharmony_ci if (chunkCnt < 16) chunkCnt = 1; 948c2ecf20Sopenharmony_ci hdw->cmd_buffer[0] = 958c2ecf20Sopenharmony_ci ((chunkCnt == 1) ? 968c2ecf20Sopenharmony_ci FX2CMD_MEM_READ_DWORD : FX2CMD_MEM_READ_64BYTES); 978c2ecf20Sopenharmony_ci hdw->cmd_buffer[1] = 0; 988c2ecf20Sopenharmony_ci hdw->cmd_buffer[2] = 0; 998c2ecf20Sopenharmony_ci hdw->cmd_buffer[3] = 0; 1008c2ecf20Sopenharmony_ci hdw->cmd_buffer[4] = 0; 1018c2ecf20Sopenharmony_ci hdw->cmd_buffer[5] = ((offs>>16) & 0xffu); 1028c2ecf20Sopenharmony_ci hdw->cmd_buffer[6] = ((offs>>8) & 0xffu); 1038c2ecf20Sopenharmony_ci hdw->cmd_buffer[7] = (offs & 0xffu); 1048c2ecf20Sopenharmony_ci ret = pvr2_send_request(hdw, 1058c2ecf20Sopenharmony_ci hdw->cmd_buffer,8, 1068c2ecf20Sopenharmony_ci hdw->cmd_buffer, 1078c2ecf20Sopenharmony_ci (chunkCnt == 1 ? 4 : 16 * 4)); 1088c2ecf20Sopenharmony_ci if (ret) return ret; 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci for (idx = 0; idx < chunkCnt; idx++) { 1118c2ecf20Sopenharmony_ci data[idx] = PVR2_COMPOSE_LE(hdw->cmd_buffer,idx*4); 1128c2ecf20Sopenharmony_ci } 1138c2ecf20Sopenharmony_ci data += chunkCnt; 1148c2ecf20Sopenharmony_ci dlen -= chunkCnt; 1158c2ecf20Sopenharmony_ci offs += chunkCnt; 1168c2ecf20Sopenharmony_ci } 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci return 0; 1198c2ecf20Sopenharmony_ci} 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci/* This prototype is set up to be compatible with the 1238c2ecf20Sopenharmony_ci cx2341x_mbox_func prototype in cx2341x.h, which should be in 1248c2ecf20Sopenharmony_ci kernels 2.6.18 or later. We do this so that we can enable 1258c2ecf20Sopenharmony_ci cx2341x.ko to write to our encoder (by handing it a pointer to this 1268c2ecf20Sopenharmony_ci function). For earlier kernels this doesn't really matter. */ 1278c2ecf20Sopenharmony_cistatic int pvr2_encoder_cmd(void *ctxt, 1288c2ecf20Sopenharmony_ci u32 cmd, 1298c2ecf20Sopenharmony_ci int arg_cnt_send, 1308c2ecf20Sopenharmony_ci int arg_cnt_recv, 1318c2ecf20Sopenharmony_ci u32 *argp) 1328c2ecf20Sopenharmony_ci{ 1338c2ecf20Sopenharmony_ci unsigned int poll_count; 1348c2ecf20Sopenharmony_ci unsigned int try_count = 0; 1358c2ecf20Sopenharmony_ci int retry_flag; 1368c2ecf20Sopenharmony_ci int ret = 0; 1378c2ecf20Sopenharmony_ci unsigned int idx; 1388c2ecf20Sopenharmony_ci /* These sizes look to be limited by the FX2 firmware implementation */ 1398c2ecf20Sopenharmony_ci u32 wrData[16]; 1408c2ecf20Sopenharmony_ci u32 rdData[16]; 1418c2ecf20Sopenharmony_ci struct pvr2_hdw *hdw = (struct pvr2_hdw *)ctxt; 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci /* 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci The encoder seems to speak entirely using blocks 32 bit words. 1478c2ecf20Sopenharmony_ci In ivtv driver terms, this is a mailbox at MBOX_BASE which we 1488c2ecf20Sopenharmony_ci populate with data and watch what the hardware does with it. 1498c2ecf20Sopenharmony_ci The first word is a set of flags used to control the 1508c2ecf20Sopenharmony_ci transaction, the second word is the command to execute, the 1518c2ecf20Sopenharmony_ci third byte is zero (ivtv driver suggests that this is some 1528c2ecf20Sopenharmony_ci kind of return value), and the fourth byte is a specified 1538c2ecf20Sopenharmony_ci timeout (windows driver always uses 0x00060000 except for one 1548c2ecf20Sopenharmony_ci case when it is zero). All successive words are the argument 1558c2ecf20Sopenharmony_ci words for the command. 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci First, write out the entire set of words, with the first word 1588c2ecf20Sopenharmony_ci being zero. 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci Next, write out just the first word again, but set it to 1618c2ecf20Sopenharmony_ci IVTV_MBOX_DRIVER_DONE | IVTV_DRIVER_BUSY this time (which 1628c2ecf20Sopenharmony_ci probably means "go"). 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci Next, read back the return count words. Check the first word, 1658c2ecf20Sopenharmony_ci which should have IVTV_MBOX_FIRMWARE_DONE set. If however 1668c2ecf20Sopenharmony_ci that bit is not set, then the command isn't done so repeat the 1678c2ecf20Sopenharmony_ci read until it is set. 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci Finally, write out just the first word again, but set it to 1708c2ecf20Sopenharmony_ci 0x0 this time (which probably means "idle"). 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci */ 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci if (arg_cnt_send > (ARRAY_SIZE(wrData) - 4)) { 1758c2ecf20Sopenharmony_ci pvr2_trace( 1768c2ecf20Sopenharmony_ci PVR2_TRACE_ERROR_LEGS, 1778c2ecf20Sopenharmony_ci "Failed to write cx23416 command - too many input arguments (was given %u limit %lu)", 1788c2ecf20Sopenharmony_ci arg_cnt_send, (long unsigned) ARRAY_SIZE(wrData) - 4); 1798c2ecf20Sopenharmony_ci return -EINVAL; 1808c2ecf20Sopenharmony_ci } 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci if (arg_cnt_recv > (ARRAY_SIZE(rdData) - 4)) { 1838c2ecf20Sopenharmony_ci pvr2_trace( 1848c2ecf20Sopenharmony_ci PVR2_TRACE_ERROR_LEGS, 1858c2ecf20Sopenharmony_ci "Failed to write cx23416 command - too many return arguments (was given %u limit %lu)", 1868c2ecf20Sopenharmony_ci arg_cnt_recv, (long unsigned) ARRAY_SIZE(rdData) - 4); 1878c2ecf20Sopenharmony_ci return -EINVAL; 1888c2ecf20Sopenharmony_ci } 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci LOCK_TAKE(hdw->ctl_lock); while (1) { 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci if (!hdw->state_encoder_ok) { 1948c2ecf20Sopenharmony_ci ret = -EIO; 1958c2ecf20Sopenharmony_ci break; 1968c2ecf20Sopenharmony_ci } 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci retry_flag = 0; 1998c2ecf20Sopenharmony_ci try_count++; 2008c2ecf20Sopenharmony_ci ret = 0; 2018c2ecf20Sopenharmony_ci wrData[0] = 0; 2028c2ecf20Sopenharmony_ci wrData[1] = cmd; 2038c2ecf20Sopenharmony_ci wrData[2] = 0; 2048c2ecf20Sopenharmony_ci wrData[3] = 0x00060000; 2058c2ecf20Sopenharmony_ci for (idx = 0; idx < arg_cnt_send; idx++) { 2068c2ecf20Sopenharmony_ci wrData[idx+4] = argp[idx]; 2078c2ecf20Sopenharmony_ci } 2088c2ecf20Sopenharmony_ci for (; idx < ARRAY_SIZE(wrData) - 4; idx++) { 2098c2ecf20Sopenharmony_ci wrData[idx+4] = 0; 2108c2ecf20Sopenharmony_ci } 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci ret = pvr2_encoder_write_words(hdw,MBOX_BASE,wrData,idx); 2138c2ecf20Sopenharmony_ci if (ret) break; 2148c2ecf20Sopenharmony_ci wrData[0] = IVTV_MBOX_DRIVER_DONE|IVTV_MBOX_DRIVER_BUSY; 2158c2ecf20Sopenharmony_ci ret = pvr2_encoder_write_words(hdw,MBOX_BASE,wrData,1); 2168c2ecf20Sopenharmony_ci if (ret) break; 2178c2ecf20Sopenharmony_ci poll_count = 0; 2188c2ecf20Sopenharmony_ci while (1) { 2198c2ecf20Sopenharmony_ci poll_count++; 2208c2ecf20Sopenharmony_ci ret = pvr2_encoder_read_words(hdw,MBOX_BASE,rdData, 2218c2ecf20Sopenharmony_ci arg_cnt_recv+4); 2228c2ecf20Sopenharmony_ci if (ret) { 2238c2ecf20Sopenharmony_ci break; 2248c2ecf20Sopenharmony_ci } 2258c2ecf20Sopenharmony_ci if (rdData[0] & IVTV_MBOX_FIRMWARE_DONE) { 2268c2ecf20Sopenharmony_ci break; 2278c2ecf20Sopenharmony_ci } 2288c2ecf20Sopenharmony_ci if (rdData[0] && (poll_count < 1000)) continue; 2298c2ecf20Sopenharmony_ci if (!rdData[0]) { 2308c2ecf20Sopenharmony_ci retry_flag = !0; 2318c2ecf20Sopenharmony_ci pvr2_trace( 2328c2ecf20Sopenharmony_ci PVR2_TRACE_ERROR_LEGS, 2338c2ecf20Sopenharmony_ci "Encoder timed out waiting for us; arranging to retry"); 2348c2ecf20Sopenharmony_ci } else { 2358c2ecf20Sopenharmony_ci pvr2_trace( 2368c2ecf20Sopenharmony_ci PVR2_TRACE_ERROR_LEGS, 2378c2ecf20Sopenharmony_ci "***WARNING*** device's encoder appears to be stuck (status=0x%08x)", 2388c2ecf20Sopenharmony_cirdData[0]); 2398c2ecf20Sopenharmony_ci } 2408c2ecf20Sopenharmony_ci pvr2_trace( 2418c2ecf20Sopenharmony_ci PVR2_TRACE_ERROR_LEGS, 2428c2ecf20Sopenharmony_ci "Encoder command: 0x%02x",cmd); 2438c2ecf20Sopenharmony_ci for (idx = 4; idx < arg_cnt_send; idx++) { 2448c2ecf20Sopenharmony_ci pvr2_trace( 2458c2ecf20Sopenharmony_ci PVR2_TRACE_ERROR_LEGS, 2468c2ecf20Sopenharmony_ci "Encoder arg%d: 0x%08x", 2478c2ecf20Sopenharmony_ci idx-3,wrData[idx]); 2488c2ecf20Sopenharmony_ci } 2498c2ecf20Sopenharmony_ci ret = -EBUSY; 2508c2ecf20Sopenharmony_ci break; 2518c2ecf20Sopenharmony_ci } 2528c2ecf20Sopenharmony_ci if (retry_flag) { 2538c2ecf20Sopenharmony_ci if (try_count < 20) continue; 2548c2ecf20Sopenharmony_ci pvr2_trace( 2558c2ecf20Sopenharmony_ci PVR2_TRACE_ERROR_LEGS, 2568c2ecf20Sopenharmony_ci "Too many retries..."); 2578c2ecf20Sopenharmony_ci ret = -EBUSY; 2588c2ecf20Sopenharmony_ci } 2598c2ecf20Sopenharmony_ci if (ret) { 2608c2ecf20Sopenharmony_ci del_timer_sync(&hdw->encoder_run_timer); 2618c2ecf20Sopenharmony_ci hdw->state_encoder_ok = 0; 2628c2ecf20Sopenharmony_ci pvr2_trace(PVR2_TRACE_STBITS, 2638c2ecf20Sopenharmony_ci "State bit %s <-- %s", 2648c2ecf20Sopenharmony_ci "state_encoder_ok", 2658c2ecf20Sopenharmony_ci (hdw->state_encoder_ok ? "true" : "false")); 2668c2ecf20Sopenharmony_ci if (hdw->state_encoder_runok) { 2678c2ecf20Sopenharmony_ci hdw->state_encoder_runok = 0; 2688c2ecf20Sopenharmony_ci pvr2_trace(PVR2_TRACE_STBITS, 2698c2ecf20Sopenharmony_ci "State bit %s <-- %s", 2708c2ecf20Sopenharmony_ci "state_encoder_runok", 2718c2ecf20Sopenharmony_ci (hdw->state_encoder_runok ? 2728c2ecf20Sopenharmony_ci "true" : "false")); 2738c2ecf20Sopenharmony_ci } 2748c2ecf20Sopenharmony_ci pvr2_trace( 2758c2ecf20Sopenharmony_ci PVR2_TRACE_ERROR_LEGS, 2768c2ecf20Sopenharmony_ci "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."); 2778c2ecf20Sopenharmony_ci break; 2788c2ecf20Sopenharmony_ci } 2798c2ecf20Sopenharmony_ci wrData[0] = 0x7; 2808c2ecf20Sopenharmony_ci for (idx = 0; idx < arg_cnt_recv; idx++) { 2818c2ecf20Sopenharmony_ci argp[idx] = rdData[idx+4]; 2828c2ecf20Sopenharmony_ci } 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci wrData[0] = 0x0; 2858c2ecf20Sopenharmony_ci ret = pvr2_encoder_write_words(hdw,MBOX_BASE,wrData,1); 2868c2ecf20Sopenharmony_ci break; 2878c2ecf20Sopenharmony_ci } 2888c2ecf20Sopenharmony_ci LOCK_GIVE(hdw->ctl_lock); 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci return ret; 2918c2ecf20Sopenharmony_ci} 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_cistatic int pvr2_encoder_vcmd(struct pvr2_hdw *hdw, int cmd, 2958c2ecf20Sopenharmony_ci int args, ...) 2968c2ecf20Sopenharmony_ci{ 2978c2ecf20Sopenharmony_ci va_list vl; 2988c2ecf20Sopenharmony_ci unsigned int idx; 2998c2ecf20Sopenharmony_ci u32 data[12]; 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci if (args > ARRAY_SIZE(data)) { 3028c2ecf20Sopenharmony_ci pvr2_trace( 3038c2ecf20Sopenharmony_ci PVR2_TRACE_ERROR_LEGS, 3048c2ecf20Sopenharmony_ci "Failed to write cx23416 command - too many arguments (was given %u limit %lu)", 3058c2ecf20Sopenharmony_ci args, (long unsigned) ARRAY_SIZE(data)); 3068c2ecf20Sopenharmony_ci return -EINVAL; 3078c2ecf20Sopenharmony_ci } 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci va_start(vl, args); 3108c2ecf20Sopenharmony_ci for (idx = 0; idx < args; idx++) { 3118c2ecf20Sopenharmony_ci data[idx] = va_arg(vl, u32); 3128c2ecf20Sopenharmony_ci } 3138c2ecf20Sopenharmony_ci va_end(vl); 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci return pvr2_encoder_cmd(hdw,cmd,args,0,data); 3168c2ecf20Sopenharmony_ci} 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci/* This implements some extra setup for the encoder that seems to be 3208c2ecf20Sopenharmony_ci specific to the PVR USB2 hardware. */ 3218c2ecf20Sopenharmony_cistatic int pvr2_encoder_prep_config(struct pvr2_hdw *hdw) 3228c2ecf20Sopenharmony_ci{ 3238c2ecf20Sopenharmony_ci int ret = 0; 3248c2ecf20Sopenharmony_ci int encMisc3Arg = 0; 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci#if 0 3278c2ecf20Sopenharmony_ci /* This inexplicable bit happens in the Hauppauge windows 3288c2ecf20Sopenharmony_ci driver (for both 24xxx and 29xxx devices). However I 3298c2ecf20Sopenharmony_ci currently see no difference in behavior with or without 3308c2ecf20Sopenharmony_ci this stuff. Leave this here as a note of its existence, 3318c2ecf20Sopenharmony_ci but don't use it. */ 3328c2ecf20Sopenharmony_ci LOCK_TAKE(hdw->ctl_lock); do { 3338c2ecf20Sopenharmony_ci u32 dat[1]; 3348c2ecf20Sopenharmony_ci dat[0] = 0x80000640; 3358c2ecf20Sopenharmony_ci pvr2_encoder_write_words(hdw,0x01fe,dat,1); 3368c2ecf20Sopenharmony_ci pvr2_encoder_write_words(hdw,0x023e,dat,1); 3378c2ecf20Sopenharmony_ci } while(0); LOCK_GIVE(hdw->ctl_lock); 3388c2ecf20Sopenharmony_ci#endif 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci /* Mike Isely <isely@pobox.com> 26-Jan-2006 The windows driver 3418c2ecf20Sopenharmony_ci sends the following list of ENC_MISC commands (for both 3428c2ecf20Sopenharmony_ci 24xxx and 29xxx devices). Meanings are not entirely clear, 3438c2ecf20Sopenharmony_ci however without the ENC_MISC(3,1) command then we risk 3448c2ecf20Sopenharmony_ci random perpetual video corruption whenever the video input 3458c2ecf20Sopenharmony_ci breaks up for a moment (like when switching channels). */ 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci#if 0 3498c2ecf20Sopenharmony_ci /* This ENC_MISC(5,0) command seems to hurt 29xxx sync 3508c2ecf20Sopenharmony_ci performance on channel changes, but is not a problem on 3518c2ecf20Sopenharmony_ci 24xxx devices. */ 3528c2ecf20Sopenharmony_ci ret |= pvr2_encoder_vcmd(hdw, CX2341X_ENC_MISC,4, 5,0,0,0); 3538c2ecf20Sopenharmony_ci#endif 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci /* This ENC_MISC(3,encMisc3Arg) command is critical - without 3568c2ecf20Sopenharmony_ci it there will eventually be video corruption. Also, the 3578c2ecf20Sopenharmony_ci saa7115 case is strange - the Windows driver is passing 1 3588c2ecf20Sopenharmony_ci regardless of device type but if we have 1 for saa7115 3598c2ecf20Sopenharmony_ci devices the video turns sluggish. */ 3608c2ecf20Sopenharmony_ci if (hdw->hdw_desc->flag_has_cx25840) { 3618c2ecf20Sopenharmony_ci encMisc3Arg = 1; 3628c2ecf20Sopenharmony_ci } else { 3638c2ecf20Sopenharmony_ci encMisc3Arg = 0; 3648c2ecf20Sopenharmony_ci } 3658c2ecf20Sopenharmony_ci ret |= pvr2_encoder_vcmd(hdw, CX2341X_ENC_MISC,4, 3, 3668c2ecf20Sopenharmony_ci encMisc3Arg,0,0); 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci ret |= pvr2_encoder_vcmd(hdw, CX2341X_ENC_MISC,4, 8,0,0,0); 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci#if 0 3718c2ecf20Sopenharmony_ci /* This ENC_MISC(4,1) command is poisonous, so it is commented 3728c2ecf20Sopenharmony_ci out. But I'm leaving it here anyway to document its 3738c2ecf20Sopenharmony_ci existence in the Windows driver. The effect of this 3748c2ecf20Sopenharmony_ci command is that apps displaying the stream become sluggish 3758c2ecf20Sopenharmony_ci with stuttering video. */ 3768c2ecf20Sopenharmony_ci ret |= pvr2_encoder_vcmd(hdw, CX2341X_ENC_MISC,4, 4,1,0,0); 3778c2ecf20Sopenharmony_ci#endif 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci ret |= pvr2_encoder_vcmd(hdw, CX2341X_ENC_MISC,4, 0,3,0,0); 3808c2ecf20Sopenharmony_ci ret |= pvr2_encoder_vcmd(hdw, CX2341X_ENC_MISC,4,15,0,0,0); 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci /* prevent the PTSs from slowly drifting away in the generated 3838c2ecf20Sopenharmony_ci MPEG stream */ 3848c2ecf20Sopenharmony_ci ret |= pvr2_encoder_vcmd(hdw, CX2341X_ENC_MISC, 2, 4, 1); 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci return ret; 3878c2ecf20Sopenharmony_ci} 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ciint pvr2_encoder_adjust(struct pvr2_hdw *hdw) 3908c2ecf20Sopenharmony_ci{ 3918c2ecf20Sopenharmony_ci int ret; 3928c2ecf20Sopenharmony_ci ret = cx2341x_update(hdw,pvr2_encoder_cmd, 3938c2ecf20Sopenharmony_ci (hdw->enc_cur_valid ? &hdw->enc_cur_state : NULL), 3948c2ecf20Sopenharmony_ci &hdw->enc_ctl_state); 3958c2ecf20Sopenharmony_ci if (ret) { 3968c2ecf20Sopenharmony_ci pvr2_trace(PVR2_TRACE_ERROR_LEGS, 3978c2ecf20Sopenharmony_ci "Error from cx2341x module code=%d",ret); 3988c2ecf20Sopenharmony_ci } else { 3998c2ecf20Sopenharmony_ci hdw->enc_cur_state = hdw->enc_ctl_state; 4008c2ecf20Sopenharmony_ci hdw->enc_cur_valid = !0; 4018c2ecf20Sopenharmony_ci } 4028c2ecf20Sopenharmony_ci return ret; 4038c2ecf20Sopenharmony_ci} 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ciint pvr2_encoder_configure(struct pvr2_hdw *hdw) 4078c2ecf20Sopenharmony_ci{ 4088c2ecf20Sopenharmony_ci int ret; 4098c2ecf20Sopenharmony_ci int val; 4108c2ecf20Sopenharmony_ci pvr2_trace(PVR2_TRACE_ENCODER, "pvr2_encoder_configure (cx2341x module)"); 4118c2ecf20Sopenharmony_ci hdw->enc_ctl_state.port = CX2341X_PORT_STREAMING; 4128c2ecf20Sopenharmony_ci hdw->enc_ctl_state.width = hdw->res_hor_val; 4138c2ecf20Sopenharmony_ci hdw->enc_ctl_state.height = hdw->res_ver_val; 4148c2ecf20Sopenharmony_ci hdw->enc_ctl_state.is_50hz = ((hdw->std_mask_cur & V4L2_STD_525_60) ? 4158c2ecf20Sopenharmony_ci 0 : 1); 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci ret = 0; 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci ret |= pvr2_encoder_prep_config(hdw); 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci /* saa7115: 0xf0 */ 4228c2ecf20Sopenharmony_ci val = 0xf0; 4238c2ecf20Sopenharmony_ci if (hdw->hdw_desc->flag_has_cx25840) { 4248c2ecf20Sopenharmony_ci /* ivtv cx25840: 0x140 */ 4258c2ecf20Sopenharmony_ci val = 0x140; 4268c2ecf20Sopenharmony_ci } 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci if (!ret) ret = pvr2_encoder_vcmd( 4298c2ecf20Sopenharmony_ci hdw,CX2341X_ENC_SET_NUM_VSYNC_LINES, 2, 4308c2ecf20Sopenharmony_ci val, val); 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci /* setup firmware to notify us about some events (don't know why...) */ 4338c2ecf20Sopenharmony_ci if (!ret) ret = pvr2_encoder_vcmd( 4348c2ecf20Sopenharmony_ci hdw,CX2341X_ENC_SET_EVENT_NOTIFICATION, 4, 4358c2ecf20Sopenharmony_ci 0, 0, 0x10000000, 0xffffffff); 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci if (!ret) ret = pvr2_encoder_vcmd( 4388c2ecf20Sopenharmony_ci hdw,CX2341X_ENC_SET_VBI_LINE, 5, 4398c2ecf20Sopenharmony_ci 0xffffffff,0,0,0,0); 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci if (ret) { 4428c2ecf20Sopenharmony_ci pvr2_trace(PVR2_TRACE_ERROR_LEGS, 4438c2ecf20Sopenharmony_ci "Failed to configure cx23416"); 4448c2ecf20Sopenharmony_ci return ret; 4458c2ecf20Sopenharmony_ci } 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci ret = pvr2_encoder_adjust(hdw); 4488c2ecf20Sopenharmony_ci if (ret) return ret; 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci ret = pvr2_encoder_vcmd( 4518c2ecf20Sopenharmony_ci hdw, CX2341X_ENC_INITIALIZE_INPUT, 0); 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci if (ret) { 4548c2ecf20Sopenharmony_ci pvr2_trace(PVR2_TRACE_ERROR_LEGS, 4558c2ecf20Sopenharmony_ci "Failed to initialize cx23416 video input"); 4568c2ecf20Sopenharmony_ci return ret; 4578c2ecf20Sopenharmony_ci } 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci return 0; 4608c2ecf20Sopenharmony_ci} 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ciint pvr2_encoder_start(struct pvr2_hdw *hdw) 4648c2ecf20Sopenharmony_ci{ 4658c2ecf20Sopenharmony_ci int status; 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ci /* unmask some interrupts */ 4688c2ecf20Sopenharmony_ci pvr2_write_register(hdw, 0x0048, 0xbfffffff); 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci pvr2_encoder_vcmd(hdw,CX2341X_ENC_MUTE_VIDEO,1, 4718c2ecf20Sopenharmony_ci hdw->input_val == PVR2_CVAL_INPUT_RADIO ? 1 : 0); 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci switch (hdw->active_stream_type) { 4748c2ecf20Sopenharmony_ci case pvr2_config_vbi: 4758c2ecf20Sopenharmony_ci status = pvr2_encoder_vcmd(hdw,CX2341X_ENC_START_CAPTURE,2, 4768c2ecf20Sopenharmony_ci 0x01,0x14); 4778c2ecf20Sopenharmony_ci break; 4788c2ecf20Sopenharmony_ci case pvr2_config_mpeg: 4798c2ecf20Sopenharmony_ci status = pvr2_encoder_vcmd(hdw,CX2341X_ENC_START_CAPTURE,2, 4808c2ecf20Sopenharmony_ci 0,0x13); 4818c2ecf20Sopenharmony_ci break; 4828c2ecf20Sopenharmony_ci default: /* Unhandled cases for now */ 4838c2ecf20Sopenharmony_ci status = pvr2_encoder_vcmd(hdw,CX2341X_ENC_START_CAPTURE,2, 4848c2ecf20Sopenharmony_ci 0,0x13); 4858c2ecf20Sopenharmony_ci break; 4868c2ecf20Sopenharmony_ci } 4878c2ecf20Sopenharmony_ci return status; 4888c2ecf20Sopenharmony_ci} 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ciint pvr2_encoder_stop(struct pvr2_hdw *hdw) 4918c2ecf20Sopenharmony_ci{ 4928c2ecf20Sopenharmony_ci int status; 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_ci /* mask all interrupts */ 4958c2ecf20Sopenharmony_ci pvr2_write_register(hdw, 0x0048, 0xffffffff); 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci switch (hdw->active_stream_type) { 4988c2ecf20Sopenharmony_ci case pvr2_config_vbi: 4998c2ecf20Sopenharmony_ci status = pvr2_encoder_vcmd(hdw,CX2341X_ENC_STOP_CAPTURE,3, 5008c2ecf20Sopenharmony_ci 0x01,0x01,0x14); 5018c2ecf20Sopenharmony_ci break; 5028c2ecf20Sopenharmony_ci case pvr2_config_mpeg: 5038c2ecf20Sopenharmony_ci status = pvr2_encoder_vcmd(hdw,CX2341X_ENC_STOP_CAPTURE,3, 5048c2ecf20Sopenharmony_ci 0x01,0,0x13); 5058c2ecf20Sopenharmony_ci break; 5068c2ecf20Sopenharmony_ci default: /* Unhandled cases for now */ 5078c2ecf20Sopenharmony_ci status = pvr2_encoder_vcmd(hdw,CX2341X_ENC_STOP_CAPTURE,3, 5088c2ecf20Sopenharmony_ci 0x01,0,0x13); 5098c2ecf20Sopenharmony_ci break; 5108c2ecf20Sopenharmony_ci } 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_ci return status; 5138c2ecf20Sopenharmony_ci} 514