]> asedeno.scripts.mit.edu Git - linux.git/blob - drivers/media/platform/vsp1/vsp1_lut.c
Merge tag 'pstore-v4.8' of git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux
[linux.git] / drivers / media / platform / vsp1 / vsp1_lut.c
1 /*
2  * vsp1_lut.c  --  R-Car VSP1 Look-Up Table
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 #include <linux/vsp1.h>
17
18 #include <media/v4l2-subdev.h>
19
20 #include "vsp1.h"
21 #include "vsp1_dl.h"
22 #include "vsp1_lut.h"
23
24 #define LUT_MIN_SIZE                            4U
25 #define LUT_MAX_SIZE                            8190U
26
27 /* -----------------------------------------------------------------------------
28  * Device Access
29  */
30
31 static inline void vsp1_lut_write(struct vsp1_lut *lut, struct vsp1_dl_list *dl,
32                                   u32 reg, u32 data)
33 {
34         vsp1_dl_list_write(dl, reg, data);
35 }
36
37 /* -----------------------------------------------------------------------------
38  * V4L2 Subdevice Core Operations
39  */
40
41 static int lut_set_table(struct vsp1_lut *lut, struct vsp1_lut_config *config)
42 {
43         struct vsp1_dl_body *dlb;
44         unsigned int i;
45
46         dlb = vsp1_dl_fragment_alloc(lut->entity.vsp1, ARRAY_SIZE(config->lut));
47         if (!dlb)
48                 return -ENOMEM;
49
50         for (i = 0; i < ARRAY_SIZE(config->lut); ++i)
51                 vsp1_dl_fragment_write(dlb, VI6_LUT_TABLE + 4 * i,
52                                        config->lut[i]);
53
54         mutex_lock(&lut->lock);
55         swap(lut->lut, dlb);
56         mutex_unlock(&lut->lock);
57
58         vsp1_dl_fragment_free(dlb);
59         return 0;
60 }
61
62 static long lut_ioctl(struct v4l2_subdev *subdev, unsigned int cmd, void *arg)
63 {
64         struct vsp1_lut *lut = to_lut(subdev);
65
66         switch (cmd) {
67         case VIDIOC_VSP1_LUT_CONFIG:
68                 return lut_set_table(lut, arg);
69
70         default:
71                 return -ENOIOCTLCMD;
72         }
73 }
74
75 /* -----------------------------------------------------------------------------
76  * V4L2 Subdevice Pad Operations
77  */
78
79 static int lut_enum_mbus_code(struct v4l2_subdev *subdev,
80                               struct v4l2_subdev_pad_config *cfg,
81                               struct v4l2_subdev_mbus_code_enum *code)
82 {
83         static const unsigned int codes[] = {
84                 MEDIA_BUS_FMT_ARGB8888_1X32,
85                 MEDIA_BUS_FMT_AHSV8888_1X32,
86                 MEDIA_BUS_FMT_AYUV8_1X32,
87         };
88
89         return vsp1_subdev_enum_mbus_code(subdev, cfg, code, codes,
90                                           ARRAY_SIZE(codes));
91 }
92
93 static int lut_enum_frame_size(struct v4l2_subdev *subdev,
94                                struct v4l2_subdev_pad_config *cfg,
95                                struct v4l2_subdev_frame_size_enum *fse)
96 {
97         return vsp1_subdev_enum_frame_size(subdev, cfg, fse, LUT_MIN_SIZE,
98                                            LUT_MIN_SIZE, LUT_MAX_SIZE,
99                                            LUT_MAX_SIZE);
100 }
101
102 static int lut_set_format(struct v4l2_subdev *subdev,
103                           struct v4l2_subdev_pad_config *cfg,
104                           struct v4l2_subdev_format *fmt)
105 {
106         struct vsp1_lut *lut = to_lut(subdev);
107         struct v4l2_subdev_pad_config *config;
108         struct v4l2_mbus_framefmt *format;
109
110         config = vsp1_entity_get_pad_config(&lut->entity, cfg, fmt->which);
111         if (!config)
112                 return -EINVAL;
113
114         /* Default to YUV if the requested format is not supported. */
115         if (fmt->format.code != MEDIA_BUS_FMT_ARGB8888_1X32 &&
116             fmt->format.code != MEDIA_BUS_FMT_AHSV8888_1X32 &&
117             fmt->format.code != MEDIA_BUS_FMT_AYUV8_1X32)
118                 fmt->format.code = MEDIA_BUS_FMT_AYUV8_1X32;
119
120         format = vsp1_entity_get_pad_format(&lut->entity, config, fmt->pad);
121
122         if (fmt->pad == LUT_PAD_SOURCE) {
123                 /* The LUT output format can't be modified. */
124                 fmt->format = *format;
125                 return 0;
126         }
127
128         format->code = fmt->format.code;
129         format->width = clamp_t(unsigned int, fmt->format.width,
130                                 LUT_MIN_SIZE, LUT_MAX_SIZE);
131         format->height = clamp_t(unsigned int, fmt->format.height,
132                                  LUT_MIN_SIZE, LUT_MAX_SIZE);
133         format->field = V4L2_FIELD_NONE;
134         format->colorspace = V4L2_COLORSPACE_SRGB;
135
136         fmt->format = *format;
137
138         /* Propagate the format to the source pad. */
139         format = vsp1_entity_get_pad_format(&lut->entity, config,
140                                             LUT_PAD_SOURCE);
141         *format = fmt->format;
142
143         return 0;
144 }
145
146 /* -----------------------------------------------------------------------------
147  * V4L2 Subdevice Operations
148  */
149
150 static struct v4l2_subdev_core_ops lut_core_ops = {
151         .ioctl = lut_ioctl,
152 };
153
154 static struct v4l2_subdev_pad_ops lut_pad_ops = {
155         .init_cfg = vsp1_entity_init_cfg,
156         .enum_mbus_code = lut_enum_mbus_code,
157         .enum_frame_size = lut_enum_frame_size,
158         .get_fmt = vsp1_subdev_get_pad_format,
159         .set_fmt = lut_set_format,
160 };
161
162 static struct v4l2_subdev_ops lut_ops = {
163         .core   = &lut_core_ops,
164         .pad    = &lut_pad_ops,
165 };
166
167 /* -----------------------------------------------------------------------------
168  * VSP1 Entity Operations
169  */
170
171 static void lut_configure(struct vsp1_entity *entity,
172                           struct vsp1_pipeline *pipe,
173                           struct vsp1_dl_list *dl)
174 {
175         struct vsp1_lut *lut = to_lut(&entity->subdev);
176
177         vsp1_lut_write(lut, dl, VI6_LUT_CTRL, VI6_LUT_CTRL_EN);
178
179         mutex_lock(&lut->lock);
180         if (lut->lut) {
181                 vsp1_dl_list_add_fragment(dl, lut->lut);
182                 lut->lut = NULL;
183         }
184         mutex_unlock(&lut->lock);
185 }
186
187 static const struct vsp1_entity_operations lut_entity_ops = {
188         .configure = lut_configure,
189 };
190
191 /* -----------------------------------------------------------------------------
192  * Initialization and Cleanup
193  */
194
195 struct vsp1_lut *vsp1_lut_create(struct vsp1_device *vsp1)
196 {
197         struct vsp1_lut *lut;
198         int ret;
199
200         lut = devm_kzalloc(vsp1->dev, sizeof(*lut), GFP_KERNEL);
201         if (lut == NULL)
202                 return ERR_PTR(-ENOMEM);
203
204         lut->entity.ops = &lut_entity_ops;
205         lut->entity.type = VSP1_ENTITY_LUT;
206
207         ret = vsp1_entity_init(vsp1, &lut->entity, "lut", 2, &lut_ops);
208         if (ret < 0)
209                 return ERR_PTR(ret);
210
211         return lut;
212 }