162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Support for a cx23416 mpeg encoder via cx2388x host port. 462306a36Sopenharmony_ci * "blackbird" reference design. 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * (c) 2004 Jelle Foks <jelle@foks.us> 762306a36Sopenharmony_ci * (c) 2004 Gerd Knorr <kraxel@bytesex.org> 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * (c) 2005-2006 Mauro Carvalho Chehab <mchehab@kernel.org> 1062306a36Sopenharmony_ci * - video_ioctl2 conversion 1162306a36Sopenharmony_ci * 1262306a36Sopenharmony_ci * Includes parts from the ivtv driver <http://sourceforge.net/projects/ivtv/> 1362306a36Sopenharmony_ci */ 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#include "cx88.h" 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#include <linux/module.h> 1862306a36Sopenharmony_ci#include <linux/init.h> 1962306a36Sopenharmony_ci#include <linux/slab.h> 2062306a36Sopenharmony_ci#include <linux/fs.h> 2162306a36Sopenharmony_ci#include <linux/delay.h> 2262306a36Sopenharmony_ci#include <linux/device.h> 2362306a36Sopenharmony_ci#include <linux/firmware.h> 2462306a36Sopenharmony_ci#include <media/v4l2-common.h> 2562306a36Sopenharmony_ci#include <media/v4l2-ioctl.h> 2662306a36Sopenharmony_ci#include <media/v4l2-event.h> 2762306a36Sopenharmony_ci#include <media/drv-intf/cx2341x.h> 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ciMODULE_DESCRIPTION("driver for cx2388x/cx23416 based mpeg encoder cards"); 3062306a36Sopenharmony_ciMODULE_AUTHOR("Jelle Foks <jelle@foks.us>, Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]"); 3162306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 3262306a36Sopenharmony_ciMODULE_VERSION(CX88_VERSION); 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_cistatic unsigned int debug; 3562306a36Sopenharmony_cimodule_param(debug, int, 0644); 3662306a36Sopenharmony_ciMODULE_PARM_DESC(debug, "enable debug messages [blackbird]"); 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci#define dprintk(level, fmt, arg...) do { \ 3962306a36Sopenharmony_ci if (debug + 1 > level) \ 4062306a36Sopenharmony_ci printk(KERN_DEBUG pr_fmt("%s: blackbird:" fmt), \ 4162306a36Sopenharmony_ci __func__, ##arg); \ 4262306a36Sopenharmony_ci} while (0) 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci/* ------------------------------------------------------------------ */ 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci#define BLACKBIRD_FIRM_IMAGE_SIZE 376836 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci/* defines below are from ivtv-driver.h */ 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci#define IVTV_CMD_HW_BLOCKS_RST 0xFFFFFFFF 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci/* Firmware API commands */ 5362306a36Sopenharmony_ci#define IVTV_API_STD_TIMEOUT 500 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_cienum blackbird_capture_type { 5662306a36Sopenharmony_ci BLACKBIRD_MPEG_CAPTURE, 5762306a36Sopenharmony_ci BLACKBIRD_RAW_CAPTURE, 5862306a36Sopenharmony_ci BLACKBIRD_RAW_PASSTHRU_CAPTURE 5962306a36Sopenharmony_ci}; 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_cienum blackbird_capture_bits { 6262306a36Sopenharmony_ci BLACKBIRD_RAW_BITS_NONE = 0x00, 6362306a36Sopenharmony_ci BLACKBIRD_RAW_BITS_YUV_CAPTURE = 0x01, 6462306a36Sopenharmony_ci BLACKBIRD_RAW_BITS_PCM_CAPTURE = 0x02, 6562306a36Sopenharmony_ci BLACKBIRD_RAW_BITS_VBI_CAPTURE = 0x04, 6662306a36Sopenharmony_ci BLACKBIRD_RAW_BITS_PASSTHRU_CAPTURE = 0x08, 6762306a36Sopenharmony_ci BLACKBIRD_RAW_BITS_TO_HOST_CAPTURE = 0x10 6862306a36Sopenharmony_ci}; 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_cienum blackbird_capture_end { 7162306a36Sopenharmony_ci BLACKBIRD_END_AT_GOP, /* stop at the end of gop, generate irq */ 7262306a36Sopenharmony_ci BLACKBIRD_END_NOW, /* stop immediately, no irq */ 7362306a36Sopenharmony_ci}; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_cienum blackbird_framerate { 7662306a36Sopenharmony_ci BLACKBIRD_FRAMERATE_NTSC_30, /* NTSC: 30fps */ 7762306a36Sopenharmony_ci BLACKBIRD_FRAMERATE_PAL_25 /* PAL: 25fps */ 7862306a36Sopenharmony_ci}; 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_cienum blackbird_stream_port { 8162306a36Sopenharmony_ci BLACKBIRD_OUTPUT_PORT_MEMORY, 8262306a36Sopenharmony_ci BLACKBIRD_OUTPUT_PORT_STREAMING, 8362306a36Sopenharmony_ci BLACKBIRD_OUTPUT_PORT_SERIAL 8462306a36Sopenharmony_ci}; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_cienum blackbird_data_xfer_status { 8762306a36Sopenharmony_ci BLACKBIRD_MORE_BUFFERS_FOLLOW, 8862306a36Sopenharmony_ci BLACKBIRD_LAST_BUFFER, 8962306a36Sopenharmony_ci}; 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_cienum blackbird_picture_mask { 9262306a36Sopenharmony_ci BLACKBIRD_PICTURE_MASK_NONE, 9362306a36Sopenharmony_ci BLACKBIRD_PICTURE_MASK_I_FRAMES, 9462306a36Sopenharmony_ci BLACKBIRD_PICTURE_MASK_I_P_FRAMES = 0x3, 9562306a36Sopenharmony_ci BLACKBIRD_PICTURE_MASK_ALL_FRAMES = 0x7, 9662306a36Sopenharmony_ci}; 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_cienum blackbird_vbi_mode_bits { 9962306a36Sopenharmony_ci BLACKBIRD_VBI_BITS_SLICED, 10062306a36Sopenharmony_ci BLACKBIRD_VBI_BITS_RAW, 10162306a36Sopenharmony_ci}; 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_cienum blackbird_vbi_insertion_bits { 10462306a36Sopenharmony_ci BLACKBIRD_VBI_BITS_INSERT_IN_XTENSION_USR_DATA, 10562306a36Sopenharmony_ci BLACKBIRD_VBI_BITS_INSERT_IN_PRIVATE_PACKETS = 0x1 << 1, 10662306a36Sopenharmony_ci BLACKBIRD_VBI_BITS_SEPARATE_STREAM = 0x2 << 1, 10762306a36Sopenharmony_ci BLACKBIRD_VBI_BITS_SEPARATE_STREAM_USR_DATA = 0x4 << 1, 10862306a36Sopenharmony_ci BLACKBIRD_VBI_BITS_SEPARATE_STREAM_PRV_DATA = 0x5 << 1, 10962306a36Sopenharmony_ci}; 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_cienum blackbird_dma_unit { 11262306a36Sopenharmony_ci BLACKBIRD_DMA_BYTES, 11362306a36Sopenharmony_ci BLACKBIRD_DMA_FRAMES, 11462306a36Sopenharmony_ci}; 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_cienum blackbird_dma_transfer_status_bits { 11762306a36Sopenharmony_ci BLACKBIRD_DMA_TRANSFER_BITS_DONE = 0x01, 11862306a36Sopenharmony_ci BLACKBIRD_DMA_TRANSFER_BITS_ERROR = 0x04, 11962306a36Sopenharmony_ci BLACKBIRD_DMA_TRANSFER_BITS_LL_ERROR = 0x10, 12062306a36Sopenharmony_ci}; 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_cienum blackbird_pause { 12362306a36Sopenharmony_ci BLACKBIRD_PAUSE_ENCODING, 12462306a36Sopenharmony_ci BLACKBIRD_RESUME_ENCODING, 12562306a36Sopenharmony_ci}; 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_cienum blackbird_copyright { 12862306a36Sopenharmony_ci BLACKBIRD_COPYRIGHT_OFF, 12962306a36Sopenharmony_ci BLACKBIRD_COPYRIGHT_ON, 13062306a36Sopenharmony_ci}; 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_cienum blackbird_notification_type { 13362306a36Sopenharmony_ci BLACKBIRD_NOTIFICATION_REFRESH, 13462306a36Sopenharmony_ci}; 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_cienum blackbird_notification_status { 13762306a36Sopenharmony_ci BLACKBIRD_NOTIFICATION_OFF, 13862306a36Sopenharmony_ci BLACKBIRD_NOTIFICATION_ON, 13962306a36Sopenharmony_ci}; 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_cienum blackbird_notification_mailbox { 14262306a36Sopenharmony_ci BLACKBIRD_NOTIFICATION_NO_MAILBOX = -1, 14362306a36Sopenharmony_ci}; 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_cienum blackbird_field1_lines { 14662306a36Sopenharmony_ci BLACKBIRD_FIELD1_SAA7114 = 0x00EF, /* 239 */ 14762306a36Sopenharmony_ci BLACKBIRD_FIELD1_SAA7115 = 0x00F0, /* 240 */ 14862306a36Sopenharmony_ci BLACKBIRD_FIELD1_MICRONAS = 0x0105, /* 261 */ 14962306a36Sopenharmony_ci}; 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_cienum blackbird_field2_lines { 15262306a36Sopenharmony_ci BLACKBIRD_FIELD2_SAA7114 = 0x00EF, /* 239 */ 15362306a36Sopenharmony_ci BLACKBIRD_FIELD2_SAA7115 = 0x00F0, /* 240 */ 15462306a36Sopenharmony_ci BLACKBIRD_FIELD2_MICRONAS = 0x0106, /* 262 */ 15562306a36Sopenharmony_ci}; 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_cienum blackbird_custom_data_type { 15862306a36Sopenharmony_ci BLACKBIRD_CUSTOM_EXTENSION_USR_DATA, 15962306a36Sopenharmony_ci BLACKBIRD_CUSTOM_PRIVATE_PACKET, 16062306a36Sopenharmony_ci}; 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_cienum blackbird_mute { 16362306a36Sopenharmony_ci BLACKBIRD_UNMUTE, 16462306a36Sopenharmony_ci BLACKBIRD_MUTE, 16562306a36Sopenharmony_ci}; 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_cienum blackbird_mute_video_mask { 16862306a36Sopenharmony_ci BLACKBIRD_MUTE_VIDEO_V_MASK = 0x0000FF00, 16962306a36Sopenharmony_ci BLACKBIRD_MUTE_VIDEO_U_MASK = 0x00FF0000, 17062306a36Sopenharmony_ci BLACKBIRD_MUTE_VIDEO_Y_MASK = 0xFF000000, 17162306a36Sopenharmony_ci}; 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_cienum blackbird_mute_video_shift { 17462306a36Sopenharmony_ci BLACKBIRD_MUTE_VIDEO_V_SHIFT = 8, 17562306a36Sopenharmony_ci BLACKBIRD_MUTE_VIDEO_U_SHIFT = 16, 17662306a36Sopenharmony_ci BLACKBIRD_MUTE_VIDEO_Y_SHIFT = 24, 17762306a36Sopenharmony_ci}; 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci/* Registers */ 18062306a36Sopenharmony_ci#define IVTV_REG_ENC_SDRAM_REFRESH (0x07F8 /*| IVTV_REG_OFFSET*/) 18162306a36Sopenharmony_ci#define IVTV_REG_ENC_SDRAM_PRECHARGE (0x07FC /*| IVTV_REG_OFFSET*/) 18262306a36Sopenharmony_ci#define IVTV_REG_SPU (0x9050 /*| IVTV_REG_OFFSET*/) 18362306a36Sopenharmony_ci#define IVTV_REG_HW_BLOCKS (0x9054 /*| IVTV_REG_OFFSET*/) 18462306a36Sopenharmony_ci#define IVTV_REG_VPU (0x9058 /*| IVTV_REG_OFFSET*/) 18562306a36Sopenharmony_ci#define IVTV_REG_APU (0xA064 /*| IVTV_REG_OFFSET*/) 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci/* ------------------------------------------------------------------ */ 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_cistatic void host_setup(struct cx88_core *core) 19062306a36Sopenharmony_ci{ 19162306a36Sopenharmony_ci /* toggle reset of the host */ 19262306a36Sopenharmony_ci cx_write(MO_GPHST_SOFT_RST, 1); 19362306a36Sopenharmony_ci udelay(100); 19462306a36Sopenharmony_ci cx_write(MO_GPHST_SOFT_RST, 0); 19562306a36Sopenharmony_ci udelay(100); 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci /* host port setup */ 19862306a36Sopenharmony_ci cx_write(MO_GPHST_WSC, 0x44444444U); 19962306a36Sopenharmony_ci cx_write(MO_GPHST_XFR, 0); 20062306a36Sopenharmony_ci cx_write(MO_GPHST_WDTH, 15); 20162306a36Sopenharmony_ci cx_write(MO_GPHST_HDSHK, 0); 20262306a36Sopenharmony_ci cx_write(MO_GPHST_MUX16, 0x44448888U); 20362306a36Sopenharmony_ci cx_write(MO_GPHST_MODE, 0); 20462306a36Sopenharmony_ci} 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci/* ------------------------------------------------------------------ */ 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci#define P1_MDATA0 0x390000 20962306a36Sopenharmony_ci#define P1_MDATA1 0x390001 21062306a36Sopenharmony_ci#define P1_MDATA2 0x390002 21162306a36Sopenharmony_ci#define P1_MDATA3 0x390003 21262306a36Sopenharmony_ci#define P1_MADDR2 0x390004 21362306a36Sopenharmony_ci#define P1_MADDR1 0x390005 21462306a36Sopenharmony_ci#define P1_MADDR0 0x390006 21562306a36Sopenharmony_ci#define P1_RDATA0 0x390008 21662306a36Sopenharmony_ci#define P1_RDATA1 0x390009 21762306a36Sopenharmony_ci#define P1_RDATA2 0x39000A 21862306a36Sopenharmony_ci#define P1_RDATA3 0x39000B 21962306a36Sopenharmony_ci#define P1_RADDR0 0x39000C 22062306a36Sopenharmony_ci#define P1_RADDR1 0x39000D 22162306a36Sopenharmony_ci#define P1_RRDWR 0x39000E 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_cistatic int wait_ready_gpio0_bit1(struct cx88_core *core, u32 state) 22462306a36Sopenharmony_ci{ 22562306a36Sopenharmony_ci unsigned long timeout = jiffies + msecs_to_jiffies(1); 22662306a36Sopenharmony_ci u32 gpio0, need; 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci need = state ? 2 : 0; 22962306a36Sopenharmony_ci for (;;) { 23062306a36Sopenharmony_ci gpio0 = cx_read(MO_GP0_IO) & 2; 23162306a36Sopenharmony_ci if (need == gpio0) 23262306a36Sopenharmony_ci return 0; 23362306a36Sopenharmony_ci if (time_after(jiffies, timeout)) 23462306a36Sopenharmony_ci return -1; 23562306a36Sopenharmony_ci udelay(1); 23662306a36Sopenharmony_ci } 23762306a36Sopenharmony_ci} 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_cistatic int memory_write(struct cx88_core *core, u32 address, u32 value) 24062306a36Sopenharmony_ci{ 24162306a36Sopenharmony_ci /* Warning: address is dword address (4 bytes) */ 24262306a36Sopenharmony_ci cx_writeb(P1_MDATA0, (unsigned int)value); 24362306a36Sopenharmony_ci cx_writeb(P1_MDATA1, (unsigned int)(value >> 8)); 24462306a36Sopenharmony_ci cx_writeb(P1_MDATA2, (unsigned int)(value >> 16)); 24562306a36Sopenharmony_ci cx_writeb(P1_MDATA3, (unsigned int)(value >> 24)); 24662306a36Sopenharmony_ci cx_writeb(P1_MADDR2, (unsigned int)(address >> 16) | 0x40); 24762306a36Sopenharmony_ci cx_writeb(P1_MADDR1, (unsigned int)(address >> 8)); 24862306a36Sopenharmony_ci cx_writeb(P1_MADDR0, (unsigned int)address); 24962306a36Sopenharmony_ci cx_read(P1_MDATA0); 25062306a36Sopenharmony_ci cx_read(P1_MADDR0); 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci return wait_ready_gpio0_bit1(core, 1); 25362306a36Sopenharmony_ci} 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_cistatic int memory_read(struct cx88_core *core, u32 address, u32 *value) 25662306a36Sopenharmony_ci{ 25762306a36Sopenharmony_ci int retval; 25862306a36Sopenharmony_ci u32 val; 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci /* Warning: address is dword address (4 bytes) */ 26162306a36Sopenharmony_ci cx_writeb(P1_MADDR2, (unsigned int)(address >> 16) & ~0xC0); 26262306a36Sopenharmony_ci cx_writeb(P1_MADDR1, (unsigned int)(address >> 8)); 26362306a36Sopenharmony_ci cx_writeb(P1_MADDR0, (unsigned int)address); 26462306a36Sopenharmony_ci cx_read(P1_MADDR0); 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci retval = wait_ready_gpio0_bit1(core, 1); 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci cx_writeb(P1_MDATA3, 0); 26962306a36Sopenharmony_ci val = (unsigned char)cx_read(P1_MDATA3) << 24; 27062306a36Sopenharmony_ci cx_writeb(P1_MDATA2, 0); 27162306a36Sopenharmony_ci val |= (unsigned char)cx_read(P1_MDATA2) << 16; 27262306a36Sopenharmony_ci cx_writeb(P1_MDATA1, 0); 27362306a36Sopenharmony_ci val |= (unsigned char)cx_read(P1_MDATA1) << 8; 27462306a36Sopenharmony_ci cx_writeb(P1_MDATA0, 0); 27562306a36Sopenharmony_ci val |= (unsigned char)cx_read(P1_MDATA0); 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci *value = val; 27862306a36Sopenharmony_ci return retval; 27962306a36Sopenharmony_ci} 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_cistatic int register_write(struct cx88_core *core, u32 address, u32 value) 28262306a36Sopenharmony_ci{ 28362306a36Sopenharmony_ci cx_writeb(P1_RDATA0, (unsigned int)value); 28462306a36Sopenharmony_ci cx_writeb(P1_RDATA1, (unsigned int)(value >> 8)); 28562306a36Sopenharmony_ci cx_writeb(P1_RDATA2, (unsigned int)(value >> 16)); 28662306a36Sopenharmony_ci cx_writeb(P1_RDATA3, (unsigned int)(value >> 24)); 28762306a36Sopenharmony_ci cx_writeb(P1_RADDR0, (unsigned int)address); 28862306a36Sopenharmony_ci cx_writeb(P1_RADDR1, (unsigned int)(address >> 8)); 28962306a36Sopenharmony_ci cx_writeb(P1_RRDWR, 1); 29062306a36Sopenharmony_ci cx_read(P1_RDATA0); 29162306a36Sopenharmony_ci cx_read(P1_RADDR0); 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci return wait_ready_gpio0_bit1(core, 1); 29462306a36Sopenharmony_ci} 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_cistatic int register_read(struct cx88_core *core, u32 address, u32 *value) 29762306a36Sopenharmony_ci{ 29862306a36Sopenharmony_ci int retval; 29962306a36Sopenharmony_ci u32 val; 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci cx_writeb(P1_RADDR0, (unsigned int)address); 30262306a36Sopenharmony_ci cx_writeb(P1_RADDR1, (unsigned int)(address >> 8)); 30362306a36Sopenharmony_ci cx_writeb(P1_RRDWR, 0); 30462306a36Sopenharmony_ci cx_read(P1_RADDR0); 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci retval = wait_ready_gpio0_bit1(core, 1); 30762306a36Sopenharmony_ci val = (unsigned char)cx_read(P1_RDATA0); 30862306a36Sopenharmony_ci val |= (unsigned char)cx_read(P1_RDATA1) << 8; 30962306a36Sopenharmony_ci val |= (unsigned char)cx_read(P1_RDATA2) << 16; 31062306a36Sopenharmony_ci val |= (unsigned char)cx_read(P1_RDATA3) << 24; 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci *value = val; 31362306a36Sopenharmony_ci return retval; 31462306a36Sopenharmony_ci} 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci/* ------------------------------------------------------------------ */ 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_cistatic int blackbird_mbox_func(void *priv, u32 command, int in, 31962306a36Sopenharmony_ci int out, u32 data[CX2341X_MBOX_MAX_DATA]) 32062306a36Sopenharmony_ci{ 32162306a36Sopenharmony_ci struct cx8802_dev *dev = priv; 32262306a36Sopenharmony_ci unsigned long timeout; 32362306a36Sopenharmony_ci u32 value, flag, retval; 32462306a36Sopenharmony_ci int i; 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci dprintk(1, "%s: 0x%X\n", __func__, command); 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci /* 32962306a36Sopenharmony_ci * this may not be 100% safe if we can't read any memory location 33062306a36Sopenharmony_ci * without side effects 33162306a36Sopenharmony_ci */ 33262306a36Sopenharmony_ci memory_read(dev->core, dev->mailbox - 4, &value); 33362306a36Sopenharmony_ci if (value != 0x12345678) { 33462306a36Sopenharmony_ci dprintk(0, 33562306a36Sopenharmony_ci "Firmware and/or mailbox pointer not initialized or corrupted\n"); 33662306a36Sopenharmony_ci return -EIO; 33762306a36Sopenharmony_ci } 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci memory_read(dev->core, dev->mailbox, &flag); 34062306a36Sopenharmony_ci if (flag) { 34162306a36Sopenharmony_ci dprintk(0, "ERROR: Mailbox appears to be in use (%x)\n", flag); 34262306a36Sopenharmony_ci return -EIO; 34362306a36Sopenharmony_ci } 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci flag |= 1; /* tell 'em we're working on it */ 34662306a36Sopenharmony_ci memory_write(dev->core, dev->mailbox, flag); 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci /* write command + args + fill remaining with zeros */ 34962306a36Sopenharmony_ci memory_write(dev->core, dev->mailbox + 1, command); /* command code */ 35062306a36Sopenharmony_ci /* timeout */ 35162306a36Sopenharmony_ci memory_write(dev->core, dev->mailbox + 3, IVTV_API_STD_TIMEOUT); 35262306a36Sopenharmony_ci for (i = 0; i < in; i++) { 35362306a36Sopenharmony_ci memory_write(dev->core, dev->mailbox + 4 + i, data[i]); 35462306a36Sopenharmony_ci dprintk(1, "API Input %d = %d\n", i, data[i]); 35562306a36Sopenharmony_ci } 35662306a36Sopenharmony_ci for (; i < CX2341X_MBOX_MAX_DATA; i++) 35762306a36Sopenharmony_ci memory_write(dev->core, dev->mailbox + 4 + i, 0); 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci flag |= 3; /* tell 'em we're done writing */ 36062306a36Sopenharmony_ci memory_write(dev->core, dev->mailbox, flag); 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci /* wait for firmware to handle the API command */ 36362306a36Sopenharmony_ci timeout = jiffies + msecs_to_jiffies(1000); 36462306a36Sopenharmony_ci for (;;) { 36562306a36Sopenharmony_ci memory_read(dev->core, dev->mailbox, &flag); 36662306a36Sopenharmony_ci if (0 != (flag & 4)) 36762306a36Sopenharmony_ci break; 36862306a36Sopenharmony_ci if (time_after(jiffies, timeout)) { 36962306a36Sopenharmony_ci dprintk(0, "ERROR: API Mailbox timeout %x\n", command); 37062306a36Sopenharmony_ci return -EIO; 37162306a36Sopenharmony_ci } 37262306a36Sopenharmony_ci udelay(10); 37362306a36Sopenharmony_ci } 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci /* read output values */ 37662306a36Sopenharmony_ci for (i = 0; i < out; i++) { 37762306a36Sopenharmony_ci memory_read(dev->core, dev->mailbox + 4 + i, data + i); 37862306a36Sopenharmony_ci dprintk(1, "API Output %d = %d\n", i, data[i]); 37962306a36Sopenharmony_ci } 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci memory_read(dev->core, dev->mailbox + 2, &retval); 38262306a36Sopenharmony_ci dprintk(1, "API result = %d\n", retval); 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci flag = 0; 38562306a36Sopenharmony_ci memory_write(dev->core, dev->mailbox, flag); 38662306a36Sopenharmony_ci return retval; 38762306a36Sopenharmony_ci} 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci/* ------------------------------------------------------------------ */ 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci/* 39262306a36Sopenharmony_ci * We don't need to call the API often, so using just one mailbox 39362306a36Sopenharmony_ci * will probably suffice 39462306a36Sopenharmony_ci */ 39562306a36Sopenharmony_cistatic int blackbird_api_cmd(struct cx8802_dev *dev, u32 command, 39662306a36Sopenharmony_ci u32 inputcnt, u32 outputcnt, ...) 39762306a36Sopenharmony_ci{ 39862306a36Sopenharmony_ci u32 data[CX2341X_MBOX_MAX_DATA]; 39962306a36Sopenharmony_ci va_list vargs; 40062306a36Sopenharmony_ci int i, err; 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci va_start(vargs, outputcnt); 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci for (i = 0; i < inputcnt; i++) 40562306a36Sopenharmony_ci data[i] = va_arg(vargs, int); 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci err = blackbird_mbox_func(dev, command, inputcnt, outputcnt, data); 40862306a36Sopenharmony_ci for (i = 0; i < outputcnt; i++) { 40962306a36Sopenharmony_ci int *vptr = va_arg(vargs, int *); 41062306a36Sopenharmony_ci *vptr = data[i]; 41162306a36Sopenharmony_ci } 41262306a36Sopenharmony_ci va_end(vargs); 41362306a36Sopenharmony_ci return err; 41462306a36Sopenharmony_ci} 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_cistatic int blackbird_find_mailbox(struct cx8802_dev *dev) 41762306a36Sopenharmony_ci{ 41862306a36Sopenharmony_ci u32 signature[4] = {0x12345678, 0x34567812, 0x56781234, 0x78123456}; 41962306a36Sopenharmony_ci int signaturecnt = 0; 42062306a36Sopenharmony_ci u32 value; 42162306a36Sopenharmony_ci int i; 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci for (i = 0; i < BLACKBIRD_FIRM_IMAGE_SIZE; i++) { 42462306a36Sopenharmony_ci memory_read(dev->core, i, &value); 42562306a36Sopenharmony_ci if (value == signature[signaturecnt]) 42662306a36Sopenharmony_ci signaturecnt++; 42762306a36Sopenharmony_ci else 42862306a36Sopenharmony_ci signaturecnt = 0; 42962306a36Sopenharmony_ci if (signaturecnt == 4) { 43062306a36Sopenharmony_ci dprintk(1, "Mailbox signature found\n"); 43162306a36Sopenharmony_ci return i + 1; 43262306a36Sopenharmony_ci } 43362306a36Sopenharmony_ci } 43462306a36Sopenharmony_ci dprintk(0, "Mailbox signature values not found!\n"); 43562306a36Sopenharmony_ci return -EIO; 43662306a36Sopenharmony_ci} 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_cistatic int blackbird_load_firmware(struct cx8802_dev *dev) 43962306a36Sopenharmony_ci{ 44062306a36Sopenharmony_ci static const unsigned char magic[8] = { 44162306a36Sopenharmony_ci 0xa7, 0x0d, 0x00, 0x00, 0x66, 0xbb, 0x55, 0xaa 44262306a36Sopenharmony_ci }; 44362306a36Sopenharmony_ci const struct firmware *firmware; 44462306a36Sopenharmony_ci int i, retval = 0; 44562306a36Sopenharmony_ci u32 value = 0; 44662306a36Sopenharmony_ci u32 checksum = 0; 44762306a36Sopenharmony_ci __le32 *dataptr; 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci retval = register_write(dev->core, IVTV_REG_VPU, 0xFFFFFFED); 45062306a36Sopenharmony_ci retval |= register_write(dev->core, IVTV_REG_HW_BLOCKS, 45162306a36Sopenharmony_ci IVTV_CMD_HW_BLOCKS_RST); 45262306a36Sopenharmony_ci retval |= register_write(dev->core, IVTV_REG_ENC_SDRAM_REFRESH, 45362306a36Sopenharmony_ci 0x80000640); 45462306a36Sopenharmony_ci retval |= register_write(dev->core, IVTV_REG_ENC_SDRAM_PRECHARGE, 45562306a36Sopenharmony_ci 0x1A); 45662306a36Sopenharmony_ci usleep_range(10000, 20000); 45762306a36Sopenharmony_ci retval |= register_write(dev->core, IVTV_REG_APU, 0); 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci if (retval < 0) 46062306a36Sopenharmony_ci dprintk(0, "Error with register_write\n"); 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci retval = request_firmware(&firmware, CX2341X_FIRM_ENC_FILENAME, 46362306a36Sopenharmony_ci &dev->pci->dev); 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci if (retval != 0) { 46662306a36Sopenharmony_ci pr_err("Hotplug firmware request failed (%s).\n", 46762306a36Sopenharmony_ci CX2341X_FIRM_ENC_FILENAME); 46862306a36Sopenharmony_ci pr_err("Please fix your hotplug setup, the board will not work without firmware loaded!\n"); 46962306a36Sopenharmony_ci return -EIO; 47062306a36Sopenharmony_ci } 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci if (firmware->size != BLACKBIRD_FIRM_IMAGE_SIZE) { 47362306a36Sopenharmony_ci pr_err("Firmware size mismatch (have %zd, expected %d)\n", 47462306a36Sopenharmony_ci firmware->size, BLACKBIRD_FIRM_IMAGE_SIZE); 47562306a36Sopenharmony_ci release_firmware(firmware); 47662306a36Sopenharmony_ci return -EINVAL; 47762306a36Sopenharmony_ci } 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci if (memcmp(firmware->data, magic, 8) != 0) { 48062306a36Sopenharmony_ci pr_err("Firmware magic mismatch, wrong file?\n"); 48162306a36Sopenharmony_ci release_firmware(firmware); 48262306a36Sopenharmony_ci return -EINVAL; 48362306a36Sopenharmony_ci } 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci /* transfer to the chip */ 48662306a36Sopenharmony_ci dprintk(1, "Loading firmware ...\n"); 48762306a36Sopenharmony_ci dataptr = (__le32 *)firmware->data; 48862306a36Sopenharmony_ci for (i = 0; i < (firmware->size >> 2); i++) { 48962306a36Sopenharmony_ci value = le32_to_cpu(*dataptr); 49062306a36Sopenharmony_ci checksum += ~value; 49162306a36Sopenharmony_ci memory_write(dev->core, i, value); 49262306a36Sopenharmony_ci dataptr++; 49362306a36Sopenharmony_ci } 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ci /* read back to verify with the checksum */ 49662306a36Sopenharmony_ci for (i--; i >= 0; i--) { 49762306a36Sopenharmony_ci memory_read(dev->core, i, &value); 49862306a36Sopenharmony_ci checksum -= ~value; 49962306a36Sopenharmony_ci } 50062306a36Sopenharmony_ci release_firmware(firmware); 50162306a36Sopenharmony_ci if (checksum) { 50262306a36Sopenharmony_ci pr_err("Firmware load might have failed (checksum mismatch).\n"); 50362306a36Sopenharmony_ci return -EIO; 50462306a36Sopenharmony_ci } 50562306a36Sopenharmony_ci dprintk(0, "Firmware upload successful.\n"); 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci retval |= register_write(dev->core, IVTV_REG_HW_BLOCKS, 50862306a36Sopenharmony_ci IVTV_CMD_HW_BLOCKS_RST); 50962306a36Sopenharmony_ci retval |= register_read(dev->core, IVTV_REG_SPU, &value); 51062306a36Sopenharmony_ci retval |= register_write(dev->core, IVTV_REG_SPU, value & 0xFFFFFFFE); 51162306a36Sopenharmony_ci usleep_range(10000, 20000); 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci retval |= register_read(dev->core, IVTV_REG_VPU, &value); 51462306a36Sopenharmony_ci retval |= register_write(dev->core, IVTV_REG_VPU, value & 0xFFFFFFE8); 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci if (retval < 0) 51762306a36Sopenharmony_ci dprintk(0, "Error with register_write\n"); 51862306a36Sopenharmony_ci return 0; 51962306a36Sopenharmony_ci} 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci/* 52262306a36Sopenharmony_ci * Settings used by the windows tv app for PVR2000: 52362306a36Sopenharmony_ci * ================================================================================================================= 52462306a36Sopenharmony_ci * Profile | Codec | Resolution | CBR/VBR | Video Qlty | V. Bitrate | Frmrate | Audio Codec | A. Bitrate | A. Mode 52562306a36Sopenharmony_ci * ----------------------------------------------------------------------------------------------------------------- 52662306a36Sopenharmony_ci * MPEG-1 | MPEG1 | 352x288PAL | (CBR) | 1000:Optimal | 2000 Kbps | 25fps | MPG1 Layer2 | 224kbps | Stereo 52762306a36Sopenharmony_ci * MPEG-2 | MPEG2 | 720x576PAL | VBR | 600 :Good | 4000 Kbps | 25fps | MPG1 Layer2 | 224kbps | Stereo 52862306a36Sopenharmony_ci * VCD | MPEG1 | 352x288PAL | (CBR) | 1000:Optimal | 1150 Kbps | 25fps | MPG1 Layer2 | 224kbps | Stereo 52962306a36Sopenharmony_ci * DVD | MPEG2 | 720x576PAL | VBR | 600 :Good | 6000 Kbps | 25fps | MPG1 Layer2 | 224kbps | Stereo 53062306a36Sopenharmony_ci * DB* DVD | MPEG2 | 720x576PAL | CBR | 600 :Good | 6000 Kbps | 25fps | MPG1 Layer2 | 224kbps | Stereo 53162306a36Sopenharmony_ci * ================================================================================================================= 53262306a36Sopenharmony_ci * [*] DB: "DirectBurn" 53362306a36Sopenharmony_ci */ 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_cistatic void blackbird_codec_settings(struct cx8802_dev *dev) 53662306a36Sopenharmony_ci{ 53762306a36Sopenharmony_ci struct cx88_core *core = dev->core; 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci /* assign frame size */ 54062306a36Sopenharmony_ci blackbird_api_cmd(dev, CX2341X_ENC_SET_FRAME_SIZE, 2, 0, 54162306a36Sopenharmony_ci core->height, core->width); 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci dev->cxhdl.width = core->width; 54462306a36Sopenharmony_ci dev->cxhdl.height = core->height; 54562306a36Sopenharmony_ci cx2341x_handler_set_50hz(&dev->cxhdl, 54662306a36Sopenharmony_ci dev->core->tvnorm & V4L2_STD_625_50); 54762306a36Sopenharmony_ci cx2341x_handler_setup(&dev->cxhdl); 54862306a36Sopenharmony_ci} 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_cistatic int blackbird_initialize_codec(struct cx8802_dev *dev) 55162306a36Sopenharmony_ci{ 55262306a36Sopenharmony_ci struct cx88_core *core = dev->core; 55362306a36Sopenharmony_ci int version; 55462306a36Sopenharmony_ci int retval; 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci dprintk(1, "Initialize codec\n"); 55762306a36Sopenharmony_ci retval = blackbird_api_cmd(dev, CX2341X_ENC_PING_FW, 0, 0); /* ping */ 55862306a36Sopenharmony_ci if (retval < 0) { 55962306a36Sopenharmony_ci /* ping was not successful, reset and upload firmware */ 56062306a36Sopenharmony_ci cx_write(MO_SRST_IO, 0); /* SYS_RSTO=0 */ 56162306a36Sopenharmony_ci cx_write(MO_SRST_IO, 1); /* SYS_RSTO=1 */ 56262306a36Sopenharmony_ci retval = blackbird_load_firmware(dev); 56362306a36Sopenharmony_ci if (retval < 0) 56462306a36Sopenharmony_ci return retval; 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_ci retval = blackbird_find_mailbox(dev); 56762306a36Sopenharmony_ci if (retval < 0) 56862306a36Sopenharmony_ci return -1; 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci dev->mailbox = retval; 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci /* ping */ 57362306a36Sopenharmony_ci retval = blackbird_api_cmd(dev, CX2341X_ENC_PING_FW, 0, 0); 57462306a36Sopenharmony_ci if (retval < 0) { 57562306a36Sopenharmony_ci dprintk(0, "ERROR: Firmware ping failed!\n"); 57662306a36Sopenharmony_ci return -1; 57762306a36Sopenharmony_ci } 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci retval = blackbird_api_cmd(dev, CX2341X_ENC_GET_VERSION, 58062306a36Sopenharmony_ci 0, 1, &version); 58162306a36Sopenharmony_ci if (retval < 0) { 58262306a36Sopenharmony_ci dprintk(0, 58362306a36Sopenharmony_ci "ERROR: Firmware get encoder version failed!\n"); 58462306a36Sopenharmony_ci return -1; 58562306a36Sopenharmony_ci } 58662306a36Sopenharmony_ci dprintk(0, "Firmware version is 0x%08x\n", version); 58762306a36Sopenharmony_ci } 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_ci cx_write(MO_PINMUX_IO, 0x88); /* 656-8bit IO and enable MPEG parallel IO */ 59062306a36Sopenharmony_ci cx_clear(MO_INPUT_FORMAT, 0x100); /* chroma subcarrier lock to normal? */ 59162306a36Sopenharmony_ci cx_write(MO_VBOS_CONTROL, 0x84A00); /* no 656 mode, 8-bit pixels, disable VBI */ 59262306a36Sopenharmony_ci cx_clear(MO_OUTPUT_FORMAT, 0x0008); /* Normal Y-limits to let the mpeg encoder sync */ 59362306a36Sopenharmony_ci 59462306a36Sopenharmony_ci blackbird_codec_settings(dev); 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ci blackbird_api_cmd(dev, CX2341X_ENC_SET_NUM_VSYNC_LINES, 2, 0, 59762306a36Sopenharmony_ci BLACKBIRD_FIELD1_SAA7115, BLACKBIRD_FIELD2_SAA7115); 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_ci blackbird_api_cmd(dev, CX2341X_ENC_SET_PLACEHOLDER, 12, 0, 60062306a36Sopenharmony_ci BLACKBIRD_CUSTOM_EXTENSION_USR_DATA, 60162306a36Sopenharmony_ci 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_ci return 0; 60462306a36Sopenharmony_ci} 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_cistatic int blackbird_start_codec(struct cx8802_dev *dev) 60762306a36Sopenharmony_ci{ 60862306a36Sopenharmony_ci struct cx88_core *core = dev->core; 60962306a36Sopenharmony_ci /* start capturing to the host interface */ 61062306a36Sopenharmony_ci u32 reg; 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_ci int i; 61362306a36Sopenharmony_ci int lastchange = -1; 61462306a36Sopenharmony_ci int lastval = 0; 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ci for (i = 0; (i < 10) && (i < (lastchange + 4)); i++) { 61762306a36Sopenharmony_ci reg = cx_read(AUD_STATUS); 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_ci dprintk(1, "AUD_STATUS:%dL: 0x%x\n", i, reg); 62062306a36Sopenharmony_ci if ((reg & 0x0F) != lastval) { 62162306a36Sopenharmony_ci lastval = reg & 0x0F; 62262306a36Sopenharmony_ci lastchange = i; 62362306a36Sopenharmony_ci } 62462306a36Sopenharmony_ci msleep(100); 62562306a36Sopenharmony_ci } 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_ci /* unmute audio source */ 62862306a36Sopenharmony_ci cx_clear(AUD_VOL_CTL, (1 << 6)); 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_ci blackbird_api_cmd(dev, CX2341X_ENC_REFRESH_INPUT, 0, 0); 63162306a36Sopenharmony_ci 63262306a36Sopenharmony_ci /* initialize the video input */ 63362306a36Sopenharmony_ci blackbird_api_cmd(dev, CX2341X_ENC_INITIALIZE_INPUT, 0, 0); 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_ci cx2341x_handler_set_busy(&dev->cxhdl, 1); 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_ci /* start capturing to the host interface */ 63862306a36Sopenharmony_ci blackbird_api_cmd(dev, CX2341X_ENC_START_CAPTURE, 2, 0, 63962306a36Sopenharmony_ci BLACKBIRD_MPEG_CAPTURE, BLACKBIRD_RAW_BITS_NONE); 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_ci return 0; 64262306a36Sopenharmony_ci} 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_cistatic int blackbird_stop_codec(struct cx8802_dev *dev) 64562306a36Sopenharmony_ci{ 64662306a36Sopenharmony_ci blackbird_api_cmd(dev, CX2341X_ENC_STOP_CAPTURE, 3, 0, 64762306a36Sopenharmony_ci BLACKBIRD_END_NOW, 64862306a36Sopenharmony_ci BLACKBIRD_MPEG_CAPTURE, 64962306a36Sopenharmony_ci BLACKBIRD_RAW_BITS_NONE); 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_ci cx2341x_handler_set_busy(&dev->cxhdl, 0); 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ci return 0; 65462306a36Sopenharmony_ci} 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_ci/* ------------------------------------------------------------------ */ 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_cistatic int queue_setup(struct vb2_queue *q, 65962306a36Sopenharmony_ci unsigned int *num_buffers, unsigned int *num_planes, 66062306a36Sopenharmony_ci unsigned int sizes[], struct device *alloc_devs[]) 66162306a36Sopenharmony_ci{ 66262306a36Sopenharmony_ci struct cx8802_dev *dev = q->drv_priv; 66362306a36Sopenharmony_ci 66462306a36Sopenharmony_ci *num_planes = 1; 66562306a36Sopenharmony_ci dev->ts_packet_size = 188 * 4; 66662306a36Sopenharmony_ci dev->ts_packet_count = 32; 66762306a36Sopenharmony_ci sizes[0] = dev->ts_packet_size * dev->ts_packet_count; 66862306a36Sopenharmony_ci return 0; 66962306a36Sopenharmony_ci} 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_cistatic int buffer_prepare(struct vb2_buffer *vb) 67262306a36Sopenharmony_ci{ 67362306a36Sopenharmony_ci struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); 67462306a36Sopenharmony_ci struct cx8802_dev *dev = vb->vb2_queue->drv_priv; 67562306a36Sopenharmony_ci struct cx88_buffer *buf = container_of(vbuf, struct cx88_buffer, vb); 67662306a36Sopenharmony_ci 67762306a36Sopenharmony_ci return cx8802_buf_prepare(vb->vb2_queue, dev, buf); 67862306a36Sopenharmony_ci} 67962306a36Sopenharmony_ci 68062306a36Sopenharmony_cistatic void buffer_finish(struct vb2_buffer *vb) 68162306a36Sopenharmony_ci{ 68262306a36Sopenharmony_ci struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); 68362306a36Sopenharmony_ci struct cx8802_dev *dev = vb->vb2_queue->drv_priv; 68462306a36Sopenharmony_ci struct cx88_buffer *buf = container_of(vbuf, struct cx88_buffer, vb); 68562306a36Sopenharmony_ci struct cx88_riscmem *risc = &buf->risc; 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_ci if (risc->cpu) 68862306a36Sopenharmony_ci dma_free_coherent(&dev->pci->dev, risc->size, risc->cpu, 68962306a36Sopenharmony_ci risc->dma); 69062306a36Sopenharmony_ci memset(risc, 0, sizeof(*risc)); 69162306a36Sopenharmony_ci} 69262306a36Sopenharmony_ci 69362306a36Sopenharmony_cistatic void buffer_queue(struct vb2_buffer *vb) 69462306a36Sopenharmony_ci{ 69562306a36Sopenharmony_ci struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); 69662306a36Sopenharmony_ci struct cx8802_dev *dev = vb->vb2_queue->drv_priv; 69762306a36Sopenharmony_ci struct cx88_buffer *buf = container_of(vbuf, struct cx88_buffer, vb); 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_ci cx8802_buf_queue(dev, buf); 70062306a36Sopenharmony_ci} 70162306a36Sopenharmony_ci 70262306a36Sopenharmony_cistatic int start_streaming(struct vb2_queue *q, unsigned int count) 70362306a36Sopenharmony_ci{ 70462306a36Sopenharmony_ci struct cx8802_dev *dev = q->drv_priv; 70562306a36Sopenharmony_ci struct cx88_dmaqueue *dmaq = &dev->mpegq; 70662306a36Sopenharmony_ci struct cx8802_driver *drv; 70762306a36Sopenharmony_ci struct cx88_buffer *buf; 70862306a36Sopenharmony_ci unsigned long flags; 70962306a36Sopenharmony_ci int err; 71062306a36Sopenharmony_ci 71162306a36Sopenharmony_ci /* Make sure we can acquire the hardware */ 71262306a36Sopenharmony_ci drv = cx8802_get_driver(dev, CX88_MPEG_BLACKBIRD); 71362306a36Sopenharmony_ci if (!drv) { 71462306a36Sopenharmony_ci dprintk(1, "%s: blackbird driver is not loaded\n", __func__); 71562306a36Sopenharmony_ci err = -ENODEV; 71662306a36Sopenharmony_ci goto fail; 71762306a36Sopenharmony_ci } 71862306a36Sopenharmony_ci 71962306a36Sopenharmony_ci err = drv->request_acquire(drv); 72062306a36Sopenharmony_ci if (err != 0) { 72162306a36Sopenharmony_ci dprintk(1, "%s: Unable to acquire hardware, %d\n", __func__, 72262306a36Sopenharmony_ci err); 72362306a36Sopenharmony_ci goto fail; 72462306a36Sopenharmony_ci } 72562306a36Sopenharmony_ci 72662306a36Sopenharmony_ci if (blackbird_initialize_codec(dev) < 0) { 72762306a36Sopenharmony_ci drv->request_release(drv); 72862306a36Sopenharmony_ci err = -EINVAL; 72962306a36Sopenharmony_ci goto fail; 73062306a36Sopenharmony_ci } 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_ci err = blackbird_start_codec(dev); 73362306a36Sopenharmony_ci if (err == 0) { 73462306a36Sopenharmony_ci buf = list_entry(dmaq->active.next, struct cx88_buffer, list); 73562306a36Sopenharmony_ci cx8802_start_dma(dev, dmaq, buf); 73662306a36Sopenharmony_ci return 0; 73762306a36Sopenharmony_ci } 73862306a36Sopenharmony_ci 73962306a36Sopenharmony_cifail: 74062306a36Sopenharmony_ci spin_lock_irqsave(&dev->slock, flags); 74162306a36Sopenharmony_ci while (!list_empty(&dmaq->active)) { 74262306a36Sopenharmony_ci struct cx88_buffer *buf = list_entry(dmaq->active.next, 74362306a36Sopenharmony_ci struct cx88_buffer, list); 74462306a36Sopenharmony_ci 74562306a36Sopenharmony_ci list_del(&buf->list); 74662306a36Sopenharmony_ci vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_QUEUED); 74762306a36Sopenharmony_ci } 74862306a36Sopenharmony_ci spin_unlock_irqrestore(&dev->slock, flags); 74962306a36Sopenharmony_ci return err; 75062306a36Sopenharmony_ci} 75162306a36Sopenharmony_ci 75262306a36Sopenharmony_cistatic void stop_streaming(struct vb2_queue *q) 75362306a36Sopenharmony_ci{ 75462306a36Sopenharmony_ci struct cx8802_dev *dev = q->drv_priv; 75562306a36Sopenharmony_ci struct cx88_dmaqueue *dmaq = &dev->mpegq; 75662306a36Sopenharmony_ci struct cx8802_driver *drv = NULL; 75762306a36Sopenharmony_ci unsigned long flags; 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_ci cx8802_cancel_buffers(dev); 76062306a36Sopenharmony_ci blackbird_stop_codec(dev); 76162306a36Sopenharmony_ci 76262306a36Sopenharmony_ci /* Make sure we release the hardware */ 76362306a36Sopenharmony_ci drv = cx8802_get_driver(dev, CX88_MPEG_BLACKBIRD); 76462306a36Sopenharmony_ci WARN_ON(!drv); 76562306a36Sopenharmony_ci if (drv) 76662306a36Sopenharmony_ci drv->request_release(drv); 76762306a36Sopenharmony_ci 76862306a36Sopenharmony_ci spin_lock_irqsave(&dev->slock, flags); 76962306a36Sopenharmony_ci while (!list_empty(&dmaq->active)) { 77062306a36Sopenharmony_ci struct cx88_buffer *buf = list_entry(dmaq->active.next, 77162306a36Sopenharmony_ci struct cx88_buffer, list); 77262306a36Sopenharmony_ci 77362306a36Sopenharmony_ci list_del(&buf->list); 77462306a36Sopenharmony_ci vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); 77562306a36Sopenharmony_ci } 77662306a36Sopenharmony_ci spin_unlock_irqrestore(&dev->slock, flags); 77762306a36Sopenharmony_ci} 77862306a36Sopenharmony_ci 77962306a36Sopenharmony_cistatic const struct vb2_ops blackbird_qops = { 78062306a36Sopenharmony_ci .queue_setup = queue_setup, 78162306a36Sopenharmony_ci .buf_prepare = buffer_prepare, 78262306a36Sopenharmony_ci .buf_finish = buffer_finish, 78362306a36Sopenharmony_ci .buf_queue = buffer_queue, 78462306a36Sopenharmony_ci .wait_prepare = vb2_ops_wait_prepare, 78562306a36Sopenharmony_ci .wait_finish = vb2_ops_wait_finish, 78662306a36Sopenharmony_ci .start_streaming = start_streaming, 78762306a36Sopenharmony_ci .stop_streaming = stop_streaming, 78862306a36Sopenharmony_ci}; 78962306a36Sopenharmony_ci 79062306a36Sopenharmony_ci/* ------------------------------------------------------------------ */ 79162306a36Sopenharmony_ci 79262306a36Sopenharmony_cistatic int vidioc_querycap(struct file *file, void *priv, 79362306a36Sopenharmony_ci struct v4l2_capability *cap) 79462306a36Sopenharmony_ci{ 79562306a36Sopenharmony_ci struct cx8802_dev *dev = video_drvdata(file); 79662306a36Sopenharmony_ci struct cx88_core *core = dev->core; 79762306a36Sopenharmony_ci 79862306a36Sopenharmony_ci strscpy(cap->driver, "cx88_blackbird", sizeof(cap->driver)); 79962306a36Sopenharmony_ci return cx88_querycap(file, core, cap); 80062306a36Sopenharmony_ci} 80162306a36Sopenharmony_ci 80262306a36Sopenharmony_cistatic int vidioc_enum_fmt_vid_cap(struct file *file, void *priv, 80362306a36Sopenharmony_ci struct v4l2_fmtdesc *f) 80462306a36Sopenharmony_ci{ 80562306a36Sopenharmony_ci if (f->index != 0) 80662306a36Sopenharmony_ci return -EINVAL; 80762306a36Sopenharmony_ci 80862306a36Sopenharmony_ci f->pixelformat = V4L2_PIX_FMT_MPEG; 80962306a36Sopenharmony_ci return 0; 81062306a36Sopenharmony_ci} 81162306a36Sopenharmony_ci 81262306a36Sopenharmony_cistatic int vidioc_g_fmt_vid_cap(struct file *file, void *priv, 81362306a36Sopenharmony_ci struct v4l2_format *f) 81462306a36Sopenharmony_ci{ 81562306a36Sopenharmony_ci struct cx8802_dev *dev = video_drvdata(file); 81662306a36Sopenharmony_ci struct cx88_core *core = dev->core; 81762306a36Sopenharmony_ci 81862306a36Sopenharmony_ci f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG; 81962306a36Sopenharmony_ci f->fmt.pix.bytesperline = 0; 82062306a36Sopenharmony_ci f->fmt.pix.sizeimage = dev->ts_packet_size * dev->ts_packet_count; 82162306a36Sopenharmony_ci f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; 82262306a36Sopenharmony_ci f->fmt.pix.width = core->width; 82362306a36Sopenharmony_ci f->fmt.pix.height = core->height; 82462306a36Sopenharmony_ci f->fmt.pix.field = core->field; 82562306a36Sopenharmony_ci return 0; 82662306a36Sopenharmony_ci} 82762306a36Sopenharmony_ci 82862306a36Sopenharmony_cistatic int vidioc_try_fmt_vid_cap(struct file *file, void *priv, 82962306a36Sopenharmony_ci struct v4l2_format *f) 83062306a36Sopenharmony_ci{ 83162306a36Sopenharmony_ci struct cx8802_dev *dev = video_drvdata(file); 83262306a36Sopenharmony_ci struct cx88_core *core = dev->core; 83362306a36Sopenharmony_ci unsigned int maxw, maxh; 83462306a36Sopenharmony_ci enum v4l2_field field; 83562306a36Sopenharmony_ci 83662306a36Sopenharmony_ci f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG; 83762306a36Sopenharmony_ci f->fmt.pix.bytesperline = 0; 83862306a36Sopenharmony_ci f->fmt.pix.sizeimage = dev->ts_packet_size * dev->ts_packet_count; 83962306a36Sopenharmony_ci f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; 84062306a36Sopenharmony_ci 84162306a36Sopenharmony_ci maxw = norm_maxw(core->tvnorm); 84262306a36Sopenharmony_ci maxh = norm_maxh(core->tvnorm); 84362306a36Sopenharmony_ci 84462306a36Sopenharmony_ci field = f->fmt.pix.field; 84562306a36Sopenharmony_ci 84662306a36Sopenharmony_ci switch (field) { 84762306a36Sopenharmony_ci case V4L2_FIELD_TOP: 84862306a36Sopenharmony_ci case V4L2_FIELD_BOTTOM: 84962306a36Sopenharmony_ci case V4L2_FIELD_INTERLACED: 85062306a36Sopenharmony_ci case V4L2_FIELD_SEQ_BT: 85162306a36Sopenharmony_ci case V4L2_FIELD_SEQ_TB: 85262306a36Sopenharmony_ci break; 85362306a36Sopenharmony_ci default: 85462306a36Sopenharmony_ci field = (f->fmt.pix.height > maxh / 2) 85562306a36Sopenharmony_ci ? V4L2_FIELD_INTERLACED 85662306a36Sopenharmony_ci : V4L2_FIELD_BOTTOM; 85762306a36Sopenharmony_ci break; 85862306a36Sopenharmony_ci } 85962306a36Sopenharmony_ci if (V4L2_FIELD_HAS_T_OR_B(field)) 86062306a36Sopenharmony_ci maxh /= 2; 86162306a36Sopenharmony_ci 86262306a36Sopenharmony_ci v4l_bound_align_image(&f->fmt.pix.width, 48, maxw, 2, 86362306a36Sopenharmony_ci &f->fmt.pix.height, 32, maxh, 0, 0); 86462306a36Sopenharmony_ci f->fmt.pix.field = field; 86562306a36Sopenharmony_ci return 0; 86662306a36Sopenharmony_ci} 86762306a36Sopenharmony_ci 86862306a36Sopenharmony_cistatic int vidioc_s_fmt_vid_cap(struct file *file, void *priv, 86962306a36Sopenharmony_ci struct v4l2_format *f) 87062306a36Sopenharmony_ci{ 87162306a36Sopenharmony_ci struct cx8802_dev *dev = video_drvdata(file); 87262306a36Sopenharmony_ci struct cx88_core *core = dev->core; 87362306a36Sopenharmony_ci 87462306a36Sopenharmony_ci if (vb2_is_busy(&dev->vb2_mpegq)) 87562306a36Sopenharmony_ci return -EBUSY; 87662306a36Sopenharmony_ci if (core->v4ldev && (vb2_is_busy(&core->v4ldev->vb2_vidq) || 87762306a36Sopenharmony_ci vb2_is_busy(&core->v4ldev->vb2_vbiq))) 87862306a36Sopenharmony_ci return -EBUSY; 87962306a36Sopenharmony_ci vidioc_try_fmt_vid_cap(file, priv, f); 88062306a36Sopenharmony_ci core->width = f->fmt.pix.width; 88162306a36Sopenharmony_ci core->height = f->fmt.pix.height; 88262306a36Sopenharmony_ci core->field = f->fmt.pix.field; 88362306a36Sopenharmony_ci cx88_set_scale(core, f->fmt.pix.width, f->fmt.pix.height, 88462306a36Sopenharmony_ci f->fmt.pix.field); 88562306a36Sopenharmony_ci blackbird_api_cmd(dev, CX2341X_ENC_SET_FRAME_SIZE, 2, 0, 88662306a36Sopenharmony_ci f->fmt.pix.height, f->fmt.pix.width); 88762306a36Sopenharmony_ci return 0; 88862306a36Sopenharmony_ci} 88962306a36Sopenharmony_ci 89062306a36Sopenharmony_cistatic int vidioc_s_frequency(struct file *file, void *priv, 89162306a36Sopenharmony_ci const struct v4l2_frequency *f) 89262306a36Sopenharmony_ci{ 89362306a36Sopenharmony_ci struct cx8802_dev *dev = video_drvdata(file); 89462306a36Sopenharmony_ci struct cx88_core *core = dev->core; 89562306a36Sopenharmony_ci bool streaming; 89662306a36Sopenharmony_ci 89762306a36Sopenharmony_ci if (unlikely(core->board.tuner_type == UNSET)) 89862306a36Sopenharmony_ci return -EINVAL; 89962306a36Sopenharmony_ci if (unlikely(f->tuner != 0)) 90062306a36Sopenharmony_ci return -EINVAL; 90162306a36Sopenharmony_ci streaming = vb2_start_streaming_called(&dev->vb2_mpegq); 90262306a36Sopenharmony_ci if (streaming) 90362306a36Sopenharmony_ci blackbird_stop_codec(dev); 90462306a36Sopenharmony_ci 90562306a36Sopenharmony_ci cx88_set_freq(core, f); 90662306a36Sopenharmony_ci blackbird_initialize_codec(dev); 90762306a36Sopenharmony_ci cx88_set_scale(core, core->width, core->height, core->field); 90862306a36Sopenharmony_ci if (streaming) 90962306a36Sopenharmony_ci blackbird_start_codec(dev); 91062306a36Sopenharmony_ci return 0; 91162306a36Sopenharmony_ci} 91262306a36Sopenharmony_ci 91362306a36Sopenharmony_cistatic int vidioc_log_status(struct file *file, void *priv) 91462306a36Sopenharmony_ci{ 91562306a36Sopenharmony_ci struct cx8802_dev *dev = video_drvdata(file); 91662306a36Sopenharmony_ci struct cx88_core *core = dev->core; 91762306a36Sopenharmony_ci char name[32 + 2]; 91862306a36Sopenharmony_ci 91962306a36Sopenharmony_ci snprintf(name, sizeof(name), "%s/2", core->name); 92062306a36Sopenharmony_ci call_all(core, core, log_status); 92162306a36Sopenharmony_ci v4l2_ctrl_handler_log_status(&dev->cxhdl.hdl, name); 92262306a36Sopenharmony_ci return 0; 92362306a36Sopenharmony_ci} 92462306a36Sopenharmony_ci 92562306a36Sopenharmony_cistatic int vidioc_enum_input(struct file *file, void *priv, 92662306a36Sopenharmony_ci struct v4l2_input *i) 92762306a36Sopenharmony_ci{ 92862306a36Sopenharmony_ci struct cx8802_dev *dev = video_drvdata(file); 92962306a36Sopenharmony_ci struct cx88_core *core = dev->core; 93062306a36Sopenharmony_ci 93162306a36Sopenharmony_ci return cx88_enum_input(core, i); 93262306a36Sopenharmony_ci} 93362306a36Sopenharmony_ci 93462306a36Sopenharmony_cistatic int vidioc_g_frequency(struct file *file, void *priv, 93562306a36Sopenharmony_ci struct v4l2_frequency *f) 93662306a36Sopenharmony_ci{ 93762306a36Sopenharmony_ci struct cx8802_dev *dev = video_drvdata(file); 93862306a36Sopenharmony_ci struct cx88_core *core = dev->core; 93962306a36Sopenharmony_ci 94062306a36Sopenharmony_ci if (unlikely(core->board.tuner_type == UNSET)) 94162306a36Sopenharmony_ci return -EINVAL; 94262306a36Sopenharmony_ci if (unlikely(f->tuner != 0)) 94362306a36Sopenharmony_ci return -EINVAL; 94462306a36Sopenharmony_ci 94562306a36Sopenharmony_ci f->frequency = core->freq; 94662306a36Sopenharmony_ci call_all(core, tuner, g_frequency, f); 94762306a36Sopenharmony_ci 94862306a36Sopenharmony_ci return 0; 94962306a36Sopenharmony_ci} 95062306a36Sopenharmony_ci 95162306a36Sopenharmony_cistatic int vidioc_g_input(struct file *file, void *priv, unsigned int *i) 95262306a36Sopenharmony_ci{ 95362306a36Sopenharmony_ci struct cx8802_dev *dev = video_drvdata(file); 95462306a36Sopenharmony_ci struct cx88_core *core = dev->core; 95562306a36Sopenharmony_ci 95662306a36Sopenharmony_ci *i = core->input; 95762306a36Sopenharmony_ci return 0; 95862306a36Sopenharmony_ci} 95962306a36Sopenharmony_ci 96062306a36Sopenharmony_cistatic int vidioc_s_input(struct file *file, void *priv, unsigned int i) 96162306a36Sopenharmony_ci{ 96262306a36Sopenharmony_ci struct cx8802_dev *dev = video_drvdata(file); 96362306a36Sopenharmony_ci struct cx88_core *core = dev->core; 96462306a36Sopenharmony_ci 96562306a36Sopenharmony_ci if (i >= 4) 96662306a36Sopenharmony_ci return -EINVAL; 96762306a36Sopenharmony_ci if (!INPUT(i).type) 96862306a36Sopenharmony_ci return -EINVAL; 96962306a36Sopenharmony_ci 97062306a36Sopenharmony_ci cx88_newstation(core); 97162306a36Sopenharmony_ci cx88_video_mux(core, i); 97262306a36Sopenharmony_ci return 0; 97362306a36Sopenharmony_ci} 97462306a36Sopenharmony_ci 97562306a36Sopenharmony_cistatic int vidioc_g_tuner(struct file *file, void *priv, 97662306a36Sopenharmony_ci struct v4l2_tuner *t) 97762306a36Sopenharmony_ci{ 97862306a36Sopenharmony_ci struct cx8802_dev *dev = video_drvdata(file); 97962306a36Sopenharmony_ci struct cx88_core *core = dev->core; 98062306a36Sopenharmony_ci u32 reg; 98162306a36Sopenharmony_ci 98262306a36Sopenharmony_ci if (unlikely(core->board.tuner_type == UNSET)) 98362306a36Sopenharmony_ci return -EINVAL; 98462306a36Sopenharmony_ci if (t->index != 0) 98562306a36Sopenharmony_ci return -EINVAL; 98662306a36Sopenharmony_ci 98762306a36Sopenharmony_ci strscpy(t->name, "Television", sizeof(t->name)); 98862306a36Sopenharmony_ci t->capability = V4L2_TUNER_CAP_NORM; 98962306a36Sopenharmony_ci t->rangehigh = 0xffffffffUL; 99062306a36Sopenharmony_ci call_all(core, tuner, g_tuner, t); 99162306a36Sopenharmony_ci 99262306a36Sopenharmony_ci cx88_get_stereo(core, t); 99362306a36Sopenharmony_ci reg = cx_read(MO_DEVICE_STATUS); 99462306a36Sopenharmony_ci t->signal = (reg & (1 << 5)) ? 0xffff : 0x0000; 99562306a36Sopenharmony_ci return 0; 99662306a36Sopenharmony_ci} 99762306a36Sopenharmony_ci 99862306a36Sopenharmony_cistatic int vidioc_s_tuner(struct file *file, void *priv, 99962306a36Sopenharmony_ci const struct v4l2_tuner *t) 100062306a36Sopenharmony_ci{ 100162306a36Sopenharmony_ci struct cx8802_dev *dev = video_drvdata(file); 100262306a36Sopenharmony_ci struct cx88_core *core = dev->core; 100362306a36Sopenharmony_ci 100462306a36Sopenharmony_ci if (core->board.tuner_type == UNSET) 100562306a36Sopenharmony_ci return -EINVAL; 100662306a36Sopenharmony_ci if (t->index != 0) 100762306a36Sopenharmony_ci return -EINVAL; 100862306a36Sopenharmony_ci 100962306a36Sopenharmony_ci cx88_set_stereo(core, t->audmode, 1); 101062306a36Sopenharmony_ci return 0; 101162306a36Sopenharmony_ci} 101262306a36Sopenharmony_ci 101362306a36Sopenharmony_cistatic int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *tvnorm) 101462306a36Sopenharmony_ci{ 101562306a36Sopenharmony_ci struct cx8802_dev *dev = video_drvdata(file); 101662306a36Sopenharmony_ci struct cx88_core *core = dev->core; 101762306a36Sopenharmony_ci 101862306a36Sopenharmony_ci *tvnorm = core->tvnorm; 101962306a36Sopenharmony_ci return 0; 102062306a36Sopenharmony_ci} 102162306a36Sopenharmony_ci 102262306a36Sopenharmony_cistatic int vidioc_s_std(struct file *file, void *priv, v4l2_std_id id) 102362306a36Sopenharmony_ci{ 102462306a36Sopenharmony_ci struct cx8802_dev *dev = video_drvdata(file); 102562306a36Sopenharmony_ci struct cx88_core *core = dev->core; 102662306a36Sopenharmony_ci 102762306a36Sopenharmony_ci return cx88_set_tvnorm(core, id); 102862306a36Sopenharmony_ci} 102962306a36Sopenharmony_ci 103062306a36Sopenharmony_cistatic const struct v4l2_file_operations mpeg_fops = { 103162306a36Sopenharmony_ci .owner = THIS_MODULE, 103262306a36Sopenharmony_ci .open = v4l2_fh_open, 103362306a36Sopenharmony_ci .release = vb2_fop_release, 103462306a36Sopenharmony_ci .read = vb2_fop_read, 103562306a36Sopenharmony_ci .poll = vb2_fop_poll, 103662306a36Sopenharmony_ci .mmap = vb2_fop_mmap, 103762306a36Sopenharmony_ci .unlocked_ioctl = video_ioctl2, 103862306a36Sopenharmony_ci}; 103962306a36Sopenharmony_ci 104062306a36Sopenharmony_cistatic const struct v4l2_ioctl_ops mpeg_ioctl_ops = { 104162306a36Sopenharmony_ci .vidioc_querycap = vidioc_querycap, 104262306a36Sopenharmony_ci .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, 104362306a36Sopenharmony_ci .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, 104462306a36Sopenharmony_ci .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap, 104562306a36Sopenharmony_ci .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, 104662306a36Sopenharmony_ci .vidioc_reqbufs = vb2_ioctl_reqbufs, 104762306a36Sopenharmony_ci .vidioc_querybuf = vb2_ioctl_querybuf, 104862306a36Sopenharmony_ci .vidioc_qbuf = vb2_ioctl_qbuf, 104962306a36Sopenharmony_ci .vidioc_dqbuf = vb2_ioctl_dqbuf, 105062306a36Sopenharmony_ci .vidioc_streamon = vb2_ioctl_streamon, 105162306a36Sopenharmony_ci .vidioc_streamoff = vb2_ioctl_streamoff, 105262306a36Sopenharmony_ci .vidioc_s_frequency = vidioc_s_frequency, 105362306a36Sopenharmony_ci .vidioc_log_status = vidioc_log_status, 105462306a36Sopenharmony_ci .vidioc_enum_input = vidioc_enum_input, 105562306a36Sopenharmony_ci .vidioc_g_frequency = vidioc_g_frequency, 105662306a36Sopenharmony_ci .vidioc_g_input = vidioc_g_input, 105762306a36Sopenharmony_ci .vidioc_s_input = vidioc_s_input, 105862306a36Sopenharmony_ci .vidioc_g_tuner = vidioc_g_tuner, 105962306a36Sopenharmony_ci .vidioc_s_tuner = vidioc_s_tuner, 106062306a36Sopenharmony_ci .vidioc_g_std = vidioc_g_std, 106162306a36Sopenharmony_ci .vidioc_s_std = vidioc_s_std, 106262306a36Sopenharmony_ci .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, 106362306a36Sopenharmony_ci .vidioc_unsubscribe_event = v4l2_event_unsubscribe, 106462306a36Sopenharmony_ci}; 106562306a36Sopenharmony_ci 106662306a36Sopenharmony_cistatic const struct video_device cx8802_mpeg_template = { 106762306a36Sopenharmony_ci .name = "cx8802", 106862306a36Sopenharmony_ci .fops = &mpeg_fops, 106962306a36Sopenharmony_ci .ioctl_ops = &mpeg_ioctl_ops, 107062306a36Sopenharmony_ci .tvnorms = CX88_NORMS, 107162306a36Sopenharmony_ci}; 107262306a36Sopenharmony_ci 107362306a36Sopenharmony_ci/* ------------------------------------------------------------------ */ 107462306a36Sopenharmony_ci 107562306a36Sopenharmony_ci/* The CX8802 MPEG API will call this when we can use the hardware */ 107662306a36Sopenharmony_cistatic int cx8802_blackbird_advise_acquire(struct cx8802_driver *drv) 107762306a36Sopenharmony_ci{ 107862306a36Sopenharmony_ci struct cx88_core *core = drv->core; 107962306a36Sopenharmony_ci int err = 0; 108062306a36Sopenharmony_ci 108162306a36Sopenharmony_ci switch (core->boardnr) { 108262306a36Sopenharmony_ci case CX88_BOARD_HAUPPAUGE_HVR1300: 108362306a36Sopenharmony_ci /* 108462306a36Sopenharmony_ci * By default, core setup will leave the cx22702 out of reset, 108562306a36Sopenharmony_ci * on the bus. 108662306a36Sopenharmony_ci * We left the hardware on power up with the cx22702 active. 108762306a36Sopenharmony_ci * We're being given access to re-arrange the GPIOs. 108862306a36Sopenharmony_ci * Take the bus off the cx22702 and put the cx23416 on it. 108962306a36Sopenharmony_ci */ 109062306a36Sopenharmony_ci /* Toggle reset on cx22702 leaving i2c active */ 109162306a36Sopenharmony_ci cx_set(MO_GP0_IO, 0x00000080); 109262306a36Sopenharmony_ci udelay(1000); 109362306a36Sopenharmony_ci cx_clear(MO_GP0_IO, 0x00000080); 109462306a36Sopenharmony_ci udelay(50); 109562306a36Sopenharmony_ci cx_set(MO_GP0_IO, 0x00000080); 109662306a36Sopenharmony_ci udelay(1000); 109762306a36Sopenharmony_ci /* tri-state the cx22702 pins */ 109862306a36Sopenharmony_ci cx_set(MO_GP0_IO, 0x00000004); 109962306a36Sopenharmony_ci udelay(1000); 110062306a36Sopenharmony_ci break; 110162306a36Sopenharmony_ci default: 110262306a36Sopenharmony_ci err = -ENODEV; 110362306a36Sopenharmony_ci } 110462306a36Sopenharmony_ci return err; 110562306a36Sopenharmony_ci} 110662306a36Sopenharmony_ci 110762306a36Sopenharmony_ci/* The CX8802 MPEG API will call this when we need to release the hardware */ 110862306a36Sopenharmony_cistatic int cx8802_blackbird_advise_release(struct cx8802_driver *drv) 110962306a36Sopenharmony_ci{ 111062306a36Sopenharmony_ci struct cx88_core *core = drv->core; 111162306a36Sopenharmony_ci int err = 0; 111262306a36Sopenharmony_ci 111362306a36Sopenharmony_ci switch (core->boardnr) { 111462306a36Sopenharmony_ci case CX88_BOARD_HAUPPAUGE_HVR1300: 111562306a36Sopenharmony_ci /* Exit leaving the cx23416 on the bus */ 111662306a36Sopenharmony_ci break; 111762306a36Sopenharmony_ci default: 111862306a36Sopenharmony_ci err = -ENODEV; 111962306a36Sopenharmony_ci } 112062306a36Sopenharmony_ci return err; 112162306a36Sopenharmony_ci} 112262306a36Sopenharmony_ci 112362306a36Sopenharmony_cistatic void blackbird_unregister_video(struct cx8802_dev *dev) 112462306a36Sopenharmony_ci{ 112562306a36Sopenharmony_ci video_unregister_device(&dev->mpeg_dev); 112662306a36Sopenharmony_ci} 112762306a36Sopenharmony_ci 112862306a36Sopenharmony_cistatic int blackbird_register_video(struct cx8802_dev *dev) 112962306a36Sopenharmony_ci{ 113062306a36Sopenharmony_ci int err; 113162306a36Sopenharmony_ci 113262306a36Sopenharmony_ci cx88_vdev_init(dev->core, dev->pci, &dev->mpeg_dev, 113362306a36Sopenharmony_ci &cx8802_mpeg_template, "mpeg"); 113462306a36Sopenharmony_ci dev->mpeg_dev.ctrl_handler = &dev->cxhdl.hdl; 113562306a36Sopenharmony_ci video_set_drvdata(&dev->mpeg_dev, dev); 113662306a36Sopenharmony_ci dev->mpeg_dev.queue = &dev->vb2_mpegq; 113762306a36Sopenharmony_ci dev->mpeg_dev.device_caps = V4L2_CAP_READWRITE | V4L2_CAP_STREAMING | 113862306a36Sopenharmony_ci V4L2_CAP_VIDEO_CAPTURE; 113962306a36Sopenharmony_ci if (dev->core->board.tuner_type != UNSET) 114062306a36Sopenharmony_ci dev->mpeg_dev.device_caps |= V4L2_CAP_TUNER; 114162306a36Sopenharmony_ci err = video_register_device(&dev->mpeg_dev, VFL_TYPE_VIDEO, -1); 114262306a36Sopenharmony_ci if (err < 0) { 114362306a36Sopenharmony_ci pr_info("can't register mpeg device\n"); 114462306a36Sopenharmony_ci return err; 114562306a36Sopenharmony_ci } 114662306a36Sopenharmony_ci pr_info("registered device %s [mpeg]\n", 114762306a36Sopenharmony_ci video_device_node_name(&dev->mpeg_dev)); 114862306a36Sopenharmony_ci return 0; 114962306a36Sopenharmony_ci} 115062306a36Sopenharmony_ci 115162306a36Sopenharmony_ci/* ----------------------------------------------------------- */ 115262306a36Sopenharmony_ci 115362306a36Sopenharmony_cistatic int cx8802_blackbird_probe(struct cx8802_driver *drv) 115462306a36Sopenharmony_ci{ 115562306a36Sopenharmony_ci struct cx88_core *core = drv->core; 115662306a36Sopenharmony_ci struct cx8802_dev *dev = core->dvbdev; 115762306a36Sopenharmony_ci struct vb2_queue *q; 115862306a36Sopenharmony_ci int err; 115962306a36Sopenharmony_ci 116062306a36Sopenharmony_ci dprintk(1, "%s\n", __func__); 116162306a36Sopenharmony_ci dprintk(1, " ->being probed by Card=%d Name=%s, PCI %02x:%02x\n", 116262306a36Sopenharmony_ci core->boardnr, 116362306a36Sopenharmony_ci core->name, 116462306a36Sopenharmony_ci core->pci_bus, 116562306a36Sopenharmony_ci core->pci_slot); 116662306a36Sopenharmony_ci 116762306a36Sopenharmony_ci err = -ENODEV; 116862306a36Sopenharmony_ci if (!(core->board.mpeg & CX88_MPEG_BLACKBIRD)) 116962306a36Sopenharmony_ci goto fail_core; 117062306a36Sopenharmony_ci 117162306a36Sopenharmony_ci dev->cxhdl.port = CX2341X_PORT_STREAMING; 117262306a36Sopenharmony_ci dev->cxhdl.width = core->width; 117362306a36Sopenharmony_ci dev->cxhdl.height = core->height; 117462306a36Sopenharmony_ci dev->cxhdl.func = blackbird_mbox_func; 117562306a36Sopenharmony_ci dev->cxhdl.priv = dev; 117662306a36Sopenharmony_ci err = cx2341x_handler_init(&dev->cxhdl, 36); 117762306a36Sopenharmony_ci if (err) 117862306a36Sopenharmony_ci goto fail_core; 117962306a36Sopenharmony_ci v4l2_ctrl_add_handler(&dev->cxhdl.hdl, &core->video_hdl, NULL, false); 118062306a36Sopenharmony_ci 118162306a36Sopenharmony_ci /* blackbird stuff */ 118262306a36Sopenharmony_ci pr_info("cx23416 based mpeg encoder (blackbird reference design)\n"); 118362306a36Sopenharmony_ci host_setup(dev->core); 118462306a36Sopenharmony_ci 118562306a36Sopenharmony_ci blackbird_initialize_codec(dev); 118662306a36Sopenharmony_ci 118762306a36Sopenharmony_ci /* initial device configuration: needed ? */ 118862306a36Sopenharmony_ci// init_controls(core); 118962306a36Sopenharmony_ci cx88_set_tvnorm(core, core->tvnorm); 119062306a36Sopenharmony_ci cx88_video_mux(core, 0); 119162306a36Sopenharmony_ci cx2341x_handler_set_50hz(&dev->cxhdl, core->height == 576); 119262306a36Sopenharmony_ci cx2341x_handler_setup(&dev->cxhdl); 119362306a36Sopenharmony_ci 119462306a36Sopenharmony_ci q = &dev->vb2_mpegq; 119562306a36Sopenharmony_ci q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 119662306a36Sopenharmony_ci q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF | VB2_READ; 119762306a36Sopenharmony_ci q->gfp_flags = GFP_DMA32; 119862306a36Sopenharmony_ci q->min_buffers_needed = 2; 119962306a36Sopenharmony_ci q->drv_priv = dev; 120062306a36Sopenharmony_ci q->buf_struct_size = sizeof(struct cx88_buffer); 120162306a36Sopenharmony_ci q->ops = &blackbird_qops; 120262306a36Sopenharmony_ci q->mem_ops = &vb2_dma_sg_memops; 120362306a36Sopenharmony_ci q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; 120462306a36Sopenharmony_ci q->lock = &core->lock; 120562306a36Sopenharmony_ci q->dev = &dev->pci->dev; 120662306a36Sopenharmony_ci 120762306a36Sopenharmony_ci err = vb2_queue_init(q); 120862306a36Sopenharmony_ci if (err < 0) 120962306a36Sopenharmony_ci goto fail_core; 121062306a36Sopenharmony_ci 121162306a36Sopenharmony_ci blackbird_register_video(dev); 121262306a36Sopenharmony_ci 121362306a36Sopenharmony_ci return 0; 121462306a36Sopenharmony_ci 121562306a36Sopenharmony_cifail_core: 121662306a36Sopenharmony_ci return err; 121762306a36Sopenharmony_ci} 121862306a36Sopenharmony_ci 121962306a36Sopenharmony_cistatic int cx8802_blackbird_remove(struct cx8802_driver *drv) 122062306a36Sopenharmony_ci{ 122162306a36Sopenharmony_ci struct cx88_core *core = drv->core; 122262306a36Sopenharmony_ci struct cx8802_dev *dev = core->dvbdev; 122362306a36Sopenharmony_ci 122462306a36Sopenharmony_ci /* blackbird */ 122562306a36Sopenharmony_ci blackbird_unregister_video(drv->core->dvbdev); 122662306a36Sopenharmony_ci v4l2_ctrl_handler_free(&dev->cxhdl.hdl); 122762306a36Sopenharmony_ci 122862306a36Sopenharmony_ci return 0; 122962306a36Sopenharmony_ci} 123062306a36Sopenharmony_ci 123162306a36Sopenharmony_cistatic struct cx8802_driver cx8802_blackbird_driver = { 123262306a36Sopenharmony_ci .type_id = CX88_MPEG_BLACKBIRD, 123362306a36Sopenharmony_ci .hw_access = CX8802_DRVCTL_SHARED, 123462306a36Sopenharmony_ci .probe = cx8802_blackbird_probe, 123562306a36Sopenharmony_ci .remove = cx8802_blackbird_remove, 123662306a36Sopenharmony_ci .advise_acquire = cx8802_blackbird_advise_acquire, 123762306a36Sopenharmony_ci .advise_release = cx8802_blackbird_advise_release, 123862306a36Sopenharmony_ci}; 123962306a36Sopenharmony_ci 124062306a36Sopenharmony_cistatic int __init blackbird_init(void) 124162306a36Sopenharmony_ci{ 124262306a36Sopenharmony_ci pr_info("cx2388x blackbird driver version %s loaded\n", 124362306a36Sopenharmony_ci CX88_VERSION); 124462306a36Sopenharmony_ci return cx8802_register_driver(&cx8802_blackbird_driver); 124562306a36Sopenharmony_ci} 124662306a36Sopenharmony_ci 124762306a36Sopenharmony_cistatic void __exit blackbird_fini(void) 124862306a36Sopenharmony_ci{ 124962306a36Sopenharmony_ci cx8802_unregister_driver(&cx8802_blackbird_driver); 125062306a36Sopenharmony_ci} 125162306a36Sopenharmony_ci 125262306a36Sopenharmony_cimodule_init(blackbird_init); 125362306a36Sopenharmony_cimodule_exit(blackbird_fini); 1254