2 * vsp1_rpf.c -- R-Car VSP1 Read Pixel Formatter
4 * Copyright (C) 2013 Renesas Corporation
6 * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
14 #include <linux/device.h>
16 #include <media/v4l2-subdev.h>
19 #include "vsp1_rwpf.h"
20 #include "vsp1_video.h"
22 #define RPF_MAX_WIDTH 8190
23 #define RPF_MAX_HEIGHT 8190
25 /* -----------------------------------------------------------------------------
29 static inline u32
vsp1_rpf_read(struct vsp1_rwpf
*rpf
, u32 reg
)
31 return vsp1_read(rpf
->entity
.vsp1
,
32 reg
+ rpf
->entity
.index
* VI6_RPF_OFFSET
);
35 static inline void vsp1_rpf_write(struct vsp1_rwpf
*rpf
, u32 reg
, u32 data
)
37 vsp1_write(rpf
->entity
.vsp1
,
38 reg
+ rpf
->entity
.index
* VI6_RPF_OFFSET
, data
);
41 /* -----------------------------------------------------------------------------
42 * V4L2 Subdevice Core Operations
45 static int rpf_s_stream(struct v4l2_subdev
*subdev
, int enable
)
47 struct vsp1_rwpf
*rpf
= to_rwpf(subdev
);
48 const struct vsp1_format_info
*fmtinfo
= rpf
->video
.fmtinfo
;
49 const struct v4l2_pix_format_mplane
*format
= &rpf
->video
.format
;
50 const struct v4l2_rect
*crop
= &rpf
->crop
;
57 /* Source size, stride and crop offsets.
59 * The crop offsets correspond to the location of the crop rectangle top
60 * left corner in the plane buffer. Only two offsets are needed, as
61 * planes 2 and 3 always have identical strides.
63 vsp1_rpf_write(rpf
, VI6_RPF_SRC_BSIZE
,
64 (crop
->width
<< VI6_RPF_SRC_BSIZE_BHSIZE_SHIFT
) |
65 (crop
->height
<< VI6_RPF_SRC_BSIZE_BVSIZE_SHIFT
));
66 vsp1_rpf_write(rpf
, VI6_RPF_SRC_ESIZE
,
67 (crop
->width
<< VI6_RPF_SRC_ESIZE_EHSIZE_SHIFT
) |
68 (crop
->height
<< VI6_RPF_SRC_ESIZE_EVSIZE_SHIFT
));
70 rpf
->offsets
[0] = crop
->top
* format
->plane_fmt
[0].bytesperline
71 + crop
->left
* fmtinfo
->bpp
[0] / 8;
72 pstride
= format
->plane_fmt
[0].bytesperline
73 << VI6_RPF_SRCM_PSTRIDE_Y_SHIFT
;
74 if (format
->num_planes
> 1) {
75 rpf
->offsets
[1] = crop
->top
* format
->plane_fmt
[1].bytesperline
76 + crop
->left
* fmtinfo
->bpp
[1] / 8;
77 pstride
|= format
->plane_fmt
[1].bytesperline
78 << VI6_RPF_SRCM_PSTRIDE_C_SHIFT
;
81 vsp1_rpf_write(rpf
, VI6_RPF_SRCM_PSTRIDE
, pstride
);
84 infmt
= VI6_RPF_INFMT_CIPM
85 | (fmtinfo
->hwfmt
<< VI6_RPF_INFMT_RDFMT_SHIFT
);
88 infmt
|= VI6_RPF_INFMT_SPYCS
;
90 infmt
|= VI6_RPF_INFMT_SPUVS
;
92 if (rpf
->entity
.formats
[RWPF_PAD_SINK
].code
!=
93 rpf
->entity
.formats
[RWPF_PAD_SOURCE
].code
)
94 infmt
|= VI6_RPF_INFMT_CSC
;
96 vsp1_rpf_write(rpf
, VI6_RPF_INFMT
, infmt
);
97 vsp1_rpf_write(rpf
, VI6_RPF_DSWAP
, fmtinfo
->swap
);
99 /* Output location. Composing isn't supported yet. */
100 vsp1_rpf_write(rpf
, VI6_RPF_LOC
, 0);
102 /* Disable alpha, mask and color key. Set the alpha channel to a fixed
105 vsp1_rpf_write(rpf
, VI6_RPF_ALPH_SEL
, VI6_RPF_ALPH_SEL_ASEL_FIXED
);
106 vsp1_rpf_write(rpf
, VI6_RPF_VRTCOL_SET
,
107 255 << VI6_RPF_VRTCOL_SET_LAYA_SHIFT
);
108 vsp1_rpf_write(rpf
, VI6_RPF_MSK_CTRL
, 0);
109 vsp1_rpf_write(rpf
, VI6_RPF_CKEY_CTRL
, 0);
114 /* -----------------------------------------------------------------------------
115 * V4L2 Subdevice Operations
118 static struct v4l2_subdev_video_ops rpf_video_ops
= {
119 .s_stream
= rpf_s_stream
,
122 static struct v4l2_subdev_pad_ops rpf_pad_ops
= {
123 .enum_mbus_code
= vsp1_rwpf_enum_mbus_code
,
124 .enum_frame_size
= vsp1_rwpf_enum_frame_size
,
125 .get_fmt
= vsp1_rwpf_get_format
,
126 .set_fmt
= vsp1_rwpf_set_format
,
127 .get_selection
= vsp1_rwpf_get_selection
,
128 .set_selection
= vsp1_rwpf_set_selection
,
131 static struct v4l2_subdev_ops rpf_ops
= {
132 .video
= &rpf_video_ops
,
136 /* -----------------------------------------------------------------------------
137 * Video Device Operations
140 static void rpf_vdev_queue(struct vsp1_video
*video
,
141 struct vsp1_video_buffer
*buf
)
143 struct vsp1_rwpf
*rpf
= container_of(video
, struct vsp1_rwpf
, video
);
145 vsp1_rpf_write(rpf
, VI6_RPF_SRCM_ADDR_Y
,
146 buf
->addr
[0] + rpf
->offsets
[0]);
147 if (buf
->buf
.num_planes
> 1)
148 vsp1_rpf_write(rpf
, VI6_RPF_SRCM_ADDR_C0
,
149 buf
->addr
[1] + rpf
->offsets
[1]);
150 if (buf
->buf
.num_planes
> 2)
151 vsp1_rpf_write(rpf
, VI6_RPF_SRCM_ADDR_C1
,
152 buf
->addr
[2] + rpf
->offsets
[1]);
155 static const struct vsp1_video_operations rpf_vdev_ops
= {
156 .queue
= rpf_vdev_queue
,
159 /* -----------------------------------------------------------------------------
160 * Initialization and Cleanup
163 struct vsp1_rwpf
*vsp1_rpf_create(struct vsp1_device
*vsp1
, unsigned int index
)
165 struct v4l2_subdev
*subdev
;
166 struct vsp1_video
*video
;
167 struct vsp1_rwpf
*rpf
;
170 rpf
= devm_kzalloc(vsp1
->dev
, sizeof(*rpf
), GFP_KERNEL
);
172 return ERR_PTR(-ENOMEM
);
174 rpf
->max_width
= RPF_MAX_WIDTH
;
175 rpf
->max_height
= RPF_MAX_HEIGHT
;
177 rpf
->entity
.type
= VSP1_ENTITY_RPF
;
178 rpf
->entity
.index
= index
;
179 rpf
->entity
.id
= VI6_DPR_NODE_RPF(index
);
181 ret
= vsp1_entity_init(vsp1
, &rpf
->entity
, 2);
185 /* Initialize the V4L2 subdev. */
186 subdev
= &rpf
->entity
.subdev
;
187 v4l2_subdev_init(subdev
, &rpf_ops
);
189 subdev
->entity
.ops
= &vsp1_media_ops
;
190 subdev
->internal_ops
= &vsp1_subdev_internal_ops
;
191 snprintf(subdev
->name
, sizeof(subdev
->name
), "%s rpf.%u",
192 dev_name(vsp1
->dev
), index
);
193 v4l2_set_subdevdata(subdev
, rpf
);
194 subdev
->flags
|= V4L2_SUBDEV_FL_HAS_DEVNODE
;
196 vsp1_entity_init_formats(subdev
, NULL
);
198 /* Initialize the video device. */
201 video
->type
= V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE
;
203 video
->ops
= &rpf_vdev_ops
;
205 ret
= vsp1_video_init(video
, &rpf
->entity
);
209 /* Connect the video device to the RPF. */
210 ret
= media_entity_create_link(&rpf
->video
.video
.entity
, 0,
211 &rpf
->entity
.subdev
.entity
,
213 MEDIA_LNK_FL_ENABLED
|
214 MEDIA_LNK_FL_IMMUTABLE
);
221 vsp1_video_cleanup(video
);
223 media_entity_cleanup(&rpf
->entity
.subdev
.entity
);