2 * vsp1_lut.c -- R-Car VSP1 Look-Up Table
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>
15 #include <linux/gfp.h>
16 #include <linux/vsp1.h>
18 #include <media/v4l2-subdev.h>
23 #define LUT_MIN_SIZE 4U
24 #define LUT_MAX_SIZE 8190U
26 /* -----------------------------------------------------------------------------
30 static inline u32
vsp1_lut_read(struct vsp1_lut
*lut
, u32 reg
)
32 return vsp1_read(lut
->entity
.vsp1
, reg
);
35 static inline void vsp1_lut_write(struct vsp1_lut
*lut
, u32 reg
, u32 data
)
37 vsp1_write(lut
->entity
.vsp1
, reg
, data
);
40 /* -----------------------------------------------------------------------------
41 * V4L2 Subdevice Core Operations
44 static void lut_configure(struct vsp1_lut
*lut
, struct vsp1_lut_config
*config
)
46 memcpy_toio(lut
->entity
.vsp1
->mmio
+ VI6_LUT_TABLE
, config
->lut
,
50 static long lut_ioctl(struct v4l2_subdev
*subdev
, unsigned int cmd
, void *arg
)
52 struct vsp1_lut
*lut
= to_lut(subdev
);
55 case VIDIOC_VSP1_LUT_CONFIG
:
56 lut_configure(lut
, arg
);
64 /* -----------------------------------------------------------------------------
65 * V4L2 Subdevice Video Operations
68 static int lut_s_stream(struct v4l2_subdev
*subdev
, int enable
)
70 struct vsp1_lut
*lut
= to_lut(subdev
);
75 vsp1_lut_write(lut
, VI6_LUT_CTRL
, VI6_LUT_CTRL_EN
);
80 /* -----------------------------------------------------------------------------
81 * V4L2 Subdevice Pad Operations
84 static int lut_enum_mbus_code(struct v4l2_subdev
*subdev
,
85 struct v4l2_subdev_fh
*fh
,
86 struct v4l2_subdev_mbus_code_enum
*code
)
88 static const unsigned int codes
[] = {
89 V4L2_MBUS_FMT_ARGB8888_1X32
,
90 V4L2_MBUS_FMT_AHSV8888_1X32
,
91 V4L2_MBUS_FMT_AYUV8_1X32
,
93 struct v4l2_mbus_framefmt
*format
;
95 if (code
->pad
== LUT_PAD_SINK
) {
96 if (code
->index
>= ARRAY_SIZE(codes
))
99 code
->code
= codes
[code
->index
];
101 /* The LUT can't perform format conversion, the sink format is
102 * always identical to the source format.
107 format
= v4l2_subdev_get_try_format(fh
, LUT_PAD_SINK
);
108 code
->code
= format
->code
;
114 static int lut_enum_frame_size(struct v4l2_subdev
*subdev
,
115 struct v4l2_subdev_fh
*fh
,
116 struct v4l2_subdev_frame_size_enum
*fse
)
118 struct v4l2_mbus_framefmt
*format
;
120 format
= v4l2_subdev_get_try_format(fh
, fse
->pad
);
122 if (fse
->index
|| fse
->code
!= format
->code
)
125 if (fse
->pad
== LUT_PAD_SINK
) {
126 fse
->min_width
= LUT_MIN_SIZE
;
127 fse
->max_width
= LUT_MAX_SIZE
;
128 fse
->min_height
= LUT_MIN_SIZE
;
129 fse
->max_height
= LUT_MAX_SIZE
;
131 /* The size on the source pad are fixed and always identical to
132 * the size on the sink pad.
134 fse
->min_width
= format
->width
;
135 fse
->max_width
= format
->width
;
136 fse
->min_height
= format
->height
;
137 fse
->max_height
= format
->height
;
143 static int lut_get_format(struct v4l2_subdev
*subdev
, struct v4l2_subdev_fh
*fh
,
144 struct v4l2_subdev_format
*fmt
)
146 struct vsp1_lut
*lut
= to_lut(subdev
);
148 fmt
->format
= *vsp1_entity_get_pad_format(&lut
->entity
, fh
, fmt
->pad
,
154 static int lut_set_format(struct v4l2_subdev
*subdev
, struct v4l2_subdev_fh
*fh
,
155 struct v4l2_subdev_format
*fmt
)
157 struct vsp1_lut
*lut
= to_lut(subdev
);
158 struct v4l2_mbus_framefmt
*format
;
160 /* Default to YUV if the requested format is not supported. */
161 if (fmt
->format
.code
!= V4L2_MBUS_FMT_ARGB8888_1X32
&&
162 fmt
->format
.code
!= V4L2_MBUS_FMT_AHSV8888_1X32
&&
163 fmt
->format
.code
!= V4L2_MBUS_FMT_AYUV8_1X32
)
164 fmt
->format
.code
= V4L2_MBUS_FMT_AYUV8_1X32
;
166 format
= vsp1_entity_get_pad_format(&lut
->entity
, fh
, fmt
->pad
,
169 if (fmt
->pad
== LUT_PAD_SOURCE
) {
170 /* The LUT output format can't be modified. */
171 fmt
->format
= *format
;
175 format
->width
= clamp_t(unsigned int, fmt
->format
.width
,
176 LUT_MIN_SIZE
, LUT_MAX_SIZE
);
177 format
->height
= clamp_t(unsigned int, fmt
->format
.height
,
178 LUT_MIN_SIZE
, LUT_MAX_SIZE
);
179 format
->field
= V4L2_FIELD_NONE
;
180 format
->colorspace
= V4L2_COLORSPACE_SRGB
;
182 fmt
->format
= *format
;
184 /* Propagate the format to the source pad. */
185 format
= vsp1_entity_get_pad_format(&lut
->entity
, fh
, LUT_PAD_SOURCE
,
187 *format
= fmt
->format
;
192 /* -----------------------------------------------------------------------------
193 * V4L2 Subdevice Operations
196 static struct v4l2_subdev_core_ops lut_core_ops
= {
200 static struct v4l2_subdev_video_ops lut_video_ops
= {
201 .s_stream
= lut_s_stream
,
204 static struct v4l2_subdev_pad_ops lut_pad_ops
= {
205 .enum_mbus_code
= lut_enum_mbus_code
,
206 .enum_frame_size
= lut_enum_frame_size
,
207 .get_fmt
= lut_get_format
,
208 .set_fmt
= lut_set_format
,
211 static struct v4l2_subdev_ops lut_ops
= {
212 .core
= &lut_core_ops
,
213 .video
= &lut_video_ops
,
217 /* -----------------------------------------------------------------------------
218 * Initialization and Cleanup
221 struct vsp1_lut
*vsp1_lut_create(struct vsp1_device
*vsp1
)
223 struct v4l2_subdev
*subdev
;
224 struct vsp1_lut
*lut
;
227 lut
= devm_kzalloc(vsp1
->dev
, sizeof(*lut
), GFP_KERNEL
);
229 return ERR_PTR(-ENOMEM
);
231 lut
->entity
.type
= VSP1_ENTITY_LUT
;
232 lut
->entity
.id
= VI6_DPR_NODE_LUT
;
234 ret
= vsp1_entity_init(vsp1
, &lut
->entity
, 2);
238 /* Initialize the V4L2 subdev. */
239 subdev
= &lut
->entity
.subdev
;
240 v4l2_subdev_init(subdev
, &lut_ops
);
242 subdev
->entity
.ops
= &vsp1_media_ops
;
243 subdev
->internal_ops
= &vsp1_subdev_internal_ops
;
244 snprintf(subdev
->name
, sizeof(subdev
->name
), "%s lut",
245 dev_name(vsp1
->dev
));
246 v4l2_set_subdevdata(subdev
, lut
);
247 subdev
->flags
|= V4L2_SUBDEV_FL_HAS_DEVNODE
;
249 vsp1_entity_init_formats(subdev
, NULL
);