Lines Matching +full:0 +full:- +full:576
1 // SPDX-License-Identifier: GPL-2.0
8 #include <linux/dma-mapping.h>
22 #include <media/drv-intf/sh_vou.h>
23 #include <media/v4l2-common.h>
24 #include <media/v4l2-device.h>
25 #include <media/v4l2-ioctl.h>
26 #include <media/v4l2-mediabus.h>
27 #include <media/videobuf2-v4l2.h>
28 #include <media/videobuf2-dma-contig.h>
31 #define VOUER 0
34 #define VOUVCR 0xc
35 #define VOUISR 0x10
36 #define VOUBCR 0x14
37 #define VOUDPR 0x18
38 #define VOUDSR 0x1c
39 #define VOUVPR 0x20
40 #define VOUIR 0x24
41 #define VOUSRR 0x28
42 #define VOUMSR 0x2c
43 #define VOUHIR 0x30
44 #define VOUDFR 0x34
45 #define VOUAD1R 0x38
46 #define VOUAD2R 0x3c
47 #define VOUAIR 0x40
48 #define VOUSWR 0x44
49 #define VOURCR 0x48
50 #define VOURPR 0x50
96 __raw_writel(value, vou_dev->base + reg); in sh_vou_reg_a_write()
102 __raw_writel(value, vou_dev->base + reg); in sh_vou_reg_ab_write()
103 __raw_writel(value, vou_dev->base + reg + 0x1000); in sh_vou_reg_ab_write()
109 __raw_writel(value, vou_dev->base + reg + 0x2000); in sh_vou_reg_m_write()
114 return __raw_readl(vou_dev->base + reg); in sh_vou_reg_a_read()
120 u32 old = __raw_readl(vou_dev->base + reg); in sh_vou_reg_a_set()
123 __raw_writel(value, vou_dev->base + reg); in sh_vou_reg_a_set()
129 sh_vou_reg_a_set(vou_dev, reg + 0x1000, value, mask); in sh_vou_reg_b_set()
154 .yf = 0,
155 .rgb = 0,
162 .rgb = 0,
192 addr1 = vb2_dma_contig_plane_dma_addr(&vbuf->vb2_buf, 0); in sh_vou_schedule_next()
193 switch (vou_dev->pix.pixelformat) { in sh_vou_schedule_next()
196 addr2 = addr1 + vou_dev->pix.width * vou_dev->pix.height; in sh_vou_schedule_next()
199 addr2 = 0; in sh_vou_schedule_next()
212 u32 dataswap = 0; in sh_vou_stream_config()
215 switch (vou_dev->pix.pixelformat) { in sh_vou_stream_config()
233 sh_vou_reg_ab_write(vou_dev, VOUAIR, vou_dev->pix.width * row_coeff); in sh_vou_stream_config()
242 struct v4l2_pix_format *pix = &vou_dev->pix; in sh_vou_queue_setup()
243 int bytes_per_line = vou_fmt[vou_dev->pix_idx].bpp * pix->width / 8; in sh_vou_queue_setup()
245 dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__); in sh_vou_queue_setup()
248 return sizes[0] < pix->height * bytes_per_line ? -EINVAL : 0; in sh_vou_queue_setup()
250 sizes[0] = pix->height * bytes_per_line; in sh_vou_queue_setup()
251 return 0; in sh_vou_queue_setup()
256 struct sh_vou_device *vou_dev = vb2_get_drv_priv(vb->vb2_queue); in sh_vou_buf_prepare()
257 struct v4l2_pix_format *pix = &vou_dev->pix; in sh_vou_buf_prepare()
258 unsigned bytes_per_line = vou_fmt[vou_dev->pix_idx].bpp * pix->width / 8; in sh_vou_buf_prepare()
259 unsigned size = pix->height * bytes_per_line; in sh_vou_buf_prepare()
261 dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__); in sh_vou_buf_prepare()
263 if (vb2_plane_size(vb, 0) < size) { in sh_vou_buf_prepare()
265 dev_warn(vou_dev->v4l2_dev.dev, "buffer too small (%lu < %u)\n", in sh_vou_buf_prepare()
266 vb2_plane_size(vb, 0), size); in sh_vou_buf_prepare()
267 return -EINVAL; in sh_vou_buf_prepare()
270 vb2_set_plane_payload(vb, 0, size); in sh_vou_buf_prepare()
271 return 0; in sh_vou_buf_prepare()
274 /* Locking: caller holds fop_lock mutex and vq->irqlock spinlock */
278 struct sh_vou_device *vou_dev = vb2_get_drv_priv(vb->vb2_queue); in sh_vou_buf_queue()
282 spin_lock_irqsave(&vou_dev->lock, flags); in sh_vou_buf_queue()
283 list_add_tail(&shbuf->list, &vou_dev->buf_list); in sh_vou_buf_queue()
284 spin_unlock_irqrestore(&vou_dev->lock, flags); in sh_vou_buf_queue()
293 vou_dev->sequence = 0; in sh_vou_start_streaming()
294 ret = v4l2_device_call_until_err(&vou_dev->v4l2_dev, 0, in sh_vou_start_streaming()
296 if (ret < 0 && ret != -ENOIOCTLCMD) { in sh_vou_start_streaming()
297 list_for_each_entry_safe(buf, node, &vou_dev->buf_list, list) { in sh_vou_start_streaming()
298 vb2_buffer_done(&buf->vb.vb2_buf, in sh_vou_start_streaming()
300 list_del(&buf->list); in sh_vou_start_streaming()
302 vou_dev->active = NULL; in sh_vou_start_streaming()
306 buf = list_entry(vou_dev->buf_list.next, struct sh_vou_buffer, list); in sh_vou_start_streaming()
308 vou_dev->active = buf; in sh_vou_start_streaming()
312 dev_dbg(vou_dev->v4l2_dev.dev, "%s: first buffer status 0x%x\n", in sh_vou_start_streaming()
314 sh_vou_schedule_next(vou_dev, &buf->vb); in sh_vou_start_streaming()
316 buf = list_entry(buf->list.next, struct sh_vou_buffer, list); in sh_vou_start_streaming()
318 /* Second buffer - initialise register side B */ in sh_vou_start_streaming()
319 sh_vou_reg_a_write(vou_dev, VOURPR, 0); in sh_vou_start_streaming()
320 sh_vou_schedule_next(vou_dev, &buf->vb); in sh_vou_start_streaming()
326 /* Enable End-of-Frame (VSYNC) interrupts */ in sh_vou_start_streaming()
327 sh_vou_reg_a_write(vou_dev, VOUIR, 0x10004); in sh_vou_start_streaming()
329 /* Two buffers on the queue - activate the hardware */ in sh_vou_start_streaming()
330 vou_dev->status = SH_VOU_RUNNING; in sh_vou_start_streaming()
331 sh_vou_reg_a_write(vou_dev, VOUER, 0x107); in sh_vou_start_streaming()
332 return 0; in sh_vou_start_streaming()
341 v4l2_device_call_until_err(&vou_dev->v4l2_dev, 0, in sh_vou_stop_streaming()
342 video, s_stream, 0); in sh_vou_stop_streaming()
344 sh_vou_reg_a_set(vou_dev, VOUER, 0, 1); in sh_vou_stop_streaming()
346 sh_vou_reg_a_set(vou_dev, VOUIR, 0, 0x30000); in sh_vou_stop_streaming()
348 spin_lock_irqsave(&vou_dev->lock, flags); in sh_vou_stop_streaming()
349 list_for_each_entry_safe(buf, node, &vou_dev->buf_list, list) { in sh_vou_stop_streaming()
350 vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); in sh_vou_stop_streaming()
351 list_del(&buf->list); in sh_vou_stop_streaming()
353 vou_dev->active = NULL; in sh_vou_stop_streaming()
354 spin_unlock_irqrestore(&vou_dev->lock, flags); in sh_vou_stop_streaming()
371 dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__); in sh_vou_querycap()
373 strscpy(cap->card, "SuperH VOU", sizeof(cap->card)); in sh_vou_querycap()
374 strscpy(cap->driver, "sh-vou", sizeof(cap->driver)); in sh_vou_querycap()
375 strscpy(cap->bus_info, "platform:sh-vou", sizeof(cap->bus_info)); in sh_vou_querycap()
376 return 0; in sh_vou_querycap()
385 if (fmt->index >= ARRAY_SIZE(vou_fmt)) in sh_vou_enum_fmt_vid_out()
386 return -EINVAL; in sh_vou_enum_fmt_vid_out()
388 dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__); in sh_vou_enum_fmt_vid_out()
390 fmt->pixelformat = vou_fmt[fmt->index].pfmt; in sh_vou_enum_fmt_vid_out()
392 return 0; in sh_vou_enum_fmt_vid_out()
400 dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__); in sh_vou_g_fmt_vid_out()
402 fmt->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; in sh_vou_g_fmt_vid_out()
403 fmt->fmt.pix = vou_dev->pix; in sh_vou_g_fmt_vid_out()
405 return 0; in sh_vou_g_fmt_vid_out()
410 static const unsigned char vou_scale_h_fld[] = {0, 2, 1, 3};
413 static const unsigned char vou_scale_v_fld[] = {0, 1};
421 struct v4l2_rect *rect = &vou_dev->rect; in sh_vou_configure_geometry()
422 struct v4l2_pix_format *pix = &vou_dev->pix; in sh_vou_configure_geometry()
423 u32 vouvcr = 0, dsr_h, dsr_v; in sh_vou_configure_geometry()
425 if (vou_dev->std & V4L2_STD_525_60) { in sh_vou_configure_geometry()
433 frame_in_height = pix->height / 2; in sh_vou_configure_geometry()
434 frame_out_height = rect->height / 2; in sh_vou_configure_geometry()
435 frame_out_top = rect->top / 2; in sh_vou_configure_geometry()
444 * exactly 858 - 138 = 864 - 144 = 720! We call the out-of-display area, in sh_vou_configure_geometry()
446 * pixels" and out-of-image area (DPR) "background pixels." We fix VPR in sh_vou_configure_geometry()
451 * could also set it to max - 240 = 22 / 72. Thus VPR depends only on in sh_vou_configure_geometry()
456 black_left = width_max - VOU_MAX_IMAGE_WIDTH; in sh_vou_configure_geometry()
459 dsr_h = rect->width + rect->left; in sh_vou_configure_geometry()
462 dev_dbg(vou_dev->v4l2_dev.dev, in sh_vou_configure_geometry()
464 pix->width, frame_in_height, black_left, black_top, in sh_vou_configure_geometry()
465 rect->left, frame_out_top, dsr_h, dsr_v); in sh_vou_configure_geometry()
467 /* VOUISR height - half of a frame height in frame mode */ in sh_vou_configure_geometry()
468 sh_vou_reg_ab_write(vou_dev, VOUISR, (pix->width << 16) | frame_in_height); in sh_vou_configure_geometry()
470 sh_vou_reg_ab_write(vou_dev, VOUDPR, (rect->left << 16) | frame_out_top); in sh_vou_configure_geometry()
479 vouvcr |= (1 << 15) | (vou_scale_h_fld[w_idx - 1] << 4); in sh_vou_configure_geometry()
481 vouvcr |= (1 << 14) | vou_scale_v_fld[h_idx - 1]; in sh_vou_configure_geometry()
483 dev_dbg(vou_dev->v4l2_dev.dev, "0x%08x: scaling 0x%x\n", in sh_vou_configure_geometry()
484 fmt->pfmt, vouvcr); in sh_vou_configure_geometry()
489 fmt->pkf | (fmt->yf << 8) | (fmt->rgb << 16)); in sh_vou_configure_geometry()
507 unsigned int best_err = UINT_MAX, best = 0, img_height_max; in vou_adjust_input()
508 int i, idx = 0; in vou_adjust_input()
513 img_height_max = 576; in vou_adjust_input()
516 v4l_bound_align_image(&geo->in_width, in vou_adjust_input()
518 &geo->in_height, in vou_adjust_input()
519 VOU_MIN_IMAGE_HEIGHT, img_height_max, 1, 0); in vou_adjust_input()
522 for (i = ARRAY_SIZE(vou_scale_h_num) - 1; i >= 0; i--) { in vou_adjust_input()
524 unsigned int found = geo->output.width * vou_scale_h_den[i] / in vou_adjust_input()
531 err = abs(found - geo->in_width); in vou_adjust_input()
541 geo->in_width = best; in vou_adjust_input()
542 geo->scale_idx_h = idx; in vou_adjust_input()
547 for (i = ARRAY_SIZE(vou_scale_v_num) - 1; i >= 0; i--) { in vou_adjust_input()
549 unsigned int found = geo->output.height * vou_scale_v_den[i] / in vou_adjust_input()
556 err = abs(found - geo->in_height); in vou_adjust_input()
566 geo->in_height = best; in vou_adjust_input()
567 geo->scale_idx_v = idx; in vou_adjust_input()
576 unsigned int best_err = UINT_MAX, best = geo->in_width, in vou_adjust_output()
578 int i, idx_h = 0, idx_v = 0; in vou_adjust_output()
587 img_height_max = 576; in vou_adjust_output()
591 for (i = 0; i < ARRAY_SIZE(vou_scale_h_num); i++) { in vou_adjust_output()
593 unsigned int found = geo->in_width * vou_scale_h_num[i] / in vou_adjust_output()
600 err = abs(found - geo->output.width); in vou_adjust_output()
610 geo->output.width = best; in vou_adjust_output()
611 geo->scale_idx_h = idx_h; in vou_adjust_output()
612 if (geo->output.left + best > width_max) in vou_adjust_output()
613 geo->output.left = width_max - best; in vou_adjust_output()
615 pr_debug("%s(): W %u * %u/%u = %u\n", __func__, geo->in_width, in vou_adjust_output()
621 for (i = 0; i < ARRAY_SIZE(vou_scale_v_num); i++) { in vou_adjust_output()
623 unsigned int found = geo->in_height * vou_scale_v_num[i] / in vou_adjust_output()
630 err = abs(found - geo->output.height); in vou_adjust_output()
640 geo->output.height = best; in vou_adjust_output()
641 geo->scale_idx_v = idx_v; in vou_adjust_output()
642 if (geo->output.top + best > height_max) in vou_adjust_output()
643 geo->output.top = height_max - best; in vou_adjust_output()
645 pr_debug("%s(): H %u * %u/%u = %u\n", __func__, geo->in_height, in vou_adjust_output()
653 struct v4l2_pix_format *pix = &fmt->fmt.pix; in sh_vou_try_fmt_vid_out()
657 dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__); in sh_vou_try_fmt_vid_out()
659 pix->field = V4L2_FIELD_INTERLACED; in sh_vou_try_fmt_vid_out()
660 pix->colorspace = V4L2_COLORSPACE_SMPTE170M; in sh_vou_try_fmt_vid_out()
661 pix->ycbcr_enc = pix->quantization = 0; in sh_vou_try_fmt_vid_out()
663 for (pix_idx = 0; pix_idx < ARRAY_SIZE(vou_fmt); pix_idx++) in sh_vou_try_fmt_vid_out()
664 if (vou_fmt[pix_idx].pfmt == pix->pixelformat) in sh_vou_try_fmt_vid_out()
668 return -EINVAL; in sh_vou_try_fmt_vid_out()
670 if (vou_dev->std & V4L2_STD_525_60) in sh_vou_try_fmt_vid_out()
673 img_height_max = 576; in sh_vou_try_fmt_vid_out()
675 v4l_bound_align_image(&pix->width, in sh_vou_try_fmt_vid_out()
677 &pix->height, in sh_vou_try_fmt_vid_out()
678 VOU_MIN_IMAGE_HEIGHT, img_height_max, 1, 0); in sh_vou_try_fmt_vid_out()
679 pix->bytesperline = pix->width * vou_fmt[pix_idx].bpl; in sh_vou_try_fmt_vid_out()
680 pix->sizeimage = pix->height * ((pix->width * vou_fmt[pix_idx].bpp) >> 3); in sh_vou_try_fmt_vid_out()
682 return 0; in sh_vou_try_fmt_vid_out()
701 if (vb2_is_busy(&vou_dev->queue)) in sh_vou_set_fmt_vid_out()
702 return -EBUSY; in sh_vou_set_fmt_vid_out()
704 for (pix_idx = 0; pix_idx < ARRAY_SIZE(vou_fmt); pix_idx++) in sh_vou_set_fmt_vid_out()
705 if (vou_fmt[pix_idx].pfmt == pix->pixelformat) in sh_vou_set_fmt_vid_out()
708 geo.in_width = pix->width; in sh_vou_set_fmt_vid_out()
709 geo.in_height = pix->height; in sh_vou_set_fmt_vid_out()
710 geo.output = vou_dev->rect; in sh_vou_set_fmt_vid_out()
712 vou_adjust_output(&geo, vou_dev->std); in sh_vou_set_fmt_vid_out()
714 mbfmt->width = geo.output.width; in sh_vou_set_fmt_vid_out()
715 mbfmt->height = geo.output.height; in sh_vou_set_fmt_vid_out()
716 ret = v4l2_device_call_until_err(&vou_dev->v4l2_dev, 0, pad, in sh_vou_set_fmt_vid_out()
718 /* Must be implemented, so, don't check for -ENOIOCTLCMD */ in sh_vou_set_fmt_vid_out()
719 if (ret < 0) in sh_vou_set_fmt_vid_out()
722 dev_dbg(vou_dev->v4l2_dev.dev, "%s(): %ux%u -> %ux%u\n", __func__, in sh_vou_set_fmt_vid_out()
723 geo.output.width, geo.output.height, mbfmt->width, mbfmt->height); in sh_vou_set_fmt_vid_out()
725 if (vou_dev->std & V4L2_STD_525_60) in sh_vou_set_fmt_vid_out()
728 img_height_max = 576; in sh_vou_set_fmt_vid_out()
731 if ((unsigned)mbfmt->width > VOU_MAX_IMAGE_WIDTH || in sh_vou_set_fmt_vid_out()
732 (unsigned)mbfmt->height > img_height_max || in sh_vou_set_fmt_vid_out()
733 mbfmt->code != MEDIA_BUS_FMT_YUYV8_2X8) in sh_vou_set_fmt_vid_out()
734 return -EIO; in sh_vou_set_fmt_vid_out()
736 if (mbfmt->width != geo.output.width || in sh_vou_set_fmt_vid_out()
737 mbfmt->height != geo.output.height) { in sh_vou_set_fmt_vid_out()
738 geo.output.width = mbfmt->width; in sh_vou_set_fmt_vid_out()
739 geo.output.height = mbfmt->height; in sh_vou_set_fmt_vid_out()
741 vou_adjust_input(&geo, vou_dev->std); in sh_vou_set_fmt_vid_out()
745 vou_dev->rect = geo.output; in sh_vou_set_fmt_vid_out()
746 pix->width = geo.in_width; in sh_vou_set_fmt_vid_out()
747 pix->height = geo.in_height; in sh_vou_set_fmt_vid_out()
749 dev_dbg(vou_dev->v4l2_dev.dev, "%s(): %ux%u\n", __func__, in sh_vou_set_fmt_vid_out()
750 pix->width, pix->height); in sh_vou_set_fmt_vid_out()
752 vou_dev->pix_idx = pix_idx; in sh_vou_set_fmt_vid_out()
754 vou_dev->pix = *pix; in sh_vou_set_fmt_vid_out()
759 return 0; in sh_vou_set_fmt_vid_out()
770 return sh_vou_set_fmt_vid_out(vou_dev, &fmt->fmt.pix); in sh_vou_s_fmt_vid_out()
778 if (a->index) in sh_vou_enum_output()
779 return -EINVAL; in sh_vou_enum_output()
780 strscpy(a->name, "Video Out", sizeof(a->name)); in sh_vou_enum_output()
781 a->type = V4L2_OUTPUT_TYPE_ANALOG; in sh_vou_enum_output()
782 a->std = vou_dev->vdev.tvnorms; in sh_vou_enum_output()
783 return 0; in sh_vou_enum_output()
788 *i = 0; in sh_vou_g_output()
789 return 0; in sh_vou_g_output()
794 return i ? -EINVAL : 0; in sh_vou_s_output()
801 pr_warn("%s(): Invalid bus-format code %d, using default 8-bit\n", in sh_vou_ntsc_mode()
807 return 0; in sh_vou_ntsc_mode()
818 dev_dbg(vou_dev->v4l2_dev.dev, "%s(): 0x%llx\n", __func__, std_id); in sh_vou_s_std()
820 if (std_id == vou_dev->std) in sh_vou_s_std()
821 return 0; in sh_vou_s_std()
823 if (vb2_is_busy(&vou_dev->queue)) in sh_vou_s_std()
824 return -EBUSY; in sh_vou_s_std()
826 ret = v4l2_device_call_until_err(&vou_dev->v4l2_dev, 0, video, in sh_vou_s_std()
829 if (ret < 0 && ret != -ENOIOCTLCMD) in sh_vou_s_std()
832 vou_dev->rect.top = vou_dev->rect.left = 0; in sh_vou_s_std()
833 vou_dev->rect.width = VOU_MAX_IMAGE_WIDTH; in sh_vou_s_std()
836 sh_vou_ntsc_mode(vou_dev->pdata->bus_fmt) << 29, 7 << 29); in sh_vou_s_std()
837 vou_dev->rect.height = 480; in sh_vou_s_std()
840 vou_dev->rect.height = 576; in sh_vou_s_std()
843 vou_dev->pix.width = vou_dev->rect.width; in sh_vou_s_std()
844 vou_dev->pix.height = vou_dev->rect.height; in sh_vou_s_std()
845 vou_dev->pix.bytesperline = in sh_vou_s_std()
846 vou_dev->pix.width * vou_fmt[vou_dev->pix_idx].bpl; in sh_vou_s_std()
847 vou_dev->pix.sizeimage = vou_dev->pix.height * in sh_vou_s_std()
848 ((vou_dev->pix.width * vou_fmt[vou_dev->pix_idx].bpp) >> 3); in sh_vou_s_std()
849 vou_dev->std = std_id; in sh_vou_s_std()
850 sh_vou_set_fmt_vid_out(vou_dev, &vou_dev->pix); in sh_vou_s_std()
852 return 0; in sh_vou_s_std()
859 dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__); in sh_vou_g_std()
861 *std = vou_dev->std; in sh_vou_g_std()
863 return 0; in sh_vou_g_std()
870 pr_info("VOUER: 0x%08x\n", sh_vou_reg_a_read(vou_dev, VOUER)); in sh_vou_log_status()
871 pr_info("VOUCR: 0x%08x\n", sh_vou_reg_a_read(vou_dev, VOUCR)); in sh_vou_log_status()
872 pr_info("VOUSTR: 0x%08x\n", sh_vou_reg_a_read(vou_dev, VOUSTR)); in sh_vou_log_status()
873 pr_info("VOUVCR: 0x%08x\n", sh_vou_reg_a_read(vou_dev, VOUVCR)); in sh_vou_log_status()
874 pr_info("VOUISR: 0x%08x\n", sh_vou_reg_a_read(vou_dev, VOUISR)); in sh_vou_log_status()
875 pr_info("VOUBCR: 0x%08x\n", sh_vou_reg_a_read(vou_dev, VOUBCR)); in sh_vou_log_status()
876 pr_info("VOUDPR: 0x%08x\n", sh_vou_reg_a_read(vou_dev, VOUDPR)); in sh_vou_log_status()
877 pr_info("VOUDSR: 0x%08x\n", sh_vou_reg_a_read(vou_dev, VOUDSR)); in sh_vou_log_status()
878 pr_info("VOUVPR: 0x%08x\n", sh_vou_reg_a_read(vou_dev, VOUVPR)); in sh_vou_log_status()
879 pr_info("VOUIR: 0x%08x\n", sh_vou_reg_a_read(vou_dev, VOUIR)); in sh_vou_log_status()
880 pr_info("VOUSRR: 0x%08x\n", sh_vou_reg_a_read(vou_dev, VOUSRR)); in sh_vou_log_status()
881 pr_info("VOUMSR: 0x%08x\n", sh_vou_reg_a_read(vou_dev, VOUMSR)); in sh_vou_log_status()
882 pr_info("VOUHIR: 0x%08x\n", sh_vou_reg_a_read(vou_dev, VOUHIR)); in sh_vou_log_status()
883 pr_info("VOUDFR: 0x%08x\n", sh_vou_reg_a_read(vou_dev, VOUDFR)); in sh_vou_log_status()
884 pr_info("VOUAD1R: 0x%08x\n", sh_vou_reg_a_read(vou_dev, VOUAD1R)); in sh_vou_log_status()
885 pr_info("VOUAD2R: 0x%08x\n", sh_vou_reg_a_read(vou_dev, VOUAD2R)); in sh_vou_log_status()
886 pr_info("VOUAIR: 0x%08x\n", sh_vou_reg_a_read(vou_dev, VOUAIR)); in sh_vou_log_status()
887 pr_info("VOUSWR: 0x%08x\n", sh_vou_reg_a_read(vou_dev, VOUSWR)); in sh_vou_log_status()
888 pr_info("VOURCR: 0x%08x\n", sh_vou_reg_a_read(vou_dev, VOURCR)); in sh_vou_log_status()
889 pr_info("VOURPR: 0x%08x\n", sh_vou_reg_a_read(vou_dev, VOURPR)); in sh_vou_log_status()
890 return 0; in sh_vou_log_status()
898 if (sel->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) in sh_vou_g_selection()
899 return -EINVAL; in sh_vou_g_selection()
900 switch (sel->target) { in sh_vou_g_selection()
902 sel->r = vou_dev->rect; in sh_vou_g_selection()
906 sel->r.left = 0; in sh_vou_g_selection()
907 sel->r.top = 0; in sh_vou_g_selection()
908 sel->r.width = VOU_MAX_IMAGE_WIDTH; in sh_vou_g_selection()
909 if (vou_dev->std & V4L2_STD_525_60) in sh_vou_g_selection()
910 sel->r.height = 480; in sh_vou_g_selection()
912 sel->r.height = 576; in sh_vou_g_selection()
915 return -EINVAL; in sh_vou_g_selection()
917 return 0; in sh_vou_g_selection()
924 struct v4l2_rect *rect = &sel->r; in sh_vou_s_selection()
930 struct v4l2_pix_format *pix = &vou_dev->pix; in sh_vou_s_selection()
942 if (sel->type != V4L2_BUF_TYPE_VIDEO_OUTPUT || in sh_vou_s_selection()
943 sel->target != V4L2_SEL_TGT_COMPOSE) in sh_vou_s_selection()
944 return -EINVAL; in sh_vou_s_selection()
946 if (vb2_is_busy(&vou_dev->queue)) in sh_vou_s_selection()
947 return -EBUSY; in sh_vou_s_selection()
949 if (vou_dev->std & V4L2_STD_525_60) in sh_vou_s_selection()
952 img_height_max = 576; in sh_vou_s_selection()
954 v4l_bound_align_image(&rect->width, in sh_vou_s_selection()
956 &rect->height, in sh_vou_s_selection()
957 VOU_MIN_IMAGE_HEIGHT, img_height_max, 1, 0); in sh_vou_s_selection()
959 if (rect->width + rect->left > VOU_MAX_IMAGE_WIDTH) in sh_vou_s_selection()
960 rect->left = VOU_MAX_IMAGE_WIDTH - rect->width; in sh_vou_s_selection()
962 if (rect->height + rect->top > img_height_max) in sh_vou_s_selection()
963 rect->top = img_height_max - rect->height; in sh_vou_s_selection()
966 geo.in_width = pix->width; in sh_vou_s_selection()
967 geo.in_height = pix->height; in sh_vou_s_selection()
969 /* Configure the encoder one-to-one, position at 0, ignore errors */ in sh_vou_s_selection()
976 v4l2_device_call_until_err(&vou_dev->v4l2_dev, 0, pad, in sh_vou_s_selection()
980 ret = v4l2_device_call_until_err(&vou_dev->v4l2_dev, 0, pad, in sh_vou_s_selection()
982 /* Must be implemented, so, don't check for -ENOIOCTLCMD */ in sh_vou_s_selection()
983 if (ret < 0) in sh_vou_s_selection()
990 return -EIO; in sh_vou_s_selection()
996 * No down-scaling. According to the API, current call has precedence: in sh_vou_s_selection()
997 * https://linuxtv.org/downloads/v4l-dvb-apis/uapi/v4l/crop.html#cropping-structures in sh_vou_s_selection()
999 vou_adjust_input(&geo, vou_dev->std); in sh_vou_s_selection()
1002 vou_dev->rect = geo.output; in sh_vou_s_selection()
1003 pix->width = geo.in_width; in sh_vou_s_selection()
1004 pix->height = geo.in_height; in sh_vou_s_selection()
1006 sh_vou_configure_geometry(vou_dev, vou_dev->pix_idx, in sh_vou_s_selection()
1009 return 0; in sh_vou_s_selection()
1021 if (!(irq_status & 0x300)) { in sh_vou_isr()
1023 dev_warn(vou_dev->v4l2_dev.dev, "IRQ status 0x%x!\n", in sh_vou_isr()
1028 spin_lock(&vou_dev->lock); in sh_vou_isr()
1029 if (!vou_dev->active || list_empty(&vou_dev->buf_list)) { in sh_vou_isr()
1031 dev_warn(vou_dev->v4l2_dev.dev, in sh_vou_isr()
1034 sh_vou_reg_a_set(vou_dev, VOUIR, 0, 0x300); in sh_vou_isr()
1035 spin_unlock(&vou_dev->lock); in sh_vou_isr()
1039 masked = ~(0x300 & irq_status) & irq_status & 0x30304; in sh_vou_isr()
1040 dev_dbg(vou_dev->v4l2_dev.dev, in sh_vou_isr()
1041 "IRQ status 0x%x -> 0x%x, VOU status 0x%x, cnt %d\n", in sh_vou_isr()
1045 /* side = vou_status & 0x10000; */ in sh_vou_isr()
1050 vb = vou_dev->active; in sh_vou_isr()
1051 if (list_is_singular(&vb->list)) { in sh_vou_isr()
1053 sh_vou_schedule_next(vou_dev, &vb->vb); in sh_vou_isr()
1054 spin_unlock(&vou_dev->lock); in sh_vou_isr()
1058 list_del(&vb->list); in sh_vou_isr()
1060 vb->vb.vb2_buf.timestamp = ktime_get_ns(); in sh_vou_isr()
1061 vb->vb.sequence = vou_dev->sequence++; in sh_vou_isr()
1062 vb->vb.field = V4L2_FIELD_INTERLACED; in sh_vou_isr()
1063 vb2_buffer_done(&vb->vb.vb2_buf, VB2_BUF_STATE_DONE); in sh_vou_isr()
1065 vou_dev->active = list_entry(vou_dev->buf_list.next, in sh_vou_isr()
1068 if (list_is_singular(&vou_dev->buf_list)) { in sh_vou_isr()
1070 sh_vou_schedule_next(vou_dev, &vou_dev->active->vb); in sh_vou_isr()
1072 struct sh_vou_buffer *new = list_entry(vou_dev->active->list.next, in sh_vou_isr()
1074 sh_vou_schedule_next(vou_dev, &new->vb); in sh_vou_isr()
1077 spin_unlock(&vou_dev->lock); in sh_vou_isr()
1084 struct sh_vou_pdata *pdata = vou_dev->pdata; in sh_vou_hw_init()
1085 u32 voucr = sh_vou_ntsc_mode(pdata->bus_fmt) << 29; in sh_vou_hw_init()
1089 sh_vou_reg_a_write(vou_dev, VOUIR, 0); in sh_vou_hw_init()
1091 /* Reset VOU interfaces - registers unaffected */ in sh_vou_hw_init()
1092 sh_vou_reg_a_write(vou_dev, VOUSRR, 0x101); in sh_vou_hw_init()
1093 while (--i && (sh_vou_reg_a_read(vou_dev, VOUSRR) & 0x101)) in sh_vou_hw_init()
1097 return -ETIMEDOUT; in sh_vou_hw_init()
1099 dev_dbg(vou_dev->v4l2_dev.dev, "Reset took %dus\n", 100 - i); in sh_vou_hw_init()
1101 if (pdata->flags & SH_VOU_PCLK_FALLING) in sh_vou_hw_init()
1103 if (pdata->flags & SH_VOU_HSYNC_LOW) in sh_vou_hw_init()
1105 if (pdata->flags & SH_VOU_VSYNC_LOW) in sh_vou_hw_init()
1107 sh_vou_reg_ab_set(vou_dev, VOUCR, voucr, 0xfc000000); in sh_vou_hw_init()
1111 /* Default - fixed HSYNC length, can be made configurable is required */ in sh_vou_hw_init()
1112 sh_vou_reg_ab_write(vou_dev, VOUMSR, 0x800000); in sh_vou_hw_init()
1114 sh_vou_set_fmt_vid_out(vou_dev, &vou_dev->pix); in sh_vou_hw_init()
1116 return 0; in sh_vou_hw_init()
1125 if (mutex_lock_interruptible(&vou_dev->fop_lock)) in sh_vou_open()
1126 return -ERESTARTSYS; in sh_vou_open()
1132 vou_dev->status == SH_VOU_INITIALISING) { in sh_vou_open()
1134 err = pm_runtime_resume_and_get(vou_dev->v4l2_dev.dev); in sh_vou_open()
1135 if (err < 0) { in sh_vou_open()
1140 if (err < 0) { in sh_vou_open()
1141 pm_runtime_put(vou_dev->v4l2_dev.dev); in sh_vou_open()
1144 vou_dev->status = SH_VOU_IDLE; in sh_vou_open()
1148 mutex_unlock(&vou_dev->fop_lock); in sh_vou_open()
1157 mutex_lock(&vou_dev->fop_lock); in sh_vou_release()
1162 vou_dev->status = SH_VOU_INITIALISING; in sh_vou_release()
1163 sh_vou_reg_a_set(vou_dev, VOUER, 0, 0x101); in sh_vou_release()
1164 pm_runtime_put(vou_dev->v4l2_dev.dev); in sh_vou_release()
1166 mutex_unlock(&vou_dev->fop_lock); in sh_vou_release()
1167 return 0; in sh_vou_release()
1210 .tvnorms = V4L2_STD_525_60, /* PAL only supported in 8-bit non-bt656 mode */
1218 struct sh_vou_pdata *vou_pdata = pdev->dev.platform_data; in sh_vou_probe()
1229 dev_err(&pdev->dev, "Insufficient VOU platform information.\n"); in sh_vou_probe()
1230 return -ENODEV; in sh_vou_probe()
1233 irq = platform_get_irq(pdev, 0); in sh_vou_probe()
1234 if (irq < 0) in sh_vou_probe()
1237 vou_dev = devm_kzalloc(&pdev->dev, sizeof(*vou_dev), GFP_KERNEL); in sh_vou_probe()
1239 return -ENOMEM; in sh_vou_probe()
1241 INIT_LIST_HEAD(&vou_dev->buf_list); in sh_vou_probe()
1242 spin_lock_init(&vou_dev->lock); in sh_vou_probe()
1243 mutex_init(&vou_dev->fop_lock); in sh_vou_probe()
1244 vou_dev->pdata = vou_pdata; in sh_vou_probe()
1245 vou_dev->status = SH_VOU_INITIALISING; in sh_vou_probe()
1246 vou_dev->pix_idx = 1; in sh_vou_probe()
1248 rect = &vou_dev->rect; in sh_vou_probe()
1249 pix = &vou_dev->pix; in sh_vou_probe()
1252 vou_dev->std = V4L2_STD_NTSC_M; in sh_vou_probe()
1253 rect->left = 0; in sh_vou_probe()
1254 rect->top = 0; in sh_vou_probe()
1255 rect->width = VOU_MAX_IMAGE_WIDTH; in sh_vou_probe()
1256 rect->height = 480; in sh_vou_probe()
1257 pix->width = VOU_MAX_IMAGE_WIDTH; in sh_vou_probe()
1258 pix->height = 480; in sh_vou_probe()
1259 pix->pixelformat = V4L2_PIX_FMT_NV16; in sh_vou_probe()
1260 pix->field = V4L2_FIELD_INTERLACED; in sh_vou_probe()
1261 pix->bytesperline = VOU_MAX_IMAGE_WIDTH; in sh_vou_probe()
1262 pix->sizeimage = VOU_MAX_IMAGE_WIDTH * 2 * 480; in sh_vou_probe()
1263 pix->colorspace = V4L2_COLORSPACE_SMPTE170M; in sh_vou_probe()
1265 vou_dev->base = devm_platform_ioremap_resource(pdev, 0); in sh_vou_probe()
1266 if (IS_ERR(vou_dev->base)) in sh_vou_probe()
1267 return PTR_ERR(vou_dev->base); in sh_vou_probe()
1269 ret = devm_request_irq(&pdev->dev, irq, sh_vou_isr, 0, "vou", vou_dev); in sh_vou_probe()
1270 if (ret < 0) in sh_vou_probe()
1273 ret = v4l2_device_register(&pdev->dev, &vou_dev->v4l2_dev); in sh_vou_probe()
1274 if (ret < 0) { in sh_vou_probe()
1275 dev_err(&pdev->dev, "Error registering v4l2 device\n"); in sh_vou_probe()
1279 vdev = &vou_dev->vdev; in sh_vou_probe()
1281 if (vou_pdata->bus_fmt == SH_VOU_BUS_8BIT) in sh_vou_probe()
1282 vdev->tvnorms |= V4L2_STD_PAL; in sh_vou_probe()
1283 vdev->v4l2_dev = &vou_dev->v4l2_dev; in sh_vou_probe()
1284 vdev->release = video_device_release_empty; in sh_vou_probe()
1285 vdev->lock = &vou_dev->fop_lock; in sh_vou_probe()
1290 q = &vou_dev->queue; in sh_vou_probe()
1291 q->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; in sh_vou_probe()
1292 q->io_modes = VB2_MMAP | VB2_DMABUF | VB2_WRITE; in sh_vou_probe()
1293 q->drv_priv = vou_dev; in sh_vou_probe()
1294 q->buf_struct_size = sizeof(struct sh_vou_buffer); in sh_vou_probe()
1295 q->ops = &sh_vou_qops; in sh_vou_probe()
1296 q->mem_ops = &vb2_dma_contig_memops; in sh_vou_probe()
1297 q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; in sh_vou_probe()
1298 q->min_queued_buffers = 2; in sh_vou_probe()
1299 q->lock = &vou_dev->fop_lock; in sh_vou_probe()
1300 q->dev = &pdev->dev; in sh_vou_probe()
1305 vdev->queue = q; in sh_vou_probe()
1306 INIT_LIST_HEAD(&vou_dev->buf_list); in sh_vou_probe()
1308 pm_runtime_enable(&pdev->dev); in sh_vou_probe()
1309 pm_runtime_resume(&pdev->dev); in sh_vou_probe()
1311 i2c_adap = i2c_get_adapter(vou_pdata->i2c_adap); in sh_vou_probe()
1313 ret = -ENODEV; in sh_vou_probe()
1318 if (ret < 0) in sh_vou_probe()
1321 subdev = v4l2_i2c_new_subdev_board(&vou_dev->v4l2_dev, i2c_adap, in sh_vou_probe()
1322 vou_pdata->board_info, NULL); in sh_vou_probe()
1324 ret = -ENOMEM; in sh_vou_probe()
1328 ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1); in sh_vou_probe()
1329 if (ret < 0) in sh_vou_probe()
1332 return 0; in sh_vou_probe()
1339 pm_runtime_disable(&pdev->dev); in sh_vou_probe()
1340 v4l2_device_unregister(&vou_dev->v4l2_dev); in sh_vou_probe()
1349 struct v4l2_subdev *sd = list_entry(v4l2_dev->subdevs.next, in sh_vou_remove()
1353 pm_runtime_disable(&pdev->dev); in sh_vou_remove()
1354 video_unregister_device(&vou_dev->vdev); in sh_vou_remove()
1355 i2c_put_adapter(client->adapter); in sh_vou_remove()
1356 v4l2_device_unregister(&vou_dev->v4l2_dev); in sh_vou_remove()
1362 .name = "sh-vou",
1371 MODULE_VERSION("0.1.0");
1372 MODULE_ALIAS("platform:sh-vou");