diff options
Diffstat (limited to 'drivers/media/platform/vsp1/vsp1_hsit.c')
-rw-r--r-- | drivers/media/platform/vsp1/vsp1_hsit.c | 222 |
1 files changed, 222 insertions, 0 deletions
diff --git a/drivers/media/platform/vsp1/vsp1_hsit.c b/drivers/media/platform/vsp1/vsp1_hsit.c new file mode 100644 index 000000000000..285485350d82 --- /dev/null +++ b/drivers/media/platform/vsp1/vsp1_hsit.c | |||
@@ -0,0 +1,222 @@ | |||
1 | /* | ||
2 | * vsp1_hsit.c -- R-Car VSP1 Hue Saturation value (Inverse) Transform | ||
3 | * | ||
4 | * Copyright (C) 2013 Renesas Corporation | ||
5 | * | ||
6 | * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) | ||
7 | * | ||
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. | ||
12 | */ | ||
13 | |||
14 | #include <linux/device.h> | ||
15 | #include <linux/gfp.h> | ||
16 | |||
17 | #include <media/v4l2-subdev.h> | ||
18 | |||
19 | #include "vsp1.h" | ||
20 | #include "vsp1_hsit.h" | ||
21 | |||
22 | #define HSIT_MIN_SIZE 4U | ||
23 | #define HSIT_MAX_SIZE 8190U | ||
24 | |||
25 | /* ----------------------------------------------------------------------------- | ||
26 | * Device Access | ||
27 | */ | ||
28 | |||
29 | static inline u32 vsp1_hsit_read(struct vsp1_hsit *hsit, u32 reg) | ||
30 | { | ||
31 | return vsp1_read(hsit->entity.vsp1, reg); | ||
32 | } | ||
33 | |||
34 | static inline void vsp1_hsit_write(struct vsp1_hsit *hsit, u32 reg, u32 data) | ||
35 | { | ||
36 | vsp1_write(hsit->entity.vsp1, reg, data); | ||
37 | } | ||
38 | |||
39 | /* ----------------------------------------------------------------------------- | ||
40 | * V4L2 Subdevice Core Operations | ||
41 | */ | ||
42 | |||
43 | static int hsit_s_stream(struct v4l2_subdev *subdev, int enable) | ||
44 | { | ||
45 | struct vsp1_hsit *hsit = to_hsit(subdev); | ||
46 | |||
47 | if (!enable) | ||
48 | return 0; | ||
49 | |||
50 | if (hsit->inverse) | ||
51 | vsp1_hsit_write(hsit, VI6_HSI_CTRL, VI6_HSI_CTRL_EN); | ||
52 | else | ||
53 | vsp1_hsit_write(hsit, VI6_HST_CTRL, VI6_HST_CTRL_EN); | ||
54 | |||
55 | return 0; | ||
56 | } | ||
57 | |||
58 | /* ----------------------------------------------------------------------------- | ||
59 | * V4L2 Subdevice Pad Operations | ||
60 | */ | ||
61 | |||
62 | static int hsit_enum_mbus_code(struct v4l2_subdev *subdev, | ||
63 | struct v4l2_subdev_fh *fh, | ||
64 | struct v4l2_subdev_mbus_code_enum *code) | ||
65 | { | ||
66 | struct vsp1_hsit *hsit = to_hsit(subdev); | ||
67 | |||
68 | if (code->index > 0) | ||
69 | return -EINVAL; | ||
70 | |||
71 | if ((code->pad == HSIT_PAD_SINK && !hsit->inverse) | | ||
72 | (code->pad == HSIT_PAD_SOURCE && hsit->inverse)) | ||
73 | code->code = V4L2_MBUS_FMT_ARGB8888_1X32; | ||
74 | else | ||
75 | code->code = V4L2_MBUS_FMT_AHSV8888_1X32; | ||
76 | |||
77 | return 0; | ||
78 | } | ||
79 | |||
80 | static int hsit_enum_frame_size(struct v4l2_subdev *subdev, | ||
81 | struct v4l2_subdev_fh *fh, | ||
82 | struct v4l2_subdev_frame_size_enum *fse) | ||
83 | { | ||
84 | struct v4l2_mbus_framefmt *format; | ||
85 | |||
86 | format = v4l2_subdev_get_try_format(fh, fse->pad); | ||
87 | |||
88 | if (fse->index || fse->code != format->code) | ||
89 | return -EINVAL; | ||
90 | |||
91 | if (fse->pad == HSIT_PAD_SINK) { | ||
92 | fse->min_width = HSIT_MIN_SIZE; | ||
93 | fse->max_width = HSIT_MAX_SIZE; | ||
94 | fse->min_height = HSIT_MIN_SIZE; | ||
95 | fse->max_height = HSIT_MAX_SIZE; | ||
96 | } else { | ||
97 | /* The size on the source pad are fixed and always identical to | ||
98 | * the size on the sink pad. | ||
99 | */ | ||
100 | fse->min_width = format->width; | ||
101 | fse->max_width = format->width; | ||
102 | fse->min_height = format->height; | ||
103 | fse->max_height = format->height; | ||
104 | } | ||
105 | |||
106 | return 0; | ||
107 | } | ||
108 | |||
109 | static int hsit_get_format(struct v4l2_subdev *subdev, | ||
110 | struct v4l2_subdev_fh *fh, | ||
111 | struct v4l2_subdev_format *fmt) | ||
112 | { | ||
113 | struct vsp1_hsit *hsit = to_hsit(subdev); | ||
114 | |||
115 | fmt->format = *vsp1_entity_get_pad_format(&hsit->entity, fh, fmt->pad, | ||
116 | fmt->which); | ||
117 | |||
118 | return 0; | ||
119 | } | ||
120 | |||
121 | static int hsit_set_format(struct v4l2_subdev *subdev, | ||
122 | struct v4l2_subdev_fh *fh, | ||
123 | struct v4l2_subdev_format *fmt) | ||
124 | { | ||
125 | struct vsp1_hsit *hsit = to_hsit(subdev); | ||
126 | struct v4l2_mbus_framefmt *format; | ||
127 | |||
128 | format = vsp1_entity_get_pad_format(&hsit->entity, fh, fmt->pad, | ||
129 | fmt->which); | ||
130 | |||
131 | if (fmt->pad == HSIT_PAD_SOURCE) { | ||
132 | /* The HST and HSI output format code and resolution can't be | ||
133 | * modified. | ||
134 | */ | ||
135 | fmt->format = *format; | ||
136 | return 0; | ||
137 | } | ||
138 | |||
139 | format->code = hsit->inverse ? V4L2_MBUS_FMT_AHSV8888_1X32 | ||
140 | : V4L2_MBUS_FMT_ARGB8888_1X32; | ||
141 | format->width = clamp_t(unsigned int, fmt->format.width, | ||
142 | HSIT_MIN_SIZE, HSIT_MAX_SIZE); | ||
143 | format->height = clamp_t(unsigned int, fmt->format.height, | ||
144 | HSIT_MIN_SIZE, HSIT_MAX_SIZE); | ||
145 | format->field = V4L2_FIELD_NONE; | ||
146 | format->colorspace = V4L2_COLORSPACE_SRGB; | ||
147 | |||
148 | fmt->format = *format; | ||
149 | |||
150 | /* Propagate the format to the source pad. */ | ||
151 | format = vsp1_entity_get_pad_format(&hsit->entity, fh, HSIT_PAD_SOURCE, | ||
152 | fmt->which); | ||
153 | *format = fmt->format; | ||
154 | format->code = hsit->inverse ? V4L2_MBUS_FMT_ARGB8888_1X32 | ||
155 | : V4L2_MBUS_FMT_AHSV8888_1X32; | ||
156 | |||
157 | return 0; | ||
158 | } | ||
159 | |||
160 | /* ----------------------------------------------------------------------------- | ||
161 | * V4L2 Subdevice Operations | ||
162 | */ | ||
163 | |||
164 | static struct v4l2_subdev_video_ops hsit_video_ops = { | ||
165 | .s_stream = hsit_s_stream, | ||
166 | }; | ||
167 | |||
168 | static struct v4l2_subdev_pad_ops hsit_pad_ops = { | ||
169 | .enum_mbus_code = hsit_enum_mbus_code, | ||
170 | .enum_frame_size = hsit_enum_frame_size, | ||
171 | .get_fmt = hsit_get_format, | ||
172 | .set_fmt = hsit_set_format, | ||
173 | }; | ||
174 | |||
175 | static struct v4l2_subdev_ops hsit_ops = { | ||
176 | .video = &hsit_video_ops, | ||
177 | .pad = &hsit_pad_ops, | ||
178 | }; | ||
179 | |||
180 | /* ----------------------------------------------------------------------------- | ||
181 | * Initialization and Cleanup | ||
182 | */ | ||
183 | |||
184 | struct vsp1_hsit *vsp1_hsit_create(struct vsp1_device *vsp1, bool inverse) | ||
185 | { | ||
186 | struct v4l2_subdev *subdev; | ||
187 | struct vsp1_hsit *hsit; | ||
188 | int ret; | ||
189 | |||
190 | hsit = devm_kzalloc(vsp1->dev, sizeof(*hsit), GFP_KERNEL); | ||
191 | if (hsit == NULL) | ||
192 | return ERR_PTR(-ENOMEM); | ||
193 | |||
194 | hsit->inverse = inverse; | ||
195 | |||
196 | if (inverse) { | ||
197 | hsit->entity.type = VSP1_ENTITY_HSI; | ||
198 | hsit->entity.id = VI6_DPR_NODE_HSI; | ||
199 | } else { | ||
200 | hsit->entity.type = VSP1_ENTITY_HST; | ||
201 | hsit->entity.id = VI6_DPR_NODE_HST; | ||
202 | } | ||
203 | |||
204 | ret = vsp1_entity_init(vsp1, &hsit->entity, 2); | ||
205 | if (ret < 0) | ||
206 | return ERR_PTR(ret); | ||
207 | |||
208 | /* Initialize the V4L2 subdev. */ | ||
209 | subdev = &hsit->entity.subdev; | ||
210 | v4l2_subdev_init(subdev, &hsit_ops); | ||
211 | |||
212 | subdev->entity.ops = &vsp1_media_ops; | ||
213 | subdev->internal_ops = &vsp1_subdev_internal_ops; | ||
214 | snprintf(subdev->name, sizeof(subdev->name), "%s %s", | ||
215 | dev_name(vsp1->dev), inverse ? "hsi" : "hst"); | ||
216 | v4l2_set_subdevdata(subdev, hsit); | ||
217 | subdev->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; | ||
218 | |||
219 | vsp1_entity_init_formats(subdev, NULL); | ||
220 | |||
221 | return hsit; | ||
222 | } | ||