/* TurboSight PCIe 2.0 HDMI capture cards driver Copyright (C) 2024 www.tbsdtv.com */ #include #include "tbs_pcie2-reg.h" #include "tbs_pcie2.h" #include "libyuv.h" static int debug=0; module_param(debug,int,0660); static bool enable_msi = true;//false; module_param(enable_msi, bool, 0444); MODULE_PARM_DESC(enable_msi, "use an msi interrupt if available"); static int tbs_get_video_param(struct tbs_video *pvideo); static void audio_wake_process(struct work_struct *p_work); static void video_wake_process(struct work_struct *p_work); static void i2c_wake_process(struct work_struct *p_work); static int ProcessStreamThread(void *data); struct workqueue_struct *wq; u8 fw7611[]= { 0x98, 0xF4, 0x80 , //CEC 0x98, 0xF5, 0x7C , //INFOFRAME 0x98, 0xF8, 0x4C , //DPLL 0x98, 0xF9, 0x64 ,// KSV 0x98, 0xFA, 0x6C , //EDID 0x98, 0xFB, 0x68, // HDMI 0x98, 0xFD, 0x44 ,// CP 0x98, 0x01, 0x06 , //Prim_Mode =110b HDMI-GR 0x98, 0x02, 0xF5 , //Auto CSC, YCrCb out, Set op_656 bit 0x98, 0x03, 0x80 , //0x80 - 16-bit ITU-656 SDR mode //0x98, 0x03, 0x8a , //24 bit SDR 444 Mode 0 //0x8A - 24-bit ITU-656 SDR mode 2 0x98, 0x04, 0x60 , //27M 2019-07-03 1734 //0x98, 0x05, 0x28 , //AV Codes Off 28 ;eric 2017-11-27 0x98, 0x05, 0x2c , //AV Codes On 2c hanly 0x98, 0x06, 0xA4 ,// A4 Invert VS,HS pins //test only; p signal //0x98, 0x06, 0x26, //eric mark, field; ok; //0x98, 0x06, 0x24, //eric mark, field; ok; I signal 0x98, 0x0B, 0x44 , //Power up part 0x98, 0x0C, 0x42 , //Power up part 0x98, 0x14, 0x7F , //Max Drive Strength 0x98, 0x15, 0x80 , //Disable Tristate of Pins 0x98, 0x19, 0x83 , //LLC DLL phase 0x98, 0x33, 0x40 , //LLC DLL enable 0x44, 0xBA, 0x01 ,// Set HDMI FreeRun 0x64, 0x40, 0x81 , //Disable HDCP 1.1 features 0x68, 0x9B, 0x03 , //ADI recommended setting 0x68, 0xC1, 0x01 , //ADI recommended setting 0x68, 0xC2, 0x01 ,// ADI recommended setting 0x68, 0xC3, 0x01 , //ADI recommended setting 0x68, 0xC4, 0x01 , //ADI recommended setting 0x68, 0xC5, 0x01 , //ADI recommended setting 0x68, 0xC6, 0x01 , //ADI recommended setting 0x68, 0xC7, 0x01 , //ADI recommended setting 0x68, 0xC8, 0x01 , //ADI recommended setting 0x68, 0xC9, 0x01 ,// ADI recommended setting 0x68, 0xCA, 0x01 , //ADI recommended setting 0x68, 0xCB, 0x01 , //ADI recommended setting 0x68, 0xCC, 0x01 ,// ADI recommended setting 0x68, 0x00, 0x00 , //Set HDMI Input Port A 0x68, 0x83, 0xFE , //Enable clock terminator for port A 0x68, 0x6F, 0x0C ,// ADI recommended setting 0x68, 0x85, 0x1F , //ADI recommended setting 0x68, 0x87, 0x70 , //ADI recommended setting 0x68, 0x8D, 0x04 , //LFG 0x68, 0x8E, 0x1E , //HFG 0x68, 0x1A, 0x8A , //unmute audio 0x68, 0x57, 0xDA , //ADI recommended setting 0x68, 0x58, 0x01 , //ADI recommended setting 0x68, 0x03, 0x98 , // DIS_I2C_ZERO_COMPR 0x68, 0x75, 0x10 , //DDC drive strength 0xff }; static int i2c_read_reg(struct i2c_adapter *adapter, u8 adr, u8 reg, u8 *val, int len) { struct i2c_msg msgs[2] = {{.addr = adr, .flags =0, .buf = ®, .len =1}, {.addr =adr, .flags = I2C_M_RD, .buf = val, .len = len}}; return (i2c_transfer(adapter, msgs,2) == 2) ? 0 : -1; } static int i2c_write_reg(struct i2c_adapter *adapter, u8 adr, u8 *val, int len) { struct i2c_msg msg[1] = {{.addr = adr, .flags =0, .buf = val, .len =len}}; return (i2c_transfer(adapter, msg,1) == 1) ? 0 : -1; } static void i2c_write_tab_new(struct i2c_adapter *adapter, u8 *script) { u8 temp[2]; do{ temp[0] = *(script+1); temp[1] = *(script+2); i2c_write_reg(adapter,*(script),temp,2 ); script += 3; }while(*script != 0xff); } static int tbs_vidioc_querycap(struct file *file, void *priv, struct v4l2_capability *cap) { struct tbs_video *videodev = video_drvdata(file); //printk( "%s() \n", __func__); sprintf(cap->driver, KBUILD_MODNAME); sprintf(cap->card, "video %d",(videodev->index>>1)); sprintf(cap->bus_info, "PCI:%s %d",pci_name(videodev->dev->pdev),(videodev->index>>1)); return 0; } static int tbs_vidioc_enum_fmt_vid_cap(struct file *file, void *priv_fh,struct v4l2_fmtdesc *fmt) { // printk( "%s() index:%d\n", __func__,fmt->index); switch (fmt->index) { case 0: strncpy(fmt->description, "NV12", sizeof(fmt->description)); fmt->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; fmt->pixelformat = V4L2_PIX_FMT_NV12; break; case 1: strncpy(fmt->description, "YU12", sizeof(fmt->description)); fmt->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; fmt->pixelformat = V4L2_PIX_FMT_YUV420; break; case 2: strncpy(fmt->description, "YV12", sizeof(fmt->description)); fmt->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; fmt->pixelformat = V4L2_PIX_FMT_YVU420; break; case 3: strncpy(fmt->description, "UYVY", sizeof(fmt->description)); fmt->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; fmt->pixelformat = V4L2_PIX_FMT_UYVY; break; default: return -EINVAL; } return 0; } static int tbs_vidioc_g_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt) { struct tbs_video *videodev = video_drvdata(file); struct v4l2_pix_format *pix = &fmt->fmt.pix; //printk( "%s() width:%d heigh:%d size:%d pixelformat:%x\n", __func__,pix->width,pix->height,pix->sizeimage,pix->pixelformat); fmt->type=V4L2_BUF_TYPE_VIDEO_CAPTURE; pix->width = videodev->dst_width ; pix->height = videodev->dst_height; pix->field = V4L2_FIELD_NONE; pix->bytesperline = videodev->dst_width ; pix->sizeimage = 3*(pix->width>>1) * pix->height; pix->colorspace = V4L2_COLORSPACE_SMPTE170M; pix->pixelformat = V4L2_PIX_FMT_NV12; return 0; } static int tbs_vidioc_try_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt) { struct tbs_video *videodev = video_drvdata(file); struct v4l2_pix_format *pix = &fmt->fmt.pix; // printk( "%s() width:%d heigh:%d size:%d pixelformat:%x\n", __func__,pix->width,pix->height,pix->sizeimage,pix->pixelformat); fmt->type=V4L2_BUF_TYPE_VIDEO_CAPTURE; pix->width = videodev->dst_width ; pix->height = videodev->dst_height; pix->field = V4L2_FIELD_NONE; pix->bytesperline = videodev->dst_width ; pix->sizeimage = 3*(pix->width>>1) * pix->height; pix->colorspace = V4L2_COLORSPACE_SMPTE170M; pix->pixelformat = V4L2_PIX_FMT_NV12; return 0; } static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,struct v4l2_format *fmt) { // struct tbs_video *videodev = video_drvdata(file); struct tbs_videofile_instance *file_instance = container_of(priv, struct tbs_videofile_instance, fh); struct v4l2_pix_format *pix = &fmt->fmt.pix; //printk( "%s() width:%d heigh:%d size:%d pixelformat:%x\n", __func__,pix->width,pix->height,pix->sizeimage,pix->pixelformat); file_instance->select_width=pix->width; file_instance->select_height=pix->height; file_instance->select_pixelformat=pix->pixelformat; if(pix->widthheightwidth > 1920 || pix->height >1080 ) { file_instance->select_width=DEFAULT_WIDTH; file_instance->select_height=DEFAULT_HEIGH; return -EINVAL; } return 0; } // static int tbs_vidioc_g_parm(struct file *file,void *fh, struct v4l2_streamparm *setfps) // { // struct tbs_video *videodev = video_drvdata(file); // printk( "%s() fps:%x\n", __func__, videodev->fps); // setfps->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; // setfps->parm.capture.timeperframe.numerator=1; // setfps->parm.capture.timeperframe.denominator=videodev->fps; // return 0; // } static int tbs_vidioc_querystd(struct file *file, void *fh, v4l2_std_id *std) { // struct tbs_video *videodev = video_drvdata(file); //printk( "%s()\n", __func__); *std= TBS_NORMS; return 0; } static int tbs_vidioc_g_std(struct file *file, void *priv, v4l2_std_id *std) { struct tbs_video *videodev = video_drvdata(file); //printk( "%s()\n", __func__); *std = videodev->std; return 0; } static int tbs_vidioc_s_std(struct file *file, void *priv,v4l2_std_id std) { struct tbs_video *videodev = video_drvdata(file); //printk( "%s()\n", __func__); videodev->std = std; return 0; } static int tbs_vidioc_enum_input(struct file *file, void *priv,struct v4l2_input *i) { //printk( "%s()\n", __func__); i->type = V4L2_INPUT_TYPE_CAMERA; strcpy(i->name, KBUILD_MODNAME); i->std = TBS_NORMS; if(i->index) return -EINVAL; return 0; } static int tbs_vidioc_g_input(struct file *file, void *priv, unsigned int *i) { //printk( "%s()\n", __func__); *i = 0; return 0; } static int tbs_vidioc_s_input(struct file *file, void *priv, unsigned int i) { //printk( "%s()\n", __func__); return i ? -EINVAL : 0; } // static int vidioc_log_status(struct file *file, void *priv) // { // printk( "%s()\n", __func__); // return 0; // } static int tbs_queue_setup(struct vb2_queue *q, unsigned int *num_buffers, unsigned int *num_planes, unsigned int sizes[], struct device *alloc_devs[]) { u32 size; struct tbs_videofile_instance *stream= list_entry(q,struct tbs_videofile_instance,queue); //printk( "%s() vb2_queue:%p select_width:%d select_height:%d\n", __func__,q,stream->select_width,stream->select_height); //printk( "%s() num_buffers:%d num_planes:%d sizes[0]:%d sizes[1]:%d sizes[2]:%d\n", __func__,*num_buffers,*num_planes,sizes[0],sizes[1],sizes[2]); if(stream->select_pixelformat == V4L2_PIX_FMT_UYVY){ size = 2*stream->select_width*stream->select_height; } if(stream->select_pixelformat == V4L2_PIX_FMT_YUV420 || stream->select_pixelformat == V4L2_PIX_FMT_NV12 || stream->select_pixelformat == V4L2_PIX_FMT_YVU420){ size = 3*(stream->select_width>>1)*stream->select_height; } if (*num_planes) return sizes[0] < size ? -EINVAL : 0; *num_planes = 1; if(*num_buffers<2) *num_buffers=2; sizes[0]= size; return 0; } static int tbs_buffer_prepare(struct vb2_buffer *vb) { struct tbs_videofile_instance *stream= list_entry(vb->vb2_queue,struct tbs_videofile_instance,queue); u32 size; if(stream->select_pixelformat == V4L2_PIX_FMT_UYVY){ size = 2*stream->select_width*stream->select_height; } if(stream->select_pixelformat == V4L2_PIX_FMT_YUV420 || stream->select_pixelformat == V4L2_PIX_FMT_NV12 || stream->select_pixelformat == V4L2_PIX_FMT_YVU420){ size = 3*(stream->select_width>>1)*stream->select_height; } // printk( "%s() vb2_plane_size:%ld size:%d\n", __func__,vb2_plane_size(vb,0), size); if (vb2_plane_size(vb, 0) < size) return -EINVAL; vb2_set_plane_payload(vb, 0, size); return 0; } static void tbs_buffer_finish(struct vb2_buffer *vb) { // printk( "%s()\n", __func__); return; } static void tbs_buffer_queue(struct vb2_buffer *vb) { struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); // struct tbs_video *videodev = vb->vb2_queue->drv_priv; struct tbs_videofile_instance *stream= list_entry(vb->vb2_queue,struct tbs_videofile_instance,queue); struct tbsvideo_buffer *buf = container_of(vbuf, struct tbsvideo_buffer, vb); // printk( "%s()\n", __func__); spin_lock(&stream->slock); list_add_tail(&buf->queue, &stream->list); spin_unlock(&stream->slock); } static void start_video_dma_transfer(struct tbs_video *videodev) { struct tbs_pcie_dev *dev =videodev->dev; // printk( "%s() addr:%x index:%x \n", __func__,TBS_DMA_BASE(videodev->index),videodev->index); mutex_lock(&dev->devicemutex); TBS_PCIE_READ(TBS_DMA_BASE(videodev->index), TBS_DMA_STATUS);// clear status; TBS_PCIE_WRITE(TBS_INT_BASE, TBS_DMA_MASK(videodev->index), 0x00000001); //start dma; // write picture size TBS_PCIE_WRITE(TBS_DMA_BASE(videodev->index), TBS_DMA_CELL_SIZE, DMA_VIDEO_CELL); //set dma address: TBS_PCIE_WRITE(TBS_DMA_BASE(videodev->index), TBS_DMA_ADDR_HIGH, 0); TBS_PCIE_WRITE(TBS_DMA_BASE(videodev->index), TBS_DMA_ADDR_LOW, videodev->dmabuf[0].dma); TBS_PCIE_WRITE(TBS_DMA_BASE(videodev->index), TBS_DMA_ADDR_LOW1, videodev->dmabuf[1].dma); TBS_PCIE_WRITE(TBS_DMA_BASE(videodev->index), TBS_DMA_ADDR_LOW2, videodev->dmabuf[2].dma); TBS_PCIE_WRITE(TBS_DMA_BASE(videodev->index), TBS_DMA_START, 0x00000001); mutex_unlock(&dev->devicemutex); } static void stop_video_dma_transfer(struct tbs_video *videodev) { struct tbs_pcie_dev *dev =videodev->dev; mutex_lock(&dev->devicemutex); TBS_PCIE_READ(TBS_DMA_BASE(videodev->index), TBS_DMA_STATUS); TBS_PCIE_WRITE(TBS_INT_BASE, TBS_DMA_MASK(videodev->index), 0x00000000); TBS_PCIE_WRITE(TBS_DMA_BASE(videodev->index), TBS_DMA_START, 0x00000000); mutex_unlock(&dev->devicemutex); } static int tbs_start_streaming(struct vb2_queue *q, unsigned int count) { struct tbs_video *videodev = q->drv_priv; struct tbs_videofile_instance *stream= list_entry(q,struct tbs_videofile_instance,queue); //printk( "%s() index:%x bufsize:%ld buf_struct_size:%d\n", __func__, videodev->index,sizeof(struct tbsvideo_buffer),q->buf_struct_size); stream->seqnr = 0; stream->stream_thread = kthread_run(ProcessStreamThread,q,"tbs_stream_thread"); return 0; } static void tbs_stop_streaming(struct vb2_queue *q) { struct tbs_video *videodev = q->drv_priv; struct tbs_videofile_instance *stream= list_entry(q,struct tbs_videofile_instance,queue); //printk( "%s() index:%x \n", __func__, videodev->index); if(stream->stream_thread){ kthread_stop(stream->stream_thread); stream->stream_thread=NULL; } vb2_wait_for_all_buffers(q); //printk( "%s() exit\n", __func__); } static const struct vb2_ops tbspcie_video_qops = { .queue_setup = tbs_queue_setup, .buf_prepare = tbs_buffer_prepare, .buf_finish = tbs_buffer_finish, .buf_queue = tbs_buffer_queue, .wait_prepare = vb2_ops_wait_prepare, .wait_finish = vb2_ops_wait_finish, .start_streaming = tbs_start_streaming, .stop_streaming = tbs_stop_streaming, }; #if 0 static void vb2_set_flags_and_caps(struct vb2_queue *q, u32 memory, u32 *flags, u32 *caps, u32 *max_num_bufs) { if (!q->allow_cache_hints || memory != V4L2_MEMORY_MMAP) { /* * This needs to clear V4L2_MEMORY_FLAG_NON_COHERENT only, * but in order to avoid bugs we zero out all bits. */ *flags = 0; } else { /* Clear all unknown flags. */ *flags &= V4L2_MEMORY_FLAG_NON_COHERENT; } *caps |= V4L2_BUF_CAP_SUPPORTS_ORPHANED_BUFS; if (q->io_modes & VB2_MMAP) *caps |= V4L2_BUF_CAP_SUPPORTS_MMAP; if (q->io_modes & VB2_USERPTR) *caps |= V4L2_BUF_CAP_SUPPORTS_USERPTR; if (q->io_modes & VB2_DMABUF) *caps |= V4L2_BUF_CAP_SUPPORTS_DMABUF; if (q->subsystem_flags & VB2_V4L2_FL_SUPPORTS_M2M_HOLD_CAPTURE_BUF) *caps |= V4L2_BUF_CAP_SUPPORTS_M2M_HOLD_CAPTURE_BUF; if (q->allow_cache_hints && q->io_modes & VB2_MMAP) *caps |= V4L2_BUF_CAP_SUPPORTS_MMAP_CACHE_HINTS; if (q->supports_requests) *caps |= V4L2_BUF_CAP_SUPPORTS_REQUESTS; if (max_num_bufs) { *max_num_bufs = q->max_num_buffers; *caps |= V4L2_BUF_CAP_SUPPORTS_MAX_NUM_BUFFERS; } } static int tbs_vidioc_reqbufs(struct file *file, void *priv, struct v4l2_requestbuffers *p) { // struct video_device *vdev = video_devdata(file); struct tbs_videofile_instance *file_instance = container_of(priv, struct tbs_videofile_instance, fh); int res = vb2_verify_memory_type(&file_instance->queue, p->memory, p->type); u32 flags = p->flags; printk( "%s() priv:%p\n", __func__,priv); vb2_set_flags_and_caps(&(file_instance->queue), p->memory, &flags, &p->capabilities, NULL); p->flags = flags; if (res) return res; if (vb2_queue_is_busy(&file_instance->queue, file)) return -EBUSY; res = vb2_core_reqbufs(&file_instance->queue, p->memory, p->flags, &p->count); /* If count == 0, then the owner has released all buffers and he is no longer owner of the queue. Otherwise we have a new owner. */ if (res == 0) file_instance->queue.owner = p->count ? file->private_data : NULL; return res; } static int tbs_vidioc_create_bufs(struct file *file, void *priv, struct v4l2_create_buffers *p) { // struct video_device *vdev = video_devdata(file); struct tbs_videofile_instance *file_instance = container_of(priv, struct tbs_videofile_instance, fh); int res = vb2_verify_memory_type(&file_instance->queue, p->memory, p->format.type); printk( "%s() priv:%p\n", __func__,priv); p->index = vb2_get_num_buffers(&file_instance->queue); vb2_set_flags_and_caps(&file_instance->queue, p->memory, &p->flags, &p->capabilities, &p->max_num_buffers); /* * If count == 0, then just check if memory and type are valid. * Any -EBUSY result from vb2_verify_memory_type can be mapped to 0. */ if (p->count == 0) return res != -EBUSY ? res : 0; if (res) return res; if (vb2_queue_is_busy(&file_instance->queue, file)) return -EBUSY; res = vb2_create_bufs(&file_instance->queue, p); if (res == 0) file_instance->queue.owner = file->private_data; return res; } static int tbs_vidioc_prepare_buf(struct file *file, void *priv, struct v4l2_buffer *p) { struct video_device *vdev = video_devdata(file); struct tbs_videofile_instance *file_instance = container_of(priv, struct tbs_videofile_instance, fh); printk( "%s() priv:%p\n", __func__,priv); if (vb2_queue_is_busy(&file_instance->queue, file)) return -EBUSY; return vb2_prepare_buf(&file_instance->queue, vdev->v4l2_dev->mdev, p); } static int tbs_vidioc_querybuf(struct file *file, void *priv, struct v4l2_buffer *p) { // struct video_device *vdev = video_devdata(file); struct tbs_videofile_instance *file_instance = container_of(priv, struct tbs_videofile_instance, fh); printk( "%s() priv:%p\n", __func__,priv); /* No need to call vb2_queue_is_busy(), anyone can query buffers. */ return vb2_querybuf(&file_instance->queue, p); } static int tbs_vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *p) { struct video_device *vdev = video_devdata(file); struct tbs_videofile_instance *file_instance = container_of(priv, struct tbs_videofile_instance, fh); // printk( "%s() priv:%p\n", __func__,priv); if (vb2_queue_is_busy(&file_instance->queue, file)) return -EBUSY; return vb2_qbuf(&file_instance->queue, vdev->v4l2_dev->mdev, p); } static int tbs_vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p) { // struct video_device *vdev = video_devdata(file); struct tbs_videofile_instance *file_instance = container_of(priv, struct tbs_videofile_instance, fh); // printk( "%s() priv:%p\n", __func__,priv); if (vb2_queue_is_busy(&file_instance->queue, file)) return -EBUSY; return vb2_dqbuf(&file_instance->queue, p, file->f_flags & O_NONBLOCK); } static int tbs_vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i) { // struct video_device *vdev = video_devdata(file); struct tbs_videofile_instance *file_instance = container_of(priv, struct tbs_videofile_instance, fh); printk( "%s() priv:%p\n", __func__,priv); if (vb2_queue_is_busy(&file_instance->queue, file)) return -EBUSY; return vb2_streamon(&file_instance->queue, i); } static int tbs_vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i) { // struct video_device *vdev = video_devdata(file); struct tbs_videofile_instance *file_instance = container_of(priv, struct tbs_videofile_instance, fh); printk( "%s() priv:%p\n", __func__,priv); if (vb2_queue_is_busy(&file_instance->queue, file)) return -EBUSY; return vb2_streamoff(&file_instance->queue, i); } #else static void fill_buf_caps(struct vb2_queue *q, u32 *caps) { *caps = V4L2_BUF_CAP_SUPPORTS_ORPHANED_BUFS; if (q->io_modes & VB2_MMAP) *caps |= V4L2_BUF_CAP_SUPPORTS_MMAP; if (q->io_modes & VB2_USERPTR) *caps |= V4L2_BUF_CAP_SUPPORTS_USERPTR; if (q->io_modes & VB2_DMABUF) *caps |= V4L2_BUF_CAP_SUPPORTS_DMABUF; if (q->subsystem_flags & VB2_V4L2_FL_SUPPORTS_M2M_HOLD_CAPTURE_BUF) *caps |= V4L2_BUF_CAP_SUPPORTS_M2M_HOLD_CAPTURE_BUF; if (q->allow_cache_hints && q->io_modes & VB2_MMAP) *caps |= V4L2_BUF_CAP_SUPPORTS_MMAP_CACHE_HINTS; #ifdef CONFIG_MEDIA_CONTROLLER_REQUEST_API if (q->supports_requests) *caps |= V4L2_BUF_CAP_SUPPORTS_REQUESTS; #endif } static void validate_memory_flags(struct vb2_queue *q, int memory, u32 *flags) { if (!q->allow_cache_hints || memory != V4L2_MEMORY_MMAP) { /* * This needs to clear V4L2_MEMORY_FLAG_NON_COHERENT only, * but in order to avoid bugs we zero out all bits. */ *flags = 0; } else { /* Clear all unknown flags. */ *flags &= V4L2_MEMORY_FLAG_NON_COHERENT; } } static int tbs_vidioc_reqbufs(struct file *file, void *priv, struct v4l2_requestbuffers *p) { // struct video_device *vdev = video_devdata(file); struct tbs_videofile_instance *file_instance = container_of(priv, struct tbs_videofile_instance, fh); int res = vb2_verify_memory_type(&file_instance->queue, p->memory, p->type); u32 flags = p->flags; fill_buf_caps(&file_instance->queue, &p->capabilities); validate_memory_flags(&file_instance->queue, p->memory, &flags); p->flags = flags; if (res) return res; if (vb2_queue_is_busy(&file_instance->queue, file)) return -EBUSY; res = vb2_core_reqbufs(&file_instance->queue, p->memory, p->flags, &p->count); /* If count == 0, then the owner has released all buffers and he is no longer owner of the queue. Otherwise we have a new owner. */ if (res == 0) file_instance->queue.owner = p->count ? file->private_data : NULL; return res; } static int tbs_vidioc_create_bufs(struct file *file, void *priv, struct v4l2_create_buffers *p) { // struct video_device *vdev = video_devdata(file); struct tbs_videofile_instance *file_instance = container_of(priv, struct tbs_videofile_instance, fh); int res = vb2_verify_memory_type(&file_instance->queue, p->memory, p->format.type); p->index = file_instance->queue.num_buffers; fill_buf_caps(&file_instance->queue, &p->capabilities); validate_memory_flags(&file_instance->queue, p->memory, &p->flags); /* * If count == 0, then just check if memory and type are valid. * Any -EBUSY result from vb2_verify_memory_type can be mapped to 0. */ if (p->count == 0) return res != -EBUSY ? res : 0; if (res) return res; if (vb2_queue_is_busy(&file_instance->queue, file)) return -EBUSY; res = vb2_create_bufs(&file_instance->queue, p); if (res == 0) file_instance->queue.owner = file->private_data; return res; } static int tbs_vidioc_prepare_buf(struct file *file, void *priv, struct v4l2_buffer *p) { struct video_device *vdev = video_devdata(file); struct tbs_videofile_instance *file_instance = container_of(priv, struct tbs_videofile_instance, fh); if (vb2_queue_is_busy(&file_instance->queue, file)) return -EBUSY; return vb2_prepare_buf(&file_instance->queue, vdev->v4l2_dev->mdev, p); } static int tbs_vidioc_querybuf(struct file *file, void *priv, struct v4l2_buffer *p) { // struct video_device *vdev = video_devdata(file); struct tbs_videofile_instance *file_instance = container_of(priv, struct tbs_videofile_instance, fh); /* No need to call vb2_queue_is_busy(), anyone can query buffers. */ return vb2_querybuf(&file_instance->queue, p); } static int tbs_vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *p) { struct video_device *vdev = video_devdata(file); struct tbs_videofile_instance *file_instance = container_of(priv, struct tbs_videofile_instance, fh); if (vb2_queue_is_busy(&file_instance->queue, file)) return -EBUSY; return vb2_qbuf(&file_instance->queue, vdev->v4l2_dev->mdev, p); } static int tbs_vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p) { // struct video_device *vdev = video_devdata(file); struct tbs_videofile_instance *file_instance = container_of(priv, struct tbs_videofile_instance, fh); if (vb2_queue_is_busy(&file_instance->queue, file)) return -EBUSY; return vb2_dqbuf(&file_instance->queue, p, file->f_flags & O_NONBLOCK); } static int tbs_vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i) { // struct video_device *vdev = video_devdata(file); struct tbs_videofile_instance *file_instance = container_of(priv, struct tbs_videofile_instance, fh); if (vb2_queue_is_busy(&file_instance->queue, file)) return -EBUSY; return vb2_streamon(&file_instance->queue, i); } static int tbs_vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i) { // struct video_device *vdev = video_devdata(file); struct tbs_videofile_instance *file_instance = container_of(priv, struct tbs_videofile_instance, fh); if (vb2_queue_is_busy(&file_instance->queue, file)) return -EBUSY; return vb2_streamoff(&file_instance->queue, i); } #endif static int tbs_vidioc_get_ctrl(struct file *file, void *fh, struct v4l2_tbs_data * data) { struct tbs_video *videodev = video_drvdata(file); struct tbs_pcie_dev *dev = videodev->dev; data->value = TBS_PCIE_READ(data->baseaddr, data->reg); //printk("read :baseaddr=0x%x, reg=0x%x, value=0x%x\n", data->baseaddr, data->reg, data->value); return 0; } static int tbs_vidioc_set_ctrl(struct file *file, void *fh, struct v4l2_tbs_data * data) { struct tbs_video *videodev = video_drvdata(file); struct tbs_pcie_dev *dev = videodev->dev; //printk("write: baseaddr=0x%x, reg=0x%x, value=0x%x\n", data->baseaddr, data->reg, data->value); TBS_PCIE_WRITE(data->baseaddr,data->reg,data->value); return 0; } static const struct v4l2_ioctl_ops tbs_ioctl_fops = { .vidioc_querycap = tbs_vidioc_querycap, .vidioc_enum_fmt_vid_cap = tbs_vidioc_enum_fmt_vid_cap, .vidioc_g_fmt_vid_cap = tbs_vidioc_g_fmt_vid_cap, // .vidioc_g_fmt_vid_cap = tbs_vidioc_g_fmt_vid_cap_mplane, .vidioc_try_fmt_vid_cap = tbs_vidioc_try_fmt_vid_cap, // .vidioc_try_fmt_vid_cap = tbs_vidioc_try_fmt_vid_cap_mplane, .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, // .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap_mplane, // .vidioc_reqbufs = vb2_ioctl_reqbufs, // .vidioc_prepare_buf = vb2_ioctl_prepare_buf, // .vidioc_create_bufs = vb2_ioctl_create_bufs, // .vidioc_querybuf = vb2_ioctl_querybuf, // .vidioc_qbuf = vb2_ioctl_qbuf, // .vidioc_dqbuf = vb2_ioctl_dqbuf, // .vidioc_streamon = vb2_ioctl_streamon, // .vidioc_streamoff = vb2_ioctl_streamoff, .vidioc_reqbufs = tbs_vidioc_reqbufs, .vidioc_prepare_buf = tbs_vidioc_prepare_buf, .vidioc_create_bufs = tbs_vidioc_create_bufs, .vidioc_querybuf = tbs_vidioc_querybuf, .vidioc_qbuf = tbs_vidioc_qbuf, .vidioc_dqbuf = tbs_vidioc_dqbuf, .vidioc_streamon = tbs_vidioc_streamon, .vidioc_streamoff = tbs_vidioc_streamoff, .vidioc_querystd = tbs_vidioc_querystd, .vidioc_g_std = tbs_vidioc_g_std, .vidioc_s_std = tbs_vidioc_s_std, .vidioc_enum_input = tbs_vidioc_enum_input, .vidioc_g_input = tbs_vidioc_g_input, .vidioc_s_input = tbs_vidioc_s_input, // .vidioc_log_status = vidioc_log_status, .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, .vidioc_unsubscribe_event = v4l2_event_unsubscribe, // .vidioc_g_parm = tbs_vidioc_g_parm, .vidioc_tbs_g_ctrls = tbs_vidioc_get_ctrl, .vidioc_tbs_s_ctrls = tbs_vidioc_set_ctrl, }; static int tbs_open(struct file *file) { struct tbs_video *videodev = video_drvdata(file); unsigned char * filebuf=NULL; struct tbs_videofile_instance *file_instance; struct vb2_queue *vb_q ; int err; //printk( "%s() index:%x entry\n", __func__,videodev->index); tbs_get_video_param(videodev); if(videodev->dst_width<=DEFAULT_WIDTH || videodev->height <= DEFAULT_HEIGH){ //printk(KERN_ERR "%s 1 \n", __func__); //return -1; } filebuf = kvmalloc(FILE_INSTANCE_SIZE,GFP_KERNEL); if(!filebuf){ printk(KERN_ERR "%s 2 \n", __func__); return -1; } //memset((void*)filebuf,0,FILE_INSTANCE_SIZE); file_instance = kzalloc(sizeof (struct tbs_videofile_instance), GFP_KERNEL); if(!file_instance){ printk(KERN_ERR "%s 3 \n", __func__); kvfree(filebuf); return -1; } mutex_init(&(file_instance->queue_lock)); INIT_LIST_HEAD(&file_instance->list); spin_lock_init(&file_instance->slock); v4l2_fh_init(&file_instance->fh, &videodev->vdev); v4l2_fh_add(&file_instance->fh); file_instance->imgbuf0 = filebuf; file_instance->imgbuf1 = filebuf+DMA_VIDEO_TOTAL; vb_q = &(file_instance->queue); vb_q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; vb_q->io_modes = VB2_MMAP|VB2_DMABUF|VB2_READ|VB2_USERPTR; //vb_q->min_queued_buffers=1; vb_q->min_buffers_needed=1; vb_q->drv_priv = videodev; vb_q->buf_struct_size = sizeof(struct tbsvideo_buffer); vb_q->ops = &tbspcie_video_qops; vb_q->mem_ops = &vb2_vmalloc_memops; // vb_q->mem_ops = &vb2_dma_sg_memops; // vb_q->mem_ops = &vb2_dma_contig_memops; vb_q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; // vb_q->gfp_flags=GFP_HIGHUSER; vb_q->lock = &(file_instance->queue_lock); vb_q->dev = &(videodev->dev->pdev->dev); err = vb2_queue_init(vb_q); if(err != 0){ printk(KERN_ERR " vb2_queue_init failed !!!!! \n"); v4l2_fh_del(&file_instance->fh); kvfree(filebuf); kfree(file_instance); return -1; } file->private_data = &file_instance->fh; videodev->runstatus++; videodev->dev->signal_ready=1; wake_up(&videodev->dev->wq); //printk( "%s() index:%x success \n", __func__,videodev->index); return 0; } static int tbs_close(struct file *file) { struct tbs_video *videodev = video_drvdata(file); struct tbs_videofile_instance *file_instance; file_instance = container_of(file->private_data, struct tbs_videofile_instance, fh); //printk( "%s() index:%x \n", __func__,videodev->index); if(videodev->runstatus>0) videodev->runstatus--; if(file_instance){ vb2_queue_release(&file_instance->queue); if(file_instance->imgbuf0){ kvfree(file_instance->imgbuf0); file_instance->imgbuf0=NULL; file_instance->imgbuf1=NULL; } v4l2_fh_del(&file_instance->fh); kfree(file_instance); } return 0; } static int tbs_mmap(struct file *file, struct vm_area_struct *vma) { struct tbs_videofile_instance *file_instance; file_instance = container_of(file->private_data, struct tbs_videofile_instance, fh); //printk( "%s() \n", __func__); return vb2_mmap(&file_instance->queue, vma); } static __poll_t tbs_poll(struct file *file, poll_table *wait) { struct video_device *vdev = video_devdata(file); struct tbs_videofile_instance *file_instance = container_of(file->private_data, struct tbs_videofile_instance, fh); struct vb2_queue *q = &file_instance->queue; struct mutex *lock = q->lock ? q->lock : vdev->lock; __poll_t res; void *fileio; /* * If this helper doesn't know how to lock, then you shouldn't be using * it but you should write your own. */ WARN_ON(!lock); if (lock && mutex_lock_interruptible(lock)) return EPOLLERR; fileio = q->fileio; res = vb2_poll(q, file, wait); /* If fileio was started, then we have a new queue owner. */ if (!fileio && q->fileio) q->owner = file->private_data; if (lock) mutex_unlock(lock); return res; } static ssize_t tbs_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) { struct video_device *vdev = video_devdata(file); struct tbs_videofile_instance *file_instance = container_of(file->private_data, struct tbs_videofile_instance, fh); struct mutex *lock = file_instance->queue.lock ? file_instance->queue.lock : vdev->lock; int err = -EBUSY; if (!(file_instance->queue.io_modes & VB2_READ)) return -EINVAL; if (lock && mutex_lock_interruptible(lock)) return -ERESTARTSYS; if (vb2_queue_is_busy(&file_instance->queue, file)) goto exit; file_instance->queue.owner = file->private_data; err = vb2_read(&file_instance->queue, buf, count, ppos, file->f_flags & O_NONBLOCK); if (!file_instance->queue.fileio) file_instance->queue.owner = NULL; exit: if (lock) mutex_unlock(lock); return err; } static const struct v4l2_file_operations tbs_fops = { .owner = THIS_MODULE, .open = tbs_open,//v4l2_fh_open, .release = tbs_close, .read = tbs_read, .poll = tbs_poll, .unlocked_ioctl = video_ioctl2, .mmap = tbs_mmap, }; static int tbs_i2c_xfer(struct i2c_adapter *adapter,struct i2c_msg msg[], int num) { struct tbs_i2c *i2c = i2c_get_adapdata(adapter); struct tbs_pcie_dev *dev = i2c->dev; u8 tmpbuf[8]; u32 data0 = 0; int timeout; int i =0; if (num == 2 && msg[1].flags & I2C_M_RD && !(msg[0].flags & I2C_M_RD)) { //test tmpbuf[0] =0x81; tmpbuf[1] = msg[0].addr; tmpbuf[1] &=0xfe; tmpbuf[2] = msg[0].buf[0]; //if (TBS_PCIE_READ(i2c->base, TBS_I2C_CTRL) == 1); TBS_PCIE_READ(i2c->base, TBS_I2C_CTRL); i2c->ready = 0; TBS_PCIE_WRITE(i2c->base, TBS_I2C_CTRL, *(u32 *)&tmpbuf[0]); timeout = wait_event_timeout(i2c->wq, i2c->ready == 1, HZ); if (timeout <= 0) { printk(KERN_ERR "TBS PCIE I2C%d timeout\n", i2c->i2c_dev); return -EIO; } tmpbuf[0] =0x80; tmpbuf[1] = msg[0].addr; tmpbuf[1] &=0xfe; tmpbuf[1] +=1; // read operation; if (msg[1].len <= 4) { tmpbuf[0] |= 0x40; } else { printk(KERN_ERR "TBS PCIE I2C%d read limit is 4 bytes\n", i2c->i2c_dev); return -EIO; } tmpbuf[0] += msg[1].len; //if (TBS_PCIE_READ(i2c->base, TBS_I2C_CTRL) == 1); TBS_PCIE_READ(i2c->base, TBS_I2C_CTRL); i2c->ready = 0; TBS_PCIE_WRITE(i2c->base, TBS_I2C_CTRL, *(u32 *)&tmpbuf[0]); // timeout of 1 sec timeout = wait_event_timeout(i2c->wq, i2c->ready == 1, HZ); if (timeout <= 0) { printk(KERN_ERR "TBS PCIE I2C%d timeout\n", i2c->i2c_dev); return -EIO;} data0 = TBS_PCIE_READ(i2c->base, TBS_I2C_DATA); memcpy(msg[1].buf, &data0, msg[1].len); return num; } if (num == 1 && !(msg[0].flags & I2C_M_RD)) { tmpbuf[0] =0x80 + msg[0].len; tmpbuf[1] = msg[0].addr; tmpbuf[1] &=0xfe; tmpbuf[0] |= 0x40; // add stop for(i =0;ibase, TBS_I2C_CTRL) == 1); TBS_PCIE_READ(i2c->base, TBS_I2C_CTRL); i2c->ready = 0; TBS_PCIE_WRITE(i2c->base, TBS_I2C_CTRL, *(u32 *)&tmpbuf[0]); // timeout of 1 sec timeout = wait_event_timeout(i2c->wq, i2c->ready == 1, HZ); if (timeout <= 0) { printk(KERN_ERR "TBS PCIE I2C%d timeout\n", i2c->i2c_dev); return -EIO; } return num; } if (num == 1 && (msg[0].flags & I2C_M_RD)) { printk(KERN_INFO "TBS PCIE I2C%d not implemented\n", i2c->i2c_dev); return num; } return -EIO; } static u32 tbs_i2c_func(struct i2c_adapter *adap) { return I2C_FUNC_SMBUS_EMUL; } struct i2c_algorithm tbs_i2c_algo = { .master_xfer = tbs_i2c_xfer, .functionality = tbs_i2c_func, }; static int tbs_i2c_init(struct tbs_pcie_dev *dev) { struct tbs_i2c *i2c; struct i2c_adapter *adap; int i, j, err = 0; dev->i2c_bus[0].base = TBS_I2C_BASE_0; dev->i2c_bus[1].base = TBS_I2C_BASE_1; dev->i2c_bus[2].base = TBS_I2C_BASE_2; dev->i2c_bus[3].base = TBS_I2C_BASE_3; for (i = 0; i < INTERFACES; i++) { i2c = &dev->i2c_bus[i]; i2c->dev = dev; i2c->i2c_dev = i; INIT_WORK(&i2c->i2cwork,i2c_wake_process); init_waitqueue_head(&i2c->wq); adap = &i2c->i2c_adap; i2c_set_adapdata(adap, i2c); sprintf(adap->name,"tbs_i2c_%d",i); adap->algo = &tbs_i2c_algo; adap->algo_data = dev; adap->dev.parent = &dev->pdev->dev; err = i2c_add_adapter(adap); if (err) goto fail; } /* enable i2c interrupts */ TBS_PCIE_WRITE(TBS_INT_BASE, TBS_INT_ENABLE, 0x00000001); TBS_PCIE_WRITE(TBS_INT_BASE, TBS_I2C_MASK_0, 0x00000001); TBS_PCIE_WRITE(TBS_INT_BASE, TBS_I2C_MASK_1, 0x00000001); TBS_PCIE_WRITE(TBS_INT_BASE, TBS_I2C_MASK_2, 0x00000001); TBS_PCIE_WRITE(TBS_INT_BASE, TBS_I2C_MASK_3, 0x00000001); return 0; fail: for (j = 0; j < i; j++) { i2c = &dev->i2c_bus[j]; adap = &i2c->i2c_adap; i2c_del_adapter(adap); } return err; } static void tbs_i2c_exit(struct tbs_pcie_dev *dev) { struct tbs_i2c *i2c; struct i2c_adapter *adap; int i; TBS_PCIE_WRITE(TBS_INT_BASE, TBS_INT_ENABLE, 0x00000000); TBS_PCIE_WRITE(TBS_INT_BASE, TBS_I2C_MASK_0, 0x00000000); TBS_PCIE_WRITE(TBS_INT_BASE, TBS_I2C_MASK_1, 0x00000000); TBS_PCIE_WRITE(TBS_INT_BASE, TBS_I2C_MASK_2, 0x00000000); TBS_PCIE_WRITE(TBS_INT_BASE, TBS_I2C_MASK_3, 0x00000000); for (i = 0; i < INTERFACES; i++) { i2c = &dev->i2c_bus[i]; adap = &i2c->i2c_adap; i2c_del_adapter(adap); } } static irqreturn_t tbs_pcie_irq(int irq, void *dev_id) { struct tbs_pcie_dev *dev = (struct tbs_pcie_dev *) dev_id; struct tbs_i2c *i2c; u32 stat; u32 ret; unsigned char index; stat = TBS_PCIE_READ(TBS_INT_BASE, TBS_INT_STATUS); TBS_PCIE_WRITE(TBS_INT_BASE, TBS_INT_STATUS, stat); if ((stat & 0x00000fff) == 0) { TBS_PCIE_WRITE(TBS_INT_BASE, TBS_INT_ENABLE, 0x00000001); return IRQ_NONE; } // printk( "%s(): stat:%x \n", __func__, stat); //hdmi interface 0 if (stat & 0x00000010){ //audio 0 ret = TBS_PCIE_READ(TBS_DMA_BASE_0, 0); //printk( "%s(): audio 0 %x \n", __func__, ret); index = (ret)&(TBS_AUDIO_CELLS-1); dev->audio[0].pos = index *TBS_AUDIO_CELL_SIZE; snd_pcm_period_elapsed(dev->audio[0].substream); //queue_work(wq,&dev->audio[0].audiowork); } if (stat & 0x00000020){//video 0 dev->video[0].videostatus= TBS_PCIE_READ(TBS_DMA_BASE_1, 0); //printk( "%s(): video 0 %x \n", __func__, dev->video[0].videostatus); if(dev->video[0].Interlaced){ if(dev->video[0].videostatus & 0x1000000){ queue_work(wq,&dev->video[0].videowork); } }else{ queue_work(wq,&dev->video[0].videowork); } } //hdmi interface 1 if (stat & 0x00000040){ //audio 1 ret = TBS_PCIE_READ(TBS_DMA_BASE_2, 0); //printk( "%s(): audio 1 %x \n", __func__, ret); index = (ret)&(TBS_AUDIO_CELLS-1); dev->audio[1].pos = index *TBS_AUDIO_CELL_SIZE; snd_pcm_period_elapsed(dev->audio[1].substream); //queue_work(wq,&dev->audio[1].audiowork); } if (stat & 0x00000080){//video 1 dev->video[1].videostatus = TBS_PCIE_READ(TBS_DMA_BASE_3, 0); //printk( "%s(): video 1 %x \n", __func__, dev->video[1].videostatus); if(dev->video[1].Interlaced){ if(dev->video[1].videostatus & 0x1000000){ queue_work(wq,&dev->video[1].videowork); } }else{ queue_work(wq,&dev->video[1].videowork); } } //hdmi interface 2 if (stat & 0x00000100){ //audio 2 ret = TBS_PCIE_READ(TBS_DMA_BASE_4, 0); //printk( "%s(): audio 2 %x \n", __func__, ret); index = (ret)&(TBS_AUDIO_CELLS-1); dev->audio[2].pos = index *TBS_AUDIO_CELL_SIZE; snd_pcm_period_elapsed(dev->audio[2].substream); //queue_work(wq,&dev->audio[2].audiowork); } if (stat & 0x00000200){//video 2 dev->video[2].videostatus = TBS_PCIE_READ(TBS_DMA_BASE_5, 0); //printk( "%s(): video 2 %x \n", __func__, dev->video[2].videostatus); if(dev->video[2].Interlaced){ if(dev->video[2].videostatus & 0x1000000){ queue_work(wq,&dev->video[2].videowork); } }else{ queue_work(wq,&dev->video[2].videowork); } } //hdmi interface 3 if (stat & 0x00000400){ //audio 3 ret = TBS_PCIE_READ(TBS_DMA_BASE_6, 0); //printk( "%s(): audio 3 %x \n", __func__, ret); index = (ret)&(TBS_AUDIO_CELLS-1); dev->audio[3].pos = index *TBS_AUDIO_CELL_SIZE; snd_pcm_period_elapsed(dev->audio[3].substream); //queue_work(wq,&dev->audio[3].audiowork); } if (stat & 0x00000800){//video 3 dev->video[3].videostatus = TBS_PCIE_READ(TBS_DMA_BASE_7, 0); //printk( "%s(): video 3 %x \n", __func__, dev->video[3].videostatus); if(dev->video[3].Interlaced){ if(dev->video[3].videostatus & 0x1000000){ queue_work(wq,&dev->video[3].videowork); } }else{ queue_work(wq,&dev->video[3].videowork); } } if (stat & 0x00000001) { i2c = &dev->i2c_bus[0]; TBS_PCIE_READ(i2c->base, TBS_I2C_CTRL); queue_work(wq,&i2c->i2cwork); } if (stat & 0x00000002) { i2c = &dev->i2c_bus[1]; TBS_PCIE_READ(i2c->base, TBS_I2C_CTRL); queue_work(wq,&i2c->i2cwork); } if (stat & 0x00000004) { i2c = &dev->i2c_bus[2]; TBS_PCIE_READ(i2c->base, TBS_I2C_CTRL); queue_work(wq,&i2c->i2cwork); } if (stat & 0x00000008) { i2c = &dev->i2c_bus[3]; TBS_PCIE_READ(i2c->base, TBS_I2C_CTRL); queue_work(wq,&i2c->i2cwork); } /* enable interrupt */ TBS_PCIE_WRITE(TBS_INT_BASE, TBS_INT_ENABLE, 0x00000001); return IRQ_HANDLED; } static int tbs_get_video_param(struct tbs_video *pvideo) { struct i2c_adapter *tbs_adap = &pvideo->dev->i2c_bus[pvideo->index>>1].i2c_adap ; struct tbs_pcie_dev *dev = pvideo->dev; unsigned char tmp[10]; unsigned int width; unsigned int height; unsigned int interlaced = 0; unsigned int tmp_B, fps = 0; mutex_lock(&dev->devicemutex); i2c_read_reg(tbs_adap, 0x98, 0x6f, tmp, 1); if ((tmp[0] & 0x01) == 0x1) { i2c_read_reg(tbs_adap, 0x68, 0x07, tmp, 2); width = (tmp[0] & 0x1f) * 256 + tmp[1]; // printk("pix:%d ", width); i2c_read_reg(tbs_adap,0x68, 0x09, tmp, 2); height = (tmp[0] & 0x1f) * 256 + tmp[1]; // printk("line:%d \n", height); i2c_read_reg(tbs_adap,0x44, 0xb8, tmp, 2); tmp_B = ((tmp[0] & 0x1f) << 8) + tmp[1]; if (tmp_B) fps = (unsigned char)((103663 + (tmp_B - 1)) / tmp_B); //printk("HDMI tmp_B:%x fps:%d \n", tmp_B, fps); i2c_read_reg(tbs_adap, 0x68, 0x0b, tmp, 1); if (tmp[0] & 0x20) { //printk("HDMI Interlaced Input:%x \n", tmp[0]); height <<= 1; interlaced = 1; tmp[0] = 0x06; tmp[1] = 0x24; i2c_write_reg(tbs_adap, 0x98, tmp, 2); } else { //printk("HDMI Progressive Input:%x \n", tmp[0]); interlaced = 0; tmp[0] = 0x06; tmp[1] = 0xA4; i2c_write_reg(tbs_adap, 0x98, tmp, 2); } //printk("line:%d \n", height); mutex_unlock(&dev->devicemutex); if (width == 0 || height == 0 || height > 1080 || width > 1920) { //printk("HDMI cable %d image size error width:%d height:%d\n",(pvideo->index>>1),width, height); pvideo->dst_width=DEFAULT_WIDTH; pvideo->dst_height=DEFAULT_HEIGH; pvideo->present=0; return -1; } }else { mutex_unlock(&dev->devicemutex); //printk("HDMI cable %d is not connected!\n",(pvideo->index>>1)); pvideo->dst_width=DEFAULT_WIDTH; pvideo->dst_height=DEFAULT_HEIGH; pvideo->present=0; return -1; } pvideo->Interlaced = interlaced; pvideo->fps = fps >> interlaced; pvideo->dst_width= pvideo->width = width; pvideo->dst_height= pvideo->height = height; pvideo->present=1; return 0; } static void tbs_adapters_reset( struct i2c_adapter *tbs_adap) { int i; u8 tmp[2]; for(i=0;i<3;i++){ //read 7611 id and init chip here i2c_read_reg(tbs_adap,0x98, 0xea,tmp, 2); printk("7611 chip id(%d) : %x, %x\n", i,tmp[0],tmp[1]); if((tmp[0] == 0x20)&&(tmp[1] == 0x51)) { //reset tmp[0] = 0xff; tmp[1] = 0x80; i2c_write_reg(tbs_adap,0x98, tmp,2); msleep(200);//sleep i2c_write_tab_new(tbs_adap, fw7611); msleep(200); break; } } } static void tbs_adapters_init(struct tbs_pcie_dev *dev) { struct i2c_adapter *tbs_adap; struct tbs_video *pvideo; int i; /* disable all interrupts */ //TBS_PCIE_WRITE(TBS_INT_BASE, TBS_INT_ENABLE, 0x00000001); /* disable dma */ TBS_PCIE_WRITE(TBS_DMA_BASE_0, TBS_DMA_START, 0x00000000); TBS_PCIE_WRITE(TBS_DMA_BASE_1, TBS_DMA_START, 0x00000000); TBS_PCIE_WRITE(TBS_DMA_BASE_2, TBS_DMA_START, 0x00000000); TBS_PCIE_WRITE(TBS_DMA_BASE_3, TBS_DMA_START, 0x00000000); TBS_PCIE_WRITE(TBS_DMA_BASE_4, TBS_DMA_START, 0x00000000); TBS_PCIE_WRITE(TBS_DMA_BASE_5, TBS_DMA_START, 0x00000000); TBS_PCIE_WRITE(TBS_DMA_BASE_6, TBS_DMA_START, 0x00000000); TBS_PCIE_WRITE(TBS_DMA_BASE_7, TBS_DMA_START, 0x00000000); for (i = 0; i < INTERFACES; i++) { tbs_adap = &dev->i2c_bus[i].i2c_adap; dev->i2c_bus[i].dev = dev; //printk( "\n%s(): %x \n", __func__, i); tbs_adapters_reset(tbs_adap); pvideo = &dev->video[i]; pvideo->dev=dev; pvideo->index = i*2+1; tbs_get_video_param(pvideo); } } static int tbs_video_register(struct tbs_pcie_dev *dev) { struct video_device *vdev ; int i; int err=-1; for(i=0;ipdev->dev, &dev->video[i].v4l2_dev); if(err<0){ printk(KERN_ERR " v4l2_device_register %d error! \n",i); goto fail; } dev->video[i].index = i*2+1; dev->video[i].dev = dev; mutex_init(&(dev->video[i].video_lock)); vdev = &(dev->video[i].vdev); vdev->queue = NULL; vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING ; vdev->tvnorms =TBS_NORMS; vdev->vfl_dir=VFL_DIR_RX; vdev->vfl_type=VFL_TYPE_VIDEO; vdev->v4l2_dev = &(dev->video[i].v4l2_dev); vdev->lock = &(dev->video[i].video_lock); vdev->fops = &tbs_fops; vdev->ioctl_ops = &tbs_ioctl_fops; vdev->release = video_device_release_empty; strcpy(vdev->name,KBUILD_MODNAME); video_set_drvdata(vdev, &(dev->video[i])); INIT_WORK(&dev->video[i].videowork,video_wake_process); init_waitqueue_head(&dev->video[i].wq); dev->video[i].dmabuf[0].virtaddr = dma_alloc_coherent(&dev->pdev->dev, DMA_VIDEO_TOTAL, &dev->video[i].dmabuf[0].dma,GFP_DMA32); if (!dev->video[i].dmabuf[0].virtaddr) { printk(" allocate memory 0 failed\n"); goto fail; } dev->video[i].dmabuf[1].virtaddr = dma_alloc_coherent(&dev->pdev->dev, DMA_VIDEO_TOTAL, &dev->video[i].dmabuf[1].dma,GFP_DMA32); if (!dev->video[i].dmabuf[1].virtaddr) { printk(" allocate memory 1 failed\n"); goto fail; } dev->video[i].dmabuf[2].virtaddr = dma_alloc_coherent(&dev->pdev->dev, DMA_VIDEO_TOTAL, &dev->video[i].dmabuf[2].dma,GFP_DMA32); if (!dev->video[i].dmabuf[2].virtaddr) { printk(" allocate memory 2 failed\n"); goto fail; } err = video_register_device(vdev, VFL_TYPE_VIDEO,-1); if(err!=0){ printk(KERN_ERR " v4l2_device_register failed !!!!! \n"); goto fail; }else{ printk(" TBS6314R video %d register OK ! \n",i); } } return 0; fail: for(i=0;ivideo[i].dmabuf[0].virtaddr){ dma_free_coherent(&dev->pdev->dev, DMA_VIDEO_TOTAL, dev->video[i].dmabuf[0].virtaddr, dev->video[i].dmabuf[0].dma); dev->video[i].dmabuf[0].virtaddr =NULL; } if(dev->video[i].dmabuf[1].virtaddr){ dma_free_coherent(&dev->pdev->dev, DMA_VIDEO_TOTAL, dev->video[i].dmabuf[1].virtaddr, dev->video[i].dmabuf[1].dma); dev->video[i].dmabuf[1].virtaddr =NULL; } if(dev->video[i].dmabuf[2].virtaddr){ dma_free_coherent(&dev->pdev->dev, DMA_VIDEO_TOTAL, dev->video[i].dmabuf[2].virtaddr, dev->video[i].dmabuf[2].dma); dev->video[i].dmabuf[2].virtaddr =NULL; } vdev = &dev->video[i].vdev; video_unregister_device(vdev); v4l2_device_unregister(&dev->video[i].v4l2_dev); } return err; } /* HDMI 0x39[3:0] - CS_DATA[27:24] 0 for reserved values*/ static const int cs_data_fs[] = { 44100, 0, 48000, 32000, 0, 0, 0, 0, 88200, 768000, 96000, 0, 176000, 0, 192000, 0, }; static struct snd_pcm_hardware mycard_capture_stero ={ .info = (SNDRV_PCM_INFO_INTERLEAVED |SNDRV_PCM_INFO_BLOCK_TRANSFER |SNDRV_PCM_INFO_MMAP|SNDRV_PCM_INFO_MMAP_VALID), .formats = (SNDRV_PCM_FMTBIT_S16_LE), .rates = SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_8000_192000, .rate_min = 8000, .rate_max = 192000, .channels_min = 2, .channels_max =2, .period_bytes_min = TBS_AUDIO_CELL_SIZE, .period_bytes_max = TBS_AUDIO_CELL_SIZE, .periods_min = TBS_AUDIO_CELLS, .periods_max = TBS_AUDIO_CELLS, .buffer_bytes_max = TBS_AUDIO_CELL_SIZE*TBS_AUDIO_CELLS, }; static int tbs_pcie_audio_open(struct snd_pcm_substream *substream) { struct tbs_audio *chip = snd_pcm_substream_chip(substream); struct snd_pcm_runtime *runtime = substream->runtime; struct i2c_adapter *tbs_adap; unsigned int rate,setrate=44100; u8 tmp[2]; //printk(KERN_INFO "%s() index:%x runstatus:%d substream:%p runtime:%p \n",__func__,chip->index,chip->runstatus,substream,runtime); // if(chip->dev->video[chip->index>>1].present ==0) // return -1; chip->runstatus++; chip->substream = substream; runtime->hw = mycard_capture_stero; tbs_adap = &chip->dev->i2c_bus[chip->index>>1].i2c_adap; i2c_read_reg(tbs_adap,0x68, 0x39,tmp, 1); rate = cs_data_fs[tmp[0]&15]; if(rate) setrate = rate; setrate = 48000; //printk(KERN_INFO "%s() rate:%d setrate:%d tmp[0]:%d\n",__func__, rate,setrate,tmp[0]&15); snd_pcm_hw_constraint_minmax(runtime,SNDRV_PCM_HW_PARAM_RATE,setrate,setrate); return 0; } static int tbs_pcie_audio_close(struct snd_pcm_substream *substream) { struct tbs_audio *chip = snd_pcm_substream_chip(substream); chip->runstatus--; //printk(KERN_INFO "%s() index:%x\n",__func__,chip->index); return 0; } static int tbs_pcie_audio_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params) { struct tbs_audio *chip = snd_pcm_substream_chip(substream); //printk(KERN_INFO "%s() index:%x\n",__func__,chip->index); return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params)); } static int tbs_pcie_audio_hw_free(struct snd_pcm_substream *substream) { struct tbs_audio *chip = snd_pcm_substream_chip(substream); struct tbs_pcie_dev *dev= chip->dev; //printk(KERN_INFO "%s() index:%x\n",__func__,chip->index); TBS_PCIE_READ(TBS_DMA_BASE(chip->index), TBS_DMA_STATUS); //stop dma TBS_PCIE_WRITE(TBS_INT_BASE, TBS_DMA_MASK(chip->index), 0x000000000); TBS_PCIE_WRITE(TBS_DMA_BASE(chip->index), TBS_DMA_START, 0x00000000); return snd_pcm_lib_free_pages(substream); } static int tbs_pcie_audio_prepare(struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; struct tbs_audio *chip = snd_pcm_substream_chip(substream); struct tbs_pcie_dev *dev= chip->dev; //printk(KERN_INFO "%s() index:%x\n",__func__,chip->index); //set dma address: TBS_PCIE_WRITE(TBS_DMA_BASE(chip->index), TBS_DMA_ADDR_HIGH, 0); TBS_PCIE_WRITE(TBS_DMA_BASE(chip->index), TBS_DMA_ADDR_LOW, runtime->dma_addr); //write dma size TBS_PCIE_WRITE(TBS_DMA_BASE(chip->index), TBS_DMA_SIZE,TBS_AUDIO_CELL_SIZE*TBS_AUDIO_CELLS ); // write picture size TBS_PCIE_WRITE(TBS_DMA_BASE(chip->index), TBS_DMA_CELL_SIZE, TBS_AUDIO_CELL_SIZE); TBS_PCIE_READ(TBS_DMA_BASE(chip->index), TBS_DMA_STATUS); //start dma TBS_PCIE_WRITE(TBS_INT_BASE, TBS_DMA_MASK(chip->index), 0x00000001); TBS_PCIE_WRITE(TBS_DMA_BASE(chip->index), TBS_DMA_START, 0x00000001); chip->pos=0; return 0; } static int tbs_pcie_audio_trigger(struct snd_pcm_substream *substream, int cmd) { struct tbs_audio *chip = snd_pcm_substream_chip(substream); switch(cmd){ case SNDRV_PCM_TRIGGER_START: //printk(KERN_INFO "SNDRV_PCM_TRIGGER_START index:%x\n",chip->index); break; case SNDRV_PCM_TRIGGER_STOP: //printk(KERN_INFO "SNDRV_PCM_TRIGGER_STOP index:%x\n",chip->index); break; default: return -EINVAL; break; } return 0; } static snd_pcm_uframes_t tbs_pcie_audio_pointer(struct snd_pcm_substream *substream) { struct tbs_audio *chip = snd_pcm_substream_chip(substream); struct snd_pcm_runtime *runtime = substream->runtime; // printk(KERN_INFO "%s() index:%x\n",__func__,chip->index); return bytes_to_frames(runtime,chip->pos); } #if LINUX_VERSION_CODE < KERNEL_VERSION(6, 6, 0) static int tbs_pcie_audio_copy_user(struct snd_pcm_substream *substream, int channel, unsigned long pos, void __user *dst, unsigned long count) { struct snd_pcm_runtime *runtime = substream->runtime; int ret; ret = copy_to_user(dst,runtime->dma_area+pos,count); return 0; } #elif LINUX_VERSION_CODE < KERNEL_VERSION(6, 14, 0) static int tbs_pcie_audio_copy(struct snd_pcm_substream *substream, int channel, unsigned long pos, struct iov_iter *iter, unsigned long bytes) { struct snd_pcm_runtime *runtime = substream->runtime; int ret; // printk(KERN_INFO "%s() index:%x\n",__func__,chip->index); ret = copy_to_iter_fromio(iter,runtime->dma_area+pos,bytes); return 0; }; #else static int tbs_pcie_audio_copy(struct snd_pcm_substream *substream, int channel, unsigned long pos, struct iov_iter *iter, unsigned long bytes) { struct snd_pcm_runtime *runtime = substream->runtime; size_t ret; // printk(KERN_INFO "%s() index:%x\n",__func__,chip->index); ret = copy_to_iter_fromio(runtime->dma_area+pos,bytes,iter); return 0; }; #endif struct snd_pcm_ops tbs_pcie_pcm_ops ={ .open = tbs_pcie_audio_open, .close = tbs_pcie_audio_close, .ioctl = snd_pcm_lib_ioctl, .hw_params = tbs_pcie_audio_hw_params, .hw_free = tbs_pcie_audio_hw_free, .prepare = tbs_pcie_audio_prepare, .trigger = tbs_pcie_audio_trigger, .pointer = tbs_pcie_audio_pointer, #if LINUX_VERSION_CODE < KERNEL_VERSION(6, 6, 0) .copy_user = tbs_pcie_audio_copy_user, #else .copy = tbs_pcie_audio_copy, #endif }; static int tbs_audio_register(struct tbs_pcie_dev *dev) { struct snd_card *card; int ret; int i; char audioname[100]; for(i=0;ipdev->dev, -1, audioname, THIS_MODULE, sizeof(struct tbs_audio), &card); if (ret < 0){ printk(KERN_ERR "%s() ERROR: snd_card_new failed <%d>\n",__func__, ret); goto fail0; } strcpy(card->driver, KBUILD_MODNAME); strcpy(card->shortname, audioname); sprintf(card->longname, "%s",audioname); ret = snd_pcm_new(card,audioname,0,0,1,&dev->audio[i].pcm); if (ret < 0){ printk(KERN_ERR "%s() ERROR: snd_pcm_new failed <%d>\n",__func__, ret); goto fail1; } dev->audio[i].index=i*2; dev->audio[i].dev=dev; dev->audio[i].pcm->private_data = &dev->audio[i]; snd_pcm_set_ops(dev->audio[i].pcm,SNDRV_PCM_STREAM_CAPTURE,&tbs_pcie_pcm_ops); snd_pcm_lib_preallocate_pages_for_all(dev->audio[i].pcm, SNDRV_DMA_TYPE_DEV,&dev->pdev->dev, TBS_AUDIO_CELL_SIZE*TBS_AUDIO_CELLS, TBS_AUDIO_CELL_SIZE*TBS_AUDIO_CELLS); ret = snd_card_register(card); if ( ret < 0) { printk(KERN_ERR "%s() ERROR: snd_card_register failed\n",__func__); goto fail1; } INIT_WORK(&dev->audio[i].audiowork,audio_wake_process); dev->audio[i].card =card; } return 0; fail1: for(i=0;iaudio[i].pcm){ snd_pcm_lib_preallocate_free_for_all(dev->audio[i].pcm); } dev->audio[i].pcm=NULL; if(dev->audio[i].card){ snd_card_disconnect(dev->audio[i].card); snd_card_free(dev->audio[i].card); } dev->audio[i].card=NULL; } fail0: return -1; } static void tbs_remove(struct pci_dev *pdev) { struct tbs_pcie_dev *dev = (struct tbs_pcie_dev*) pci_get_drvdata(pdev); struct video_device *vdev; int i; if(dev->signalthread){ kthread_stop(dev->signalthread); dev->signalthread=NULL; } for(i=0;iaudio[i].pcm){ snd_pcm_lib_preallocate_free_for_all(dev->audio[i].pcm); } dev->audio[i].pcm=NULL; if(dev->audio[i].card){ snd_card_disconnect(dev->audio[i].card); snd_card_free(dev->audio[i].card); } dev->audio[i].card=NULL; } tbs_i2c_exit(dev); /* disable interrupts */ for(i=0;ivideo[i].dmabuf[0].virtaddr){ dma_free_coherent(&dev->pdev->dev, DMA_VIDEO_TOTAL,dev->video[i].dmabuf[0].virtaddr, dev->video[i].dmabuf[0].dma); dev->video[i].dmabuf[0].virtaddr =NULL; } if(dev->video[i].dmabuf[1].virtaddr){ dma_free_coherent(&dev->pdev->dev, DMA_VIDEO_TOTAL,dev->video[i].dmabuf[1].virtaddr, dev->video[i].dmabuf[1].dma); dev->video[i].dmabuf[1].virtaddr =NULL; } if(dev->video[i].dmabuf[2].virtaddr){ dma_free_coherent(&dev->pdev->dev, DMA_VIDEO_TOTAL,dev->video[i].dmabuf[2].virtaddr, dev->video[i].dmabuf[2].dma); dev->video[i].dmabuf[2].virtaddr =NULL; } vdev = &dev->video[i].vdev; video_unregister_device(vdev); v4l2_device_unregister(&dev->video[i].v4l2_dev); } free_irq(dev->pdev->irq, dev); if (dev->mmio) iounmap(dev->mmio); pci_disable_device(pdev); pci_set_drvdata(pdev, NULL); kfree(dev); } static void i2c_wake_process(struct work_struct *p_work) { struct tbs_i2c *i2c = container_of(p_work, struct tbs_i2c, i2cwork); i2c->ready =1; wake_up(&i2c->wq); return; } static void audio_wake_process(struct work_struct *p_work) { struct tbs_audio *chip = container_of(p_work, struct tbs_audio, audiowork); snd_pcm_period_elapsed(chip->substream); return; } static void video_wake_process(struct work_struct *p_work) { struct tbs_video *videodev = container_of(p_work, struct tbs_video, videowork); videodev->img_ready =1; wake_up(&videodev->wq); return; } static int ProcessStreamThread(void *data){ struct vb2_queue *q =data; struct tbs_videofile_instance *stream= list_entry(q,struct tbs_videofile_instance,queue); struct tbs_video *videodev = q->drv_priv; void * buf_mem; struct tbsvideo_buffer *buf; unsigned int iNum; unsigned int iNext1; unsigned int iNext2; unsigned int rwidth; unsigned int rheight; unsigned int dst_width=stream->select_width; unsigned int dst_height=stream->select_height; int timeout; //printk( "%s() index:%x \n", __func__, videodev->index); while( !kthread_should_stop() ){ timeout = wait_event_timeout(videodev->wq, videodev->img_ready == 1, (HZ>>4)); if (timeout <= 0) { msleep(1); continue; } videodev->img_ready=0; spin_lock(&stream->slock); if(list_empty(&stream->list)){ spin_unlock(&stream->slock); msleep(1); continue; } buf = list_entry(stream->list.next, struct tbsvideo_buffer, queue); list_del(&buf->queue); buf_mem = vb2_plane_vaddr(&buf->vb.vb2_buf,0); if(!buf_mem){ buf->vb.vb2_buf.timestamp = ktime_get_ns(); buf->vb.sequence = stream->seqnr++; buf->vb.field = V4L2_FIELD_NONE; vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); spin_unlock(&stream->slock); printk(KERN_INFO "%s() vb2_plane_vaddr NULL\n",__func__); continue; } rwidth = videodev->dst_width; rheight = videodev->dst_height; iNum = (videodev->videostatus) & 0x3; if(videodev->Interlaced){ int i; iNext1 = (iNum + 1) % 3; iNext2 = (iNum + 2) % 3; for(i=0;iimgbuf0+(i+1)*rwidth*2, (u8*)videodev->dmabuf[iNext2].virtaddr+(i>>1)*rwidth*2,rwidth*2); memcpy(stream->imgbuf0+(i)*rwidth*2, (u8*)videodev->dmabuf[iNext1].virtaddr+(i>>1)*rwidth*2,rwidth*2); } }else{ memcpy(stream->imgbuf0,(u8*)videodev->dmabuf[iNum].virtaddr,rwidth*rheight*2); } /* Protect FPU/SIMD registers */ kernel_fpu_begin(); if(stream->select_pixelformat == V4L2_PIX_FMT_UYVY){ YUY2ToI422(stream->imgbuf0, rwidth << 1, stream->imgbuf1, rwidth, stream->imgbuf1 + rwidth * rheight, rwidth >> 1, stream->imgbuf1 + rwidth * rheight + (rwidth * rheight >> 1), rwidth >> 1, rwidth, rheight); I422Scale(stream->imgbuf1, rwidth, stream->imgbuf1 + rwidth * rheight, rwidth >> 1, stream->imgbuf1 + rwidth * rheight + (rwidth * rheight >> 1), rwidth >> 1, rwidth, rheight, stream->imgbuf0, dst_width, stream->imgbuf0 + dst_width * dst_height, dst_width >> 1, stream->imgbuf0 + dst_width * dst_height + (dst_width * dst_height >> 1), dst_width >> 1, dst_width, dst_height, 0 ); I422ToUYVY(stream->imgbuf0, dst_width, stream->imgbuf0 + dst_width * dst_height, dst_width >> 1, stream->imgbuf0 + dst_width * dst_height + (dst_width * dst_height >> 1), dst_width >> 1, buf_mem, dst_width << 1, dst_width, dst_height); } if(stream->select_pixelformat == V4L2_PIX_FMT_YUV420){ //YU12 YUY2ToNV12(stream->imgbuf0, rwidth << 1, stream->imgbuf1, rwidth, stream->imgbuf1 + rwidth * rheight, rwidth, rwidth, rheight); NV12Scale(stream->imgbuf1, rwidth, stream->imgbuf1 + rwidth * rheight, rwidth, rwidth, rheight, stream->imgbuf0, dst_width, stream->imgbuf0 + dst_width * dst_height, dst_width, dst_width, dst_height, 0 ); NV12ToI420(stream->imgbuf0, dst_width, stream->imgbuf0 + dst_width * dst_height, dst_width, buf_mem, dst_width, buf_mem + dst_width * dst_height, dst_width >> 1, buf_mem + dst_width * dst_height + (dst_width * dst_height >> 2),dst_width >> 1, dst_width, dst_height); } if(stream->select_pixelformat == V4L2_PIX_FMT_YVU420){ //YV12 YUY2ToNV12(stream->imgbuf0, rwidth << 1, stream->imgbuf1, rwidth, stream->imgbuf1 + rwidth * rheight, rwidth, rwidth, rheight); NV12Scale(stream->imgbuf1, rwidth, stream->imgbuf1 + rwidth * rheight, rwidth, rwidth, rheight, stream->imgbuf0, dst_width, stream->imgbuf0 + dst_width * dst_height, dst_width, dst_width, dst_height, 0 ); NV12ToI420(stream->imgbuf0, dst_width, stream->imgbuf0 + dst_width * dst_height, dst_width, buf_mem, dst_width, buf_mem + dst_width * dst_height + (dst_width * dst_height >> 2),dst_width >> 1, buf_mem + dst_width * dst_height, dst_width >> 1, dst_width, dst_height); } if(stream->select_pixelformat == V4L2_PIX_FMT_NV12){ //NV12 YUY2ToNV12(stream->imgbuf0, rwidth << 1, stream->imgbuf1, rwidth, stream->imgbuf1 + rwidth * rheight, rwidth, rwidth, rheight); NV12Scale(stream->imgbuf1, rwidth, stream->imgbuf1 + rwidth * rheight, rwidth, rwidth, rheight, buf_mem, dst_width, buf_mem + dst_width * dst_height, dst_width, dst_width, dst_height, 0 ); } kernel_fpu_end(); buf->vb.vb2_buf.timestamp = ktime_get_ns(); buf->vb.sequence = stream->seqnr++; buf->vb.field = V4L2_FIELD_NONE; vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_DONE); spin_unlock(&stream->slock); } spin_lock(&stream->slock); while (!list_empty(&stream->list)) { struct tbsvideo_buffer *buf = list_entry(stream->list.next, struct tbsvideo_buffer, queue); list_del(&buf->queue); vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); } spin_unlock(&stream->slock); return 0; } static int SignalDetectThread(void *data){ struct tbs_pcie_dev *dev = (struct tbs_pcie_dev *)data; int status; unsigned long channelwidth[4]; unsigned long channelheigh[4]; unsigned long channelinterlaced[4]; channelwidth[0] = channelwidth[1] =channelwidth[2] =channelwidth[3] =DEFAULT_WIDTH; channelheigh[0] = channelheigh[1] =channelheigh[2] =channelheigh[3] =DEFAULT_HEIGH; channelinterlaced[0] = channelinterlaced[1] =channelinterlaced[2] =channelinterlaced[3] =0; int i; //printk(KERN_INFO "%s() start\n",__func__); while (!kthread_should_stop()) { for(i=0;ivideo[i]); if (status || dev->video[i].runstatus == 0) { stop_video_dma_transfer(&dev->video[i]); channelwidth[i] = dev->video[i].width = DEFAULT_WIDTH; channelheigh[i] = dev->video[i].height = DEFAULT_HEIGH; channelinterlaced[i] = dev->video[i].Interlaced = 0; }else { if (channelwidth[i] != dev->video[i].width || channelheigh[i] != dev->video[i].height || channelinterlaced[i] != dev->video[i].Interlaced) { //printk(KERN_INFO "%s() video %d switch\n", __func__,i); stop_video_dma_transfer(&dev->video[i]); msleep(50); start_video_dma_transfer(&dev->video[i]); } channelwidth[i] = dev->video[i].width; channelheigh[i] = dev->video[i].height; channelinterlaced[i] = dev->video[i].Interlaced; } } // msleep(1000); wait_event_timeout(dev->wq, dev->signal_ready == 1, HZ); dev->signal_ready=0; } //printk(KERN_INFO "%s() end\n",__func__); stop_video_dma_transfer(&dev->video[0]); stop_video_dma_transfer(&dev->video[1]); stop_video_dma_transfer(&dev->video[2]); stop_video_dma_transfer(&dev->video[3]); return 0; } static bool tbs_enable_msi(struct pci_dev *pdev, struct tbs_pcie_dev *dev) { int err; if (!enable_msi) { dev_warn(&dev->pdev->dev, "MSI disabled by module parameter 'enable_msi'\n"); return false; } err = pci_enable_msi(pdev); if (err) { dev_err(&dev->pdev->dev, "Failed to enable MSI interrupt." " Falling back to a shared IRQ\n"); return false; } /* no error - so request an msi interrupt */ err = request_irq(pdev->irq, tbs_pcie_irq, 0, KBUILD_MODNAME, dev); if (err) { /* fall back to legacy interrupt */ dev_err(&dev->pdev->dev, "Failed to get an MSI interrupt." " Falling back to a shared IRQ\n"); pci_disable_msi(pdev); return false; } return true; } static int tbs_probe(struct pci_dev *pdev, const struct pci_device_id *pci_id) { struct tbs_pcie_dev *dev; int err = 0, ret = -ENODEV; dev = kzalloc(sizeof (struct tbs_pcie_dev), GFP_KERNEL); if (dev == NULL) { printk(KERN_ERR "pcie_tbs_probe ERROR: out of memory\n"); ret = -ENOMEM; goto fail0; } dev->pdev = pdev; err = pci_enable_device(pdev); if (err != 0) { ret = -ENODEV; printk(KERN_ERR "pcie_tbs_probe ERROR: PCI enable failed %d\n", err); goto fail1; } if(!pdev->is_busmaster) { pdev->is_busmaster=1; pci_set_master(pdev); } dev->mmio = ioremap(pci_resource_start(dev->pdev, 0), pci_resource_len(dev->pdev, 0)); if (!dev->mmio) { printk(KERN_ERR "pcie_tbs_probe ERROR: Mem 0 remap failed\n"); ret = -ENODEV; /* -ENOMEM better?! */ goto fail2; } TBS_PCIE_WRITE(TBS_INT_BASE, TBS_INT_ENABLE, 0x00000000); //interrupts if (tbs_enable_msi(pdev, dev)) { printk("KBUILD_MODNAME : %s --MSI!\n",KBUILD_MODNAME); dev->msi = true; } else { printk("KBUILD_MODNAME : %s --INTx\n\n",KBUILD_MODNAME); ret = request_irq(dev->pdev->irq,tbs_pcie_irq,IRQF_SHARED,KBUILD_MODNAME,(void *) dev); if (ret != 0) { printk(KERN_ERR "pcie_tbs_probe ERROR: IRQ registration failed %d\n", ret); ret = -ENODEV; goto fail3; } dev->msi = false; } if (pdev->msix_enabled){ printk("%s msix_enabled irq:%d \n",__func__,pdev->irq); }else if (pdev->msi_enabled){ printk("%s msi_enabled irq:%d \n",__func__,pdev->irq); }else{ printk("%s other irq:%d \n",__func__,pdev->irq); } pci_set_drvdata(pdev, dev); mutex_init(&(dev->devicemutex)); init_waitqueue_head(&dev->wq); if (tbs_i2c_init(dev) < 0){ printk(KERN_ERR "tbs_i2c_init failed \n"); goto fail4; } tbs_adapters_init(dev); if( tbs_video_register(dev) ){ printk(KERN_ERR "tbs_video_register failed \n"); goto fail4; } if(tbs_audio_register(dev)){ printk(KERN_ERR "tbs_audio_register failed \n"); goto fail4; } dev->signalthread=kthread_run(SignalDetectThread,dev,"tbs_signalthread"); printk("%s end\n",__func__); return 0; printk("%s failed:%d end\n",__func__,ret); fail4: free_irq(dev->pdev->irq, dev); fail3: if (dev->mmio) iounmap(dev->mmio); fail2: pci_disable_device(pdev); fail1: pci_set_drvdata(pdev, NULL); kfree(dev); fail0: return ret; } static int tbs_suspend(struct pci_dev *pdev, pm_message_t state) { struct tbs_pcie_dev *dev = (struct tbs_pcie_dev*) pci_get_drvdata(pdev); //printk(KERN_INFO "%s() \n",__func__); mutex_lock(&dev->devicemutex); return 0; } static int tbs_resume(struct pci_dev *pdev) { struct tbs_pcie_dev *dev = (struct tbs_pcie_dev*) pci_get_drvdata(pdev); //printk(KERN_INFO "%s() end\n",__func__); /* enable i2c interrupts */ TBS_PCIE_WRITE(TBS_INT_BASE, TBS_INT_ENABLE, 0x00000001); TBS_PCIE_WRITE(TBS_INT_BASE, TBS_I2C_MASK_0, 0x00000001); TBS_PCIE_WRITE(TBS_INT_BASE, TBS_I2C_MASK_1, 0x00000001); TBS_PCIE_WRITE(TBS_INT_BASE, TBS_I2C_MASK_2, 0x00000001); TBS_PCIE_WRITE(TBS_INT_BASE, TBS_I2C_MASK_3, 0x00000001); tbs_adapters_init(dev); mutex_unlock(&dev->devicemutex); return 0; } #define MAKE_ENTRY( __vend, __chip, __subven, __subdev, __configptr) { \ .vendor = (__vend), \ .device = (__chip), \ .subvendor = (__subven), \ .subdevice = (__subdev), \ .driver_data = (unsigned long) (__configptr) \ } static const struct pci_device_id tbs_pci_table[] = { MAKE_ENTRY(0x544d, 0x6178, 0x6314, 0x0003, NULL), { } }; MODULE_DEVICE_TABLE(pci, tbs_pci_table); static struct pci_driver tbs_pci_driver = { .name = KBUILD_MODNAME, .id_table = tbs_pci_table, .probe = tbs_probe, .remove = tbs_remove, .suspend = tbs_suspend, .resume = tbs_resume, }; static __init int pcie_tbs_init(void) { wq = create_singlethread_workqueue("tbs"); if (!wq) return -ENOMEM; return pci_register_driver(&tbs_pci_driver); } static __exit void pcie_tbs_exit(void) { if(wq) destroy_workqueue(wq); wq=NULL; pci_unregister_driver(&tbs_pci_driver); } module_init(pcie_tbs_init); module_exit(pcie_tbs_exit); MODULE_DESCRIPTION("TBS PCIE 2.0 HDMI capture driver"); MODULE_AUTHOR("tbs"); MODULE_LICENSE("GPL"); MODULE_VERSION("1.0");