1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21#include <media/v4l2-event.h>
22#include <media/v4l2-mediabus.h>
23
24#include <media/videobuf-vmalloc.h>
25#include <linux/delay.h>
26
27#include "ia_css.h"
28
29#include "atomisp_cmd.h"
30#include "atomisp_common.h"
31#include "atomisp_file.h"
32#include "atomisp_internal.h"
33#include "atomisp_ioctl.h"
34
35static void file_work(struct work_struct *work)
36{
37 struct atomisp_file_device *file_dev =
38 container_of(work, struct atomisp_file_device, work);
39 struct atomisp_device *isp = file_dev->isp;
40
41 struct atomisp_sub_device *asd = &isp->asd[0];
42 struct atomisp_video_pipe *out_pipe = &asd->video_in;
43 unsigned short *buf = videobuf_to_vmalloc(out_pipe->outq.bufs[0]);
44 struct v4l2_mbus_framefmt isp_sink_fmt;
45
46 if (asd->streaming != ATOMISP_DEVICE_STREAMING_ENABLED)
47 return;
48
49 dev_dbg(isp->dev, ">%s: ready to start streaming\n", __func__);
50 isp_sink_fmt = *atomisp_subdev_get_ffmt(&asd->subdev, NULL,
51 V4L2_SUBDEV_FORMAT_ACTIVE,
52 ATOMISP_SUBDEV_PAD_SINK);
53
54 while (!ia_css_isp_has_started())
55 usleep_range(1000, 1500);
56
57 ia_css_stream_send_input_frame(asd->stream_env[ATOMISP_INPUT_STREAM_GENERAL].stream,
58 buf, isp_sink_fmt.width,
59 isp_sink_fmt.height);
60 dev_dbg(isp->dev, "<%s: streaming done\n", __func__);
61}
62
63static int file_input_s_stream(struct v4l2_subdev *sd, int enable)
64{
65 struct atomisp_file_device *file_dev = v4l2_get_subdevdata(sd);
66 struct atomisp_device *isp = file_dev->isp;
67
68 struct atomisp_sub_device *asd = &isp->asd[0];
69
70 dev_dbg(isp->dev, "%s: enable %d\n", __func__, enable);
71 if (enable) {
72 if (asd->streaming != ATOMISP_DEVICE_STREAMING_ENABLED)
73 return 0;
74
75 queue_work(file_dev->work_queue, &file_dev->work);
76 return 0;
77 }
78 cancel_work_sync(&file_dev->work);
79 return 0;
80}
81
82static int file_input_get_fmt(struct v4l2_subdev *sd,
83 struct v4l2_subdev_state *sd_state,
84 struct v4l2_subdev_format *format)
85{
86 struct v4l2_mbus_framefmt *fmt = &format->format;
87 struct atomisp_file_device *file_dev = v4l2_get_subdevdata(sd);
88 struct atomisp_device *isp = file_dev->isp;
89
90 struct atomisp_sub_device *asd = &isp->asd[0];
91 struct v4l2_mbus_framefmt *isp_sink_fmt;
92
93 if (format->pad)
94 return -EINVAL;
95 isp_sink_fmt = atomisp_subdev_get_ffmt(&asd->subdev, NULL,
96 V4L2_SUBDEV_FORMAT_ACTIVE,
97 ATOMISP_SUBDEV_PAD_SINK);
98
99 fmt->width = isp_sink_fmt->width;
100 fmt->height = isp_sink_fmt->height;
101 fmt->code = isp_sink_fmt->code;
102
103 return 0;
104}
105
106static int file_input_set_fmt(struct v4l2_subdev *sd,
107 struct v4l2_subdev_state *sd_state,
108 struct v4l2_subdev_format *format)
109{
110 struct v4l2_mbus_framefmt *fmt = &format->format;
111
112 if (format->pad)
113 return -EINVAL;
114 file_input_get_fmt(sd, sd_state, format);
115 if (format->which == V4L2_SUBDEV_FORMAT_TRY)
116 sd_state->pads->try_fmt = *fmt;
117 return 0;
118}
119
120static int file_input_log_status(struct v4l2_subdev *sd)
121{
122
123 return 0;
124}
125
126static int file_input_s_power(struct v4l2_subdev *sd, int on)
127{
128
129 return 0;
130}
131
132static int file_input_enum_mbus_code(struct v4l2_subdev *sd,
133 struct v4l2_subdev_state *sd_state,
134 struct v4l2_subdev_mbus_code_enum *code)
135{
136
137 return 0;
138}
139
140static int file_input_enum_frame_size(struct v4l2_subdev *sd,
141 struct v4l2_subdev_state *sd_state,
142 struct v4l2_subdev_frame_size_enum *fse)
143{
144
145 return 0;
146}
147
148static int file_input_enum_frame_ival(struct v4l2_subdev *sd,
149 struct v4l2_subdev_state *sd_state,
150 struct v4l2_subdev_frame_interval_enum
151 *fie)
152{
153
154 return 0;
155}
156
157static const struct v4l2_subdev_video_ops file_input_video_ops = {
158 .s_stream = file_input_s_stream,
159};
160
161static const struct v4l2_subdev_core_ops file_input_core_ops = {
162 .log_status = file_input_log_status,
163 .s_power = file_input_s_power,
164};
165
166static const struct v4l2_subdev_pad_ops file_input_pad_ops = {
167 .enum_mbus_code = file_input_enum_mbus_code,
168 .enum_frame_size = file_input_enum_frame_size,
169 .enum_frame_interval = file_input_enum_frame_ival,
170 .get_fmt = file_input_get_fmt,
171 .set_fmt = file_input_set_fmt,
172};
173
174static const struct v4l2_subdev_ops file_input_ops = {
175 .core = &file_input_core_ops,
176 .video = &file_input_video_ops,
177 .pad = &file_input_pad_ops,
178};
179
180void
181atomisp_file_input_unregister_entities(struct atomisp_file_device *file_dev)
182{
183 media_entity_cleanup(&file_dev->sd.entity);
184 v4l2_device_unregister_subdev(&file_dev->sd);
185}
186
187int atomisp_file_input_register_entities(struct atomisp_file_device *file_dev,
188 struct v4l2_device *vdev)
189{
190
191 return v4l2_device_register_subdev(vdev, &file_dev->sd);
192}
193
194void atomisp_file_input_cleanup(struct atomisp_device *isp)
195{
196 struct atomisp_file_device *file_dev = &isp->file_dev;
197
198 if (file_dev->work_queue) {
199 destroy_workqueue(file_dev->work_queue);
200 file_dev->work_queue = NULL;
201 }
202}
203
204int atomisp_file_input_init(struct atomisp_device *isp)
205{
206 struct atomisp_file_device *file_dev = &isp->file_dev;
207 struct v4l2_subdev *sd = &file_dev->sd;
208 struct media_pad *pads = file_dev->pads;
209 struct media_entity *me = &sd->entity;
210
211 file_dev->isp = isp;
212 file_dev->work_queue = alloc_workqueue(isp->v4l2_dev.name, 0, 1);
213 if (!file_dev->work_queue) {
214 dev_err(isp->dev, "Failed to initialize file inject workq\n");
215 return -ENOMEM;
216 }
217
218 INIT_WORK(&file_dev->work, file_work);
219
220 v4l2_subdev_init(sd, &file_input_ops);
221 sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
222 strscpy(sd->name, "file_input_subdev", sizeof(sd->name));
223 v4l2_set_subdevdata(sd, file_dev);
224
225 pads[0].flags = MEDIA_PAD_FL_SINK;
226 me->function = MEDIA_ENT_F_V4L2_SUBDEV_UNKNOWN;
227
228 return media_entity_pads_init(me, 1, pads);
229}
230