162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Copyright (C) 2005 Mike Isely <isely@pobox.com> 562306a36Sopenharmony_ci * Copyright (C) 2004 Aurelien Alleaume <slts@free.fr> 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/device.h> // for linux/firmware.h 962306a36Sopenharmony_ci#include <linux/firmware.h> 1062306a36Sopenharmony_ci#include "pvrusb2-util.h" 1162306a36Sopenharmony_ci#include "pvrusb2-encoder.h" 1262306a36Sopenharmony_ci#include "pvrusb2-hdw-internal.h" 1362306a36Sopenharmony_ci#include "pvrusb2-debug.h" 1462306a36Sopenharmony_ci#include "pvrusb2-fx2-cmd.h" 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci/* Firmware mailbox flags - definitions found from ivtv */ 1962306a36Sopenharmony_ci#define IVTV_MBOX_FIRMWARE_DONE 0x00000004 2062306a36Sopenharmony_ci#define IVTV_MBOX_DRIVER_DONE 0x00000002 2162306a36Sopenharmony_ci#define IVTV_MBOX_DRIVER_BUSY 0x00000001 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci#define MBOX_BASE 0x44 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_cistatic int pvr2_encoder_write_words(struct pvr2_hdw *hdw, 2762306a36Sopenharmony_ci unsigned int offs, 2862306a36Sopenharmony_ci const u32 *data, unsigned int dlen) 2962306a36Sopenharmony_ci{ 3062306a36Sopenharmony_ci unsigned int idx,addr; 3162306a36Sopenharmony_ci unsigned int bAddr; 3262306a36Sopenharmony_ci int ret; 3362306a36Sopenharmony_ci unsigned int chunkCnt; 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci /* 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci Format: First byte must be 0x01. Remaining 32 bit words are 3862306a36Sopenharmony_ci spread out into chunks of 7 bytes each, with the first 4 bytes 3962306a36Sopenharmony_ci being the data word (little endian), and the next 3 bytes 4062306a36Sopenharmony_ci being the address where that data word is to be written (big 4162306a36Sopenharmony_ci endian). Repeat request for additional words, with offset 4262306a36Sopenharmony_ci adjusted accordingly. 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci */ 4562306a36Sopenharmony_ci while (dlen) { 4662306a36Sopenharmony_ci chunkCnt = 8; 4762306a36Sopenharmony_ci if (chunkCnt > dlen) chunkCnt = dlen; 4862306a36Sopenharmony_ci memset(hdw->cmd_buffer,0,sizeof(hdw->cmd_buffer)); 4962306a36Sopenharmony_ci bAddr = 0; 5062306a36Sopenharmony_ci hdw->cmd_buffer[bAddr++] = FX2CMD_MEM_WRITE_DWORD; 5162306a36Sopenharmony_ci for (idx = 0; idx < chunkCnt; idx++) { 5262306a36Sopenharmony_ci addr = idx + offs; 5362306a36Sopenharmony_ci hdw->cmd_buffer[bAddr+6] = (addr & 0xffu); 5462306a36Sopenharmony_ci hdw->cmd_buffer[bAddr+5] = ((addr>>8) & 0xffu); 5562306a36Sopenharmony_ci hdw->cmd_buffer[bAddr+4] = ((addr>>16) & 0xffu); 5662306a36Sopenharmony_ci PVR2_DECOMPOSE_LE(hdw->cmd_buffer, bAddr,data[idx]); 5762306a36Sopenharmony_ci bAddr += 7; 5862306a36Sopenharmony_ci } 5962306a36Sopenharmony_ci ret = pvr2_send_request(hdw, 6062306a36Sopenharmony_ci hdw->cmd_buffer,1+(chunkCnt*7), 6162306a36Sopenharmony_ci NULL,0); 6262306a36Sopenharmony_ci if (ret) return ret; 6362306a36Sopenharmony_ci data += chunkCnt; 6462306a36Sopenharmony_ci dlen -= chunkCnt; 6562306a36Sopenharmony_ci offs += chunkCnt; 6662306a36Sopenharmony_ci } 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci return 0; 6962306a36Sopenharmony_ci} 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_cistatic int pvr2_encoder_read_words(struct pvr2_hdw *hdw, 7362306a36Sopenharmony_ci unsigned int offs, 7462306a36Sopenharmony_ci u32 *data, unsigned int dlen) 7562306a36Sopenharmony_ci{ 7662306a36Sopenharmony_ci unsigned int idx; 7762306a36Sopenharmony_ci int ret; 7862306a36Sopenharmony_ci unsigned int chunkCnt; 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci /* 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci Format: First byte must be 0x02 (status check) or 0x28 (read 8362306a36Sopenharmony_ci back block of 32 bit words). Next 6 bytes must be zero, 8462306a36Sopenharmony_ci followed by a single byte of MBOX_BASE+offset for portion to 8562306a36Sopenharmony_ci be read. Returned data is packed set of 32 bits words that 8662306a36Sopenharmony_ci were read. 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci */ 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci while (dlen) { 9162306a36Sopenharmony_ci chunkCnt = 16; 9262306a36Sopenharmony_ci if (chunkCnt > dlen) chunkCnt = dlen; 9362306a36Sopenharmony_ci if (chunkCnt < 16) chunkCnt = 1; 9462306a36Sopenharmony_ci hdw->cmd_buffer[0] = 9562306a36Sopenharmony_ci ((chunkCnt == 1) ? 9662306a36Sopenharmony_ci FX2CMD_MEM_READ_DWORD : FX2CMD_MEM_READ_64BYTES); 9762306a36Sopenharmony_ci hdw->cmd_buffer[1] = 0; 9862306a36Sopenharmony_ci hdw->cmd_buffer[2] = 0; 9962306a36Sopenharmony_ci hdw->cmd_buffer[3] = 0; 10062306a36Sopenharmony_ci hdw->cmd_buffer[4] = 0; 10162306a36Sopenharmony_ci hdw->cmd_buffer[5] = ((offs>>16) & 0xffu); 10262306a36Sopenharmony_ci hdw->cmd_buffer[6] = ((offs>>8) & 0xffu); 10362306a36Sopenharmony_ci hdw->cmd_buffer[7] = (offs & 0xffu); 10462306a36Sopenharmony_ci ret = pvr2_send_request(hdw, 10562306a36Sopenharmony_ci hdw->cmd_buffer,8, 10662306a36Sopenharmony_ci hdw->cmd_buffer, 10762306a36Sopenharmony_ci (chunkCnt == 1 ? 4 : 16 * 4)); 10862306a36Sopenharmony_ci if (ret) return ret; 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci for (idx = 0; idx < chunkCnt; idx++) { 11162306a36Sopenharmony_ci data[idx] = PVR2_COMPOSE_LE(hdw->cmd_buffer,idx*4); 11262306a36Sopenharmony_ci } 11362306a36Sopenharmony_ci data += chunkCnt; 11462306a36Sopenharmony_ci dlen -= chunkCnt; 11562306a36Sopenharmony_ci offs += chunkCnt; 11662306a36Sopenharmony_ci } 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci return 0; 11962306a36Sopenharmony_ci} 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci/* This prototype is set up to be compatible with the 12362306a36Sopenharmony_ci cx2341x_mbox_func prototype in cx2341x.h, which should be in 12462306a36Sopenharmony_ci kernels 2.6.18 or later. We do this so that we can enable 12562306a36Sopenharmony_ci cx2341x.ko to write to our encoder (by handing it a pointer to this 12662306a36Sopenharmony_ci function). For earlier kernels this doesn't really matter. */ 12762306a36Sopenharmony_cistatic int pvr2_encoder_cmd(void *ctxt, 12862306a36Sopenharmony_ci u32 cmd, 12962306a36Sopenharmony_ci int arg_cnt_send, 13062306a36Sopenharmony_ci int arg_cnt_recv, 13162306a36Sopenharmony_ci u32 *argp) 13262306a36Sopenharmony_ci{ 13362306a36Sopenharmony_ci unsigned int poll_count; 13462306a36Sopenharmony_ci unsigned int try_count = 0; 13562306a36Sopenharmony_ci int retry_flag; 13662306a36Sopenharmony_ci int ret = 0; 13762306a36Sopenharmony_ci unsigned int idx; 13862306a36Sopenharmony_ci /* These sizes look to be limited by the FX2 firmware implementation */ 13962306a36Sopenharmony_ci u32 wrData[16]; 14062306a36Sopenharmony_ci u32 rdData[16]; 14162306a36Sopenharmony_ci struct pvr2_hdw *hdw = (struct pvr2_hdw *)ctxt; 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci /* 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci The encoder seems to speak entirely using blocks 32 bit words. 14762306a36Sopenharmony_ci In ivtv driver terms, this is a mailbox at MBOX_BASE which we 14862306a36Sopenharmony_ci populate with data and watch what the hardware does with it. 14962306a36Sopenharmony_ci The first word is a set of flags used to control the 15062306a36Sopenharmony_ci transaction, the second word is the command to execute, the 15162306a36Sopenharmony_ci third byte is zero (ivtv driver suggests that this is some 15262306a36Sopenharmony_ci kind of return value), and the fourth byte is a specified 15362306a36Sopenharmony_ci timeout (windows driver always uses 0x00060000 except for one 15462306a36Sopenharmony_ci case when it is zero). All successive words are the argument 15562306a36Sopenharmony_ci words for the command. 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci First, write out the entire set of words, with the first word 15862306a36Sopenharmony_ci being zero. 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci Next, write out just the first word again, but set it to 16162306a36Sopenharmony_ci IVTV_MBOX_DRIVER_DONE | IVTV_DRIVER_BUSY this time (which 16262306a36Sopenharmony_ci probably means "go"). 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci Next, read back the return count words. Check the first word, 16562306a36Sopenharmony_ci which should have IVTV_MBOX_FIRMWARE_DONE set. If however 16662306a36Sopenharmony_ci that bit is not set, then the command isn't done so repeat the 16762306a36Sopenharmony_ci read until it is set. 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci Finally, write out just the first word again, but set it to 17062306a36Sopenharmony_ci 0x0 this time (which probably means "idle"). 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci */ 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci if (arg_cnt_send > (ARRAY_SIZE(wrData) - 4)) { 17562306a36Sopenharmony_ci pvr2_trace( 17662306a36Sopenharmony_ci PVR2_TRACE_ERROR_LEGS, 17762306a36Sopenharmony_ci "Failed to write cx23416 command - too many input arguments (was given %u limit %lu)", 17862306a36Sopenharmony_ci arg_cnt_send, (long unsigned) ARRAY_SIZE(wrData) - 4); 17962306a36Sopenharmony_ci return -EINVAL; 18062306a36Sopenharmony_ci } 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci if (arg_cnt_recv > (ARRAY_SIZE(rdData) - 4)) { 18362306a36Sopenharmony_ci pvr2_trace( 18462306a36Sopenharmony_ci PVR2_TRACE_ERROR_LEGS, 18562306a36Sopenharmony_ci "Failed to write cx23416 command - too many return arguments (was given %u limit %lu)", 18662306a36Sopenharmony_ci arg_cnt_recv, (long unsigned) ARRAY_SIZE(rdData) - 4); 18762306a36Sopenharmony_ci return -EINVAL; 18862306a36Sopenharmony_ci } 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci LOCK_TAKE(hdw->ctl_lock); 19262306a36Sopenharmony_ci while (1) { 19362306a36Sopenharmony_ci if (!hdw->state_encoder_ok) { 19462306a36Sopenharmony_ci ret = -EIO; 19562306a36Sopenharmony_ci break; 19662306a36Sopenharmony_ci } 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci retry_flag = 0; 19962306a36Sopenharmony_ci try_count++; 20062306a36Sopenharmony_ci ret = 0; 20162306a36Sopenharmony_ci wrData[0] = 0; 20262306a36Sopenharmony_ci wrData[1] = cmd; 20362306a36Sopenharmony_ci wrData[2] = 0; 20462306a36Sopenharmony_ci wrData[3] = 0x00060000; 20562306a36Sopenharmony_ci for (idx = 0; idx < arg_cnt_send; idx++) { 20662306a36Sopenharmony_ci wrData[idx+4] = argp[idx]; 20762306a36Sopenharmony_ci } 20862306a36Sopenharmony_ci for (; idx < ARRAY_SIZE(wrData) - 4; idx++) { 20962306a36Sopenharmony_ci wrData[idx+4] = 0; 21062306a36Sopenharmony_ci } 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci ret = pvr2_encoder_write_words(hdw,MBOX_BASE,wrData,idx); 21362306a36Sopenharmony_ci if (ret) break; 21462306a36Sopenharmony_ci wrData[0] = IVTV_MBOX_DRIVER_DONE|IVTV_MBOX_DRIVER_BUSY; 21562306a36Sopenharmony_ci ret = pvr2_encoder_write_words(hdw,MBOX_BASE,wrData,1); 21662306a36Sopenharmony_ci if (ret) break; 21762306a36Sopenharmony_ci poll_count = 0; 21862306a36Sopenharmony_ci while (1) { 21962306a36Sopenharmony_ci poll_count++; 22062306a36Sopenharmony_ci ret = pvr2_encoder_read_words(hdw,MBOX_BASE,rdData, 22162306a36Sopenharmony_ci arg_cnt_recv+4); 22262306a36Sopenharmony_ci if (ret) { 22362306a36Sopenharmony_ci break; 22462306a36Sopenharmony_ci } 22562306a36Sopenharmony_ci if (rdData[0] & IVTV_MBOX_FIRMWARE_DONE) { 22662306a36Sopenharmony_ci break; 22762306a36Sopenharmony_ci } 22862306a36Sopenharmony_ci if (rdData[0] && (poll_count < 1000)) continue; 22962306a36Sopenharmony_ci if (!rdData[0]) { 23062306a36Sopenharmony_ci retry_flag = !0; 23162306a36Sopenharmony_ci pvr2_trace( 23262306a36Sopenharmony_ci PVR2_TRACE_ERROR_LEGS, 23362306a36Sopenharmony_ci "Encoder timed out waiting for us; arranging to retry"); 23462306a36Sopenharmony_ci } else { 23562306a36Sopenharmony_ci pvr2_trace( 23662306a36Sopenharmony_ci PVR2_TRACE_ERROR_LEGS, 23762306a36Sopenharmony_ci "***WARNING*** device's encoder appears to be stuck (status=0x%08x)", 23862306a36Sopenharmony_cirdData[0]); 23962306a36Sopenharmony_ci } 24062306a36Sopenharmony_ci pvr2_trace( 24162306a36Sopenharmony_ci PVR2_TRACE_ERROR_LEGS, 24262306a36Sopenharmony_ci "Encoder command: 0x%02x",cmd); 24362306a36Sopenharmony_ci for (idx = 4; idx < arg_cnt_send; idx++) { 24462306a36Sopenharmony_ci pvr2_trace( 24562306a36Sopenharmony_ci PVR2_TRACE_ERROR_LEGS, 24662306a36Sopenharmony_ci "Encoder arg%d: 0x%08x", 24762306a36Sopenharmony_ci idx-3,wrData[idx]); 24862306a36Sopenharmony_ci } 24962306a36Sopenharmony_ci ret = -EBUSY; 25062306a36Sopenharmony_ci break; 25162306a36Sopenharmony_ci } 25262306a36Sopenharmony_ci if (retry_flag) { 25362306a36Sopenharmony_ci if (try_count < 20) continue; 25462306a36Sopenharmony_ci pvr2_trace( 25562306a36Sopenharmony_ci PVR2_TRACE_ERROR_LEGS, 25662306a36Sopenharmony_ci "Too many retries..."); 25762306a36Sopenharmony_ci ret = -EBUSY; 25862306a36Sopenharmony_ci } 25962306a36Sopenharmony_ci if (ret) { 26062306a36Sopenharmony_ci del_timer_sync(&hdw->encoder_run_timer); 26162306a36Sopenharmony_ci hdw->state_encoder_ok = 0; 26262306a36Sopenharmony_ci pvr2_trace(PVR2_TRACE_STBITS, 26362306a36Sopenharmony_ci "State bit %s <-- %s", 26462306a36Sopenharmony_ci "state_encoder_ok", 26562306a36Sopenharmony_ci (hdw->state_encoder_ok ? "true" : "false")); 26662306a36Sopenharmony_ci if (hdw->state_encoder_runok) { 26762306a36Sopenharmony_ci hdw->state_encoder_runok = 0; 26862306a36Sopenharmony_ci pvr2_trace(PVR2_TRACE_STBITS, 26962306a36Sopenharmony_ci "State bit %s <-- %s", 27062306a36Sopenharmony_ci "state_encoder_runok", 27162306a36Sopenharmony_ci (hdw->state_encoder_runok ? 27262306a36Sopenharmony_ci "true" : "false")); 27362306a36Sopenharmony_ci } 27462306a36Sopenharmony_ci pvr2_trace( 27562306a36Sopenharmony_ci PVR2_TRACE_ERROR_LEGS, 27662306a36Sopenharmony_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."); 27762306a36Sopenharmony_ci break; 27862306a36Sopenharmony_ci } 27962306a36Sopenharmony_ci wrData[0] = 0x7; 28062306a36Sopenharmony_ci for (idx = 0; idx < arg_cnt_recv; idx++) { 28162306a36Sopenharmony_ci argp[idx] = rdData[idx+4]; 28262306a36Sopenharmony_ci } 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci wrData[0] = 0x0; 28562306a36Sopenharmony_ci ret = pvr2_encoder_write_words(hdw,MBOX_BASE,wrData,1); 28662306a36Sopenharmony_ci break; 28762306a36Sopenharmony_ci } 28862306a36Sopenharmony_ci LOCK_GIVE(hdw->ctl_lock); 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci return ret; 29162306a36Sopenharmony_ci} 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_cistatic int pvr2_encoder_vcmd(struct pvr2_hdw *hdw, int cmd, 29562306a36Sopenharmony_ci int args, ...) 29662306a36Sopenharmony_ci{ 29762306a36Sopenharmony_ci va_list vl; 29862306a36Sopenharmony_ci unsigned int idx; 29962306a36Sopenharmony_ci u32 data[12]; 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci if (args > ARRAY_SIZE(data)) { 30262306a36Sopenharmony_ci pvr2_trace( 30362306a36Sopenharmony_ci PVR2_TRACE_ERROR_LEGS, 30462306a36Sopenharmony_ci "Failed to write cx23416 command - too many arguments (was given %u limit %lu)", 30562306a36Sopenharmony_ci args, (long unsigned) ARRAY_SIZE(data)); 30662306a36Sopenharmony_ci return -EINVAL; 30762306a36Sopenharmony_ci } 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci va_start(vl, args); 31062306a36Sopenharmony_ci for (idx = 0; idx < args; idx++) { 31162306a36Sopenharmony_ci data[idx] = va_arg(vl, u32); 31262306a36Sopenharmony_ci } 31362306a36Sopenharmony_ci va_end(vl); 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci return pvr2_encoder_cmd(hdw,cmd,args,0,data); 31662306a36Sopenharmony_ci} 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci/* This implements some extra setup for the encoder that seems to be 32062306a36Sopenharmony_ci specific to the PVR USB2 hardware. */ 32162306a36Sopenharmony_cistatic int pvr2_encoder_prep_config(struct pvr2_hdw *hdw) 32262306a36Sopenharmony_ci{ 32362306a36Sopenharmony_ci int ret = 0; 32462306a36Sopenharmony_ci int encMisc3Arg = 0; 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci#if 0 32762306a36Sopenharmony_ci /* This inexplicable bit happens in the Hauppauge windows 32862306a36Sopenharmony_ci driver (for both 24xxx and 29xxx devices). However I 32962306a36Sopenharmony_ci currently see no difference in behavior with or without 33062306a36Sopenharmony_ci this stuff. Leave this here as a note of its existence, 33162306a36Sopenharmony_ci but don't use it. */ 33262306a36Sopenharmony_ci LOCK_TAKE(hdw->ctl_lock); do { 33362306a36Sopenharmony_ci u32 dat[1]; 33462306a36Sopenharmony_ci dat[0] = 0x80000640; 33562306a36Sopenharmony_ci pvr2_encoder_write_words(hdw,0x01fe,dat,1); 33662306a36Sopenharmony_ci pvr2_encoder_write_words(hdw,0x023e,dat,1); 33762306a36Sopenharmony_ci } while(0); LOCK_GIVE(hdw->ctl_lock); 33862306a36Sopenharmony_ci#endif 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci /* Mike Isely <isely@pobox.com> 26-Jan-2006 The windows driver 34162306a36Sopenharmony_ci sends the following list of ENC_MISC commands (for both 34262306a36Sopenharmony_ci 24xxx and 29xxx devices). Meanings are not entirely clear, 34362306a36Sopenharmony_ci however without the ENC_MISC(3,1) command then we risk 34462306a36Sopenharmony_ci random perpetual video corruption whenever the video input 34562306a36Sopenharmony_ci breaks up for a moment (like when switching channels). */ 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci#if 0 34962306a36Sopenharmony_ci /* This ENC_MISC(5,0) command seems to hurt 29xxx sync 35062306a36Sopenharmony_ci performance on channel changes, but is not a problem on 35162306a36Sopenharmony_ci 24xxx devices. */ 35262306a36Sopenharmony_ci ret |= pvr2_encoder_vcmd(hdw, CX2341X_ENC_MISC,4, 5,0,0,0); 35362306a36Sopenharmony_ci#endif 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci /* This ENC_MISC(3,encMisc3Arg) command is critical - without 35662306a36Sopenharmony_ci it there will eventually be video corruption. Also, the 35762306a36Sopenharmony_ci saa7115 case is strange - the Windows driver is passing 1 35862306a36Sopenharmony_ci regardless of device type but if we have 1 for saa7115 35962306a36Sopenharmony_ci devices the video turns sluggish. */ 36062306a36Sopenharmony_ci if (hdw->hdw_desc->flag_has_cx25840) { 36162306a36Sopenharmony_ci encMisc3Arg = 1; 36262306a36Sopenharmony_ci } else { 36362306a36Sopenharmony_ci encMisc3Arg = 0; 36462306a36Sopenharmony_ci } 36562306a36Sopenharmony_ci ret |= pvr2_encoder_vcmd(hdw, CX2341X_ENC_MISC,4, 3, 36662306a36Sopenharmony_ci encMisc3Arg,0,0); 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci ret |= pvr2_encoder_vcmd(hdw, CX2341X_ENC_MISC,4, 8,0,0,0); 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci#if 0 37162306a36Sopenharmony_ci /* This ENC_MISC(4,1) command is poisonous, so it is commented 37262306a36Sopenharmony_ci out. But I'm leaving it here anyway to document its 37362306a36Sopenharmony_ci existence in the Windows driver. The effect of this 37462306a36Sopenharmony_ci command is that apps displaying the stream become sluggish 37562306a36Sopenharmony_ci with stuttering video. */ 37662306a36Sopenharmony_ci ret |= pvr2_encoder_vcmd(hdw, CX2341X_ENC_MISC,4, 4,1,0,0); 37762306a36Sopenharmony_ci#endif 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci ret |= pvr2_encoder_vcmd(hdw, CX2341X_ENC_MISC,4, 0,3,0,0); 38062306a36Sopenharmony_ci ret |= pvr2_encoder_vcmd(hdw, CX2341X_ENC_MISC,4,15,0,0,0); 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci /* prevent the PTSs from slowly drifting away in the generated 38362306a36Sopenharmony_ci MPEG stream */ 38462306a36Sopenharmony_ci ret |= pvr2_encoder_vcmd(hdw, CX2341X_ENC_MISC, 2, 4, 1); 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci return ret; 38762306a36Sopenharmony_ci} 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ciint pvr2_encoder_adjust(struct pvr2_hdw *hdw) 39062306a36Sopenharmony_ci{ 39162306a36Sopenharmony_ci int ret; 39262306a36Sopenharmony_ci ret = cx2341x_update(hdw,pvr2_encoder_cmd, 39362306a36Sopenharmony_ci (hdw->enc_cur_valid ? &hdw->enc_cur_state : NULL), 39462306a36Sopenharmony_ci &hdw->enc_ctl_state); 39562306a36Sopenharmony_ci if (ret) { 39662306a36Sopenharmony_ci pvr2_trace(PVR2_TRACE_ERROR_LEGS, 39762306a36Sopenharmony_ci "Error from cx2341x module code=%d",ret); 39862306a36Sopenharmony_ci } else { 39962306a36Sopenharmony_ci hdw->enc_cur_state = hdw->enc_ctl_state; 40062306a36Sopenharmony_ci hdw->enc_cur_valid = !0; 40162306a36Sopenharmony_ci } 40262306a36Sopenharmony_ci return ret; 40362306a36Sopenharmony_ci} 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ciint pvr2_encoder_configure(struct pvr2_hdw *hdw) 40762306a36Sopenharmony_ci{ 40862306a36Sopenharmony_ci int ret; 40962306a36Sopenharmony_ci int val; 41062306a36Sopenharmony_ci pvr2_trace(PVR2_TRACE_ENCODER, "pvr2_encoder_configure (cx2341x module)"); 41162306a36Sopenharmony_ci hdw->enc_ctl_state.port = CX2341X_PORT_STREAMING; 41262306a36Sopenharmony_ci hdw->enc_ctl_state.width = hdw->res_hor_val; 41362306a36Sopenharmony_ci hdw->enc_ctl_state.height = hdw->res_ver_val; 41462306a36Sopenharmony_ci hdw->enc_ctl_state.is_50hz = ((hdw->std_mask_cur & V4L2_STD_525_60) ? 41562306a36Sopenharmony_ci 0 : 1); 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci ret = 0; 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci ret |= pvr2_encoder_prep_config(hdw); 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci /* saa7115: 0xf0 */ 42262306a36Sopenharmony_ci val = 0xf0; 42362306a36Sopenharmony_ci if (hdw->hdw_desc->flag_has_cx25840) { 42462306a36Sopenharmony_ci /* ivtv cx25840: 0x140 */ 42562306a36Sopenharmony_ci val = 0x140; 42662306a36Sopenharmony_ci } 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci if (!ret) ret = pvr2_encoder_vcmd( 42962306a36Sopenharmony_ci hdw,CX2341X_ENC_SET_NUM_VSYNC_LINES, 2, 43062306a36Sopenharmony_ci val, val); 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci /* setup firmware to notify us about some events (don't know why...) */ 43362306a36Sopenharmony_ci if (!ret) ret = pvr2_encoder_vcmd( 43462306a36Sopenharmony_ci hdw,CX2341X_ENC_SET_EVENT_NOTIFICATION, 4, 43562306a36Sopenharmony_ci 0, 0, 0x10000000, 0xffffffff); 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci if (!ret) ret = pvr2_encoder_vcmd( 43862306a36Sopenharmony_ci hdw,CX2341X_ENC_SET_VBI_LINE, 5, 43962306a36Sopenharmony_ci 0xffffffff,0,0,0,0); 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci if (ret) { 44262306a36Sopenharmony_ci pvr2_trace(PVR2_TRACE_ERROR_LEGS, 44362306a36Sopenharmony_ci "Failed to configure cx23416"); 44462306a36Sopenharmony_ci return ret; 44562306a36Sopenharmony_ci } 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci ret = pvr2_encoder_adjust(hdw); 44862306a36Sopenharmony_ci if (ret) return ret; 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci ret = pvr2_encoder_vcmd( 45162306a36Sopenharmony_ci hdw, CX2341X_ENC_INITIALIZE_INPUT, 0); 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci if (ret) { 45462306a36Sopenharmony_ci pvr2_trace(PVR2_TRACE_ERROR_LEGS, 45562306a36Sopenharmony_ci "Failed to initialize cx23416 video input"); 45662306a36Sopenharmony_ci return ret; 45762306a36Sopenharmony_ci } 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci return 0; 46062306a36Sopenharmony_ci} 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ciint pvr2_encoder_start(struct pvr2_hdw *hdw) 46462306a36Sopenharmony_ci{ 46562306a36Sopenharmony_ci int status; 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci /* unmask some interrupts */ 46862306a36Sopenharmony_ci pvr2_write_register(hdw, 0x0048, 0xbfffffff); 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci pvr2_encoder_vcmd(hdw,CX2341X_ENC_MUTE_VIDEO,1, 47162306a36Sopenharmony_ci hdw->input_val == PVR2_CVAL_INPUT_RADIO ? 1 : 0); 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci switch (hdw->active_stream_type) { 47462306a36Sopenharmony_ci case pvr2_config_vbi: 47562306a36Sopenharmony_ci status = pvr2_encoder_vcmd(hdw,CX2341X_ENC_START_CAPTURE,2, 47662306a36Sopenharmony_ci 0x01,0x14); 47762306a36Sopenharmony_ci break; 47862306a36Sopenharmony_ci case pvr2_config_mpeg: 47962306a36Sopenharmony_ci status = pvr2_encoder_vcmd(hdw,CX2341X_ENC_START_CAPTURE,2, 48062306a36Sopenharmony_ci 0,0x13); 48162306a36Sopenharmony_ci break; 48262306a36Sopenharmony_ci default: /* Unhandled cases for now */ 48362306a36Sopenharmony_ci status = pvr2_encoder_vcmd(hdw,CX2341X_ENC_START_CAPTURE,2, 48462306a36Sopenharmony_ci 0,0x13); 48562306a36Sopenharmony_ci break; 48662306a36Sopenharmony_ci } 48762306a36Sopenharmony_ci return status; 48862306a36Sopenharmony_ci} 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ciint pvr2_encoder_stop(struct pvr2_hdw *hdw) 49162306a36Sopenharmony_ci{ 49262306a36Sopenharmony_ci int status; 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci /* mask all interrupts */ 49562306a36Sopenharmony_ci pvr2_write_register(hdw, 0x0048, 0xffffffff); 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_ci switch (hdw->active_stream_type) { 49862306a36Sopenharmony_ci case pvr2_config_vbi: 49962306a36Sopenharmony_ci status = pvr2_encoder_vcmd(hdw,CX2341X_ENC_STOP_CAPTURE,3, 50062306a36Sopenharmony_ci 0x01,0x01,0x14); 50162306a36Sopenharmony_ci break; 50262306a36Sopenharmony_ci case pvr2_config_mpeg: 50362306a36Sopenharmony_ci status = pvr2_encoder_vcmd(hdw,CX2341X_ENC_STOP_CAPTURE,3, 50462306a36Sopenharmony_ci 0x01,0,0x13); 50562306a36Sopenharmony_ci break; 50662306a36Sopenharmony_ci default: /* Unhandled cases for now */ 50762306a36Sopenharmony_ci status = pvr2_encoder_vcmd(hdw,CX2341X_ENC_STOP_CAPTURE,3, 50862306a36Sopenharmony_ci 0x01,0,0x13); 50962306a36Sopenharmony_ci break; 51062306a36Sopenharmony_ci } 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci return status; 51362306a36Sopenharmony_ci} 514