aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/media/platform/vimc
diff options
context:
space:
mode:
authorLucas A. M. Magalhães <lucmaga@gmail.com>2019-01-21 20:05:01 -0500
committerMauro Carvalho Chehab <mchehab+samsung@kernel.org>2019-02-07 12:23:09 -0500
commitadc589d2a20808fb99d46a78175cd023f2040338 (patch)
treed31919d178d5b9a82a3be8fb9aea4a2e74742e66 /drivers/media/platform/vimc
parent276c1f066bdaaed6fe82ed231e1eff2f41c34882 (diff)
media: vimc: Add vimc-streamer for stream control
Add a linear pipeline logic for the stream control. It's created by walking backwards on the entity graph. When the stream starts it will simply loop through the pipeline calling the respective process_frame function of each entity. Fixes: f2fe89061d797 ("vimc: Virtual Media Controller core, capture and sensor") Cc: stable@vger.kernel.org # for v4.20 Signed-off-by: Lucas A. M. Magalhães <lucmaga@gmail.com> Acked-by: Helen Koike <helen.koike@collabora.com> Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl> [hverkuil-cisco@xs4all.nl: fixed small space-after-tab issue in the patch] Signed-off-by: Mauro Carvalho Chehab <mchehab+samsung@kernel.org>
Diffstat (limited to 'drivers/media/platform/vimc')
-rw-r--r--drivers/media/platform/vimc/Makefile3
-rw-r--r--drivers/media/platform/vimc/vimc-capture.c18
-rw-r--r--drivers/media/platform/vimc/vimc-common.c35
-rw-r--r--drivers/media/platform/vimc/vimc-common.h15
-rw-r--r--drivers/media/platform/vimc/vimc-debayer.c26
-rw-r--r--drivers/media/platform/vimc/vimc-scaler.c28
-rw-r--r--drivers/media/platform/vimc/vimc-sensor.c56
-rw-r--r--drivers/media/platform/vimc/vimc-streamer.c188
-rw-r--r--drivers/media/platform/vimc/vimc-streamer.h38
9 files changed, 260 insertions, 147 deletions
diff --git a/drivers/media/platform/vimc/Makefile b/drivers/media/platform/vimc/Makefile
index 4b2e3de7856e..c4fc8e7d365a 100644
--- a/drivers/media/platform/vimc/Makefile
+++ b/drivers/media/platform/vimc/Makefile
@@ -5,6 +5,7 @@ vimc_common-objs := vimc-common.o
5vimc_debayer-objs := vimc-debayer.o 5vimc_debayer-objs := vimc-debayer.o
6vimc_scaler-objs := vimc-scaler.o 6vimc_scaler-objs := vimc-scaler.o
7vimc_sensor-objs := vimc-sensor.o 7vimc_sensor-objs := vimc-sensor.o
8vimc_streamer-objs := vimc-streamer.o
8 9
9obj-$(CONFIG_VIDEO_VIMC) += vimc.o vimc_capture.o vimc_common.o vimc-debayer.o \ 10obj-$(CONFIG_VIDEO_VIMC) += vimc.o vimc_capture.o vimc_common.o vimc-debayer.o \
10 vimc_scaler.o vimc_sensor.o 11 vimc_scaler.o vimc_sensor.o vimc_streamer.o
diff --git a/drivers/media/platform/vimc/vimc-capture.c b/drivers/media/platform/vimc/vimc-capture.c
index aaeddf24b042..93837d9eecd2 100644
--- a/drivers/media/platform/vimc/vimc-capture.c
+++ b/drivers/media/platform/vimc/vimc-capture.c
@@ -24,6 +24,7 @@
24#include <media/videobuf2-vmalloc.h> 24#include <media/videobuf2-vmalloc.h>
25 25
26#include "vimc-common.h" 26#include "vimc-common.h"
27#include "vimc-streamer.h"
27 28
28#define VIMC_CAP_DRV_NAME "vimc-capture" 29#define VIMC_CAP_DRV_NAME "vimc-capture"
29 30
@@ -44,7 +45,7 @@ struct vimc_cap_device {
44 spinlock_t qlock; 45 spinlock_t qlock;
45 struct mutex lock; 46 struct mutex lock;
46 u32 sequence; 47 u32 sequence;
47 struct media_pipeline pipe; 48 struct vimc_stream stream;
48}; 49};
49 50
50static const struct v4l2_pix_format fmt_default = { 51static const struct v4l2_pix_format fmt_default = {
@@ -248,14 +249,13 @@ static int vimc_cap_start_streaming(struct vb2_queue *vq, unsigned int count)
248 vcap->sequence = 0; 249 vcap->sequence = 0;
249 250
250 /* Start the media pipeline */ 251 /* Start the media pipeline */
251 ret = media_pipeline_start(entity, &vcap->pipe); 252 ret = media_pipeline_start(entity, &vcap->stream.pipe);
252 if (ret) { 253 if (ret) {
253 vimc_cap_return_all_buffers(vcap, VB2_BUF_STATE_QUEUED); 254 vimc_cap_return_all_buffers(vcap, VB2_BUF_STATE_QUEUED);
254 return ret; 255 return ret;
255 } 256 }
256 257
257 /* Enable streaming from the pipe */ 258 ret = vimc_streamer_s_stream(&vcap->stream, &vcap->ved, 1);
258 ret = vimc_pipeline_s_stream(&vcap->vdev.entity, 1);
259 if (ret) { 259 if (ret) {
260 media_pipeline_stop(entity); 260 media_pipeline_stop(entity);
261 vimc_cap_return_all_buffers(vcap, VB2_BUF_STATE_QUEUED); 261 vimc_cap_return_all_buffers(vcap, VB2_BUF_STATE_QUEUED);
@@ -273,8 +273,7 @@ static void vimc_cap_stop_streaming(struct vb2_queue *vq)
273{ 273{
274 struct vimc_cap_device *vcap = vb2_get_drv_priv(vq); 274 struct vimc_cap_device *vcap = vb2_get_drv_priv(vq);
275 275
276 /* Disable streaming from the pipe */ 276 vimc_streamer_s_stream(&vcap->stream, &vcap->ved, 0);
277 vimc_pipeline_s_stream(&vcap->vdev.entity, 0);
278 277
279 /* Stop the media pipeline */ 278 /* Stop the media pipeline */
280 media_pipeline_stop(&vcap->vdev.entity); 279 media_pipeline_stop(&vcap->vdev.entity);
@@ -355,8 +354,8 @@ static void vimc_cap_comp_unbind(struct device *comp, struct device *master,
355 kfree(vcap); 354 kfree(vcap);
356} 355}
357 356
358static void vimc_cap_process_frame(struct vimc_ent_device *ved, 357static void *vimc_cap_process_frame(struct vimc_ent_device *ved,
359 struct media_pad *sink, const void *frame) 358 const void *frame)
360{ 359{
361 struct vimc_cap_device *vcap = container_of(ved, struct vimc_cap_device, 360 struct vimc_cap_device *vcap = container_of(ved, struct vimc_cap_device,
362 ved); 361 ved);
@@ -370,7 +369,7 @@ static void vimc_cap_process_frame(struct vimc_ent_device *ved,
370 typeof(*vimc_buf), list); 369 typeof(*vimc_buf), list);
371 if (!vimc_buf) { 370 if (!vimc_buf) {
372 spin_unlock(&vcap->qlock); 371 spin_unlock(&vcap->qlock);
373 return; 372 return ERR_PTR(-EAGAIN);
374 } 373 }
375 374
376 /* Remove this entry from the list */ 375 /* Remove this entry from the list */
@@ -391,6 +390,7 @@ static void vimc_cap_process_frame(struct vimc_ent_device *ved,
391 vb2_set_plane_payload(&vimc_buf->vb2.vb2_buf, 0, 390 vb2_set_plane_payload(&vimc_buf->vb2.vb2_buf, 0,
392 vcap->format.sizeimage); 391 vcap->format.sizeimage);
393 vb2_buffer_done(&vimc_buf->vb2.vb2_buf, VB2_BUF_STATE_DONE); 392 vb2_buffer_done(&vimc_buf->vb2.vb2_buf, VB2_BUF_STATE_DONE);
393 return NULL;
394} 394}
395 395
396static int vimc_cap_comp_bind(struct device *comp, struct device *master, 396static int vimc_cap_comp_bind(struct device *comp, struct device *master,
diff --git a/drivers/media/platform/vimc/vimc-common.c b/drivers/media/platform/vimc/vimc-common.c
index 867e24dbd6b5..c1a74bb2df58 100644
--- a/drivers/media/platform/vimc/vimc-common.c
+++ b/drivers/media/platform/vimc/vimc-common.c
@@ -207,41 +207,6 @@ const struct vimc_pix_map *vimc_pix_map_by_pixelformat(u32 pixelformat)
207} 207}
208EXPORT_SYMBOL_GPL(vimc_pix_map_by_pixelformat); 208EXPORT_SYMBOL_GPL(vimc_pix_map_by_pixelformat);
209 209
210int vimc_propagate_frame(struct media_pad *src, const void *frame)
211{
212 struct media_link *link;
213
214 if (!(src->flags & MEDIA_PAD_FL_SOURCE))
215 return -EINVAL;
216
217 /* Send this frame to all sink pads that are direct linked */
218 list_for_each_entry(link, &src->entity->links, list) {
219 if (link->source == src &&
220 (link->flags & MEDIA_LNK_FL_ENABLED)) {
221 struct vimc_ent_device *ved = NULL;
222 struct media_entity *entity = link->sink->entity;
223
224 if (is_media_entity_v4l2_subdev(entity)) {
225 struct v4l2_subdev *sd =
226 container_of(entity, struct v4l2_subdev,
227 entity);
228 ved = v4l2_get_subdevdata(sd);
229 } else if (is_media_entity_v4l2_video_device(entity)) {
230 struct video_device *vdev =
231 container_of(entity,
232 struct video_device,
233 entity);
234 ved = video_get_drvdata(vdev);
235 }
236 if (ved && ved->process_frame)
237 ved->process_frame(ved, link->sink, frame);
238 }
239 }
240
241 return 0;
242}
243EXPORT_SYMBOL_GPL(vimc_propagate_frame);
244
245/* Helper function to allocate and initialize pads */ 210/* Helper function to allocate and initialize pads */
246struct media_pad *vimc_pads_init(u16 num_pads, const unsigned long *pads_flag) 211struct media_pad *vimc_pads_init(u16 num_pads, const unsigned long *pads_flag)
247{ 212{
diff --git a/drivers/media/platform/vimc/vimc-common.h b/drivers/media/platform/vimc/vimc-common.h
index f491c33c7c14..84539430b5e7 100644
--- a/drivers/media/platform/vimc/vimc-common.h
+++ b/drivers/media/platform/vimc/vimc-common.h
@@ -115,24 +115,13 @@ struct vimc_pix_map {
115struct vimc_ent_device { 115struct vimc_ent_device {
116 struct media_entity *ent; 116 struct media_entity *ent;
117 struct media_pad *pads; 117 struct media_pad *pads;
118 void (*process_frame)(struct vimc_ent_device *ved, 118 void * (*process_frame)(struct vimc_ent_device *ved,
119 struct media_pad *sink, const void *frame); 119 const void *frame);
120 void (*vdev_get_format)(struct vimc_ent_device *ved, 120 void (*vdev_get_format)(struct vimc_ent_device *ved,
121 struct v4l2_pix_format *fmt); 121 struct v4l2_pix_format *fmt);
122}; 122};
123 123
124/** 124/**
125 * vimc_propagate_frame - propagate a frame through the topology
126 *
127 * @src: the source pad where the frame is being originated
128 * @frame: the frame to be propagated
129 *
130 * This function will call the process_frame callback from the vimc_ent_device
131 * struct of the nodes directly connected to the @src pad
132 */
133int vimc_propagate_frame(struct media_pad *src, const void *frame);
134
135/**
136 * vimc_pads_init - initialize pads 125 * vimc_pads_init - initialize pads
137 * 126 *
138 * @num_pads: number of pads to initialize 127 * @num_pads: number of pads to initialize
diff --git a/drivers/media/platform/vimc/vimc-debayer.c b/drivers/media/platform/vimc/vimc-debayer.c
index 77887f66f323..7d77c63b99d2 100644
--- a/drivers/media/platform/vimc/vimc-debayer.c
+++ b/drivers/media/platform/vimc/vimc-debayer.c
@@ -321,7 +321,6 @@ static void vimc_deb_set_rgb_mbus_fmt_rgb888_1x24(struct vimc_deb_device *vdeb,
321static int vimc_deb_s_stream(struct v4l2_subdev *sd, int enable) 321static int vimc_deb_s_stream(struct v4l2_subdev *sd, int enable)
322{ 322{
323 struct vimc_deb_device *vdeb = v4l2_get_subdevdata(sd); 323 struct vimc_deb_device *vdeb = v4l2_get_subdevdata(sd);
324 int ret;
325 324
326 if (enable) { 325 if (enable) {
327 const struct vimc_pix_map *vpix; 326 const struct vimc_pix_map *vpix;
@@ -351,22 +350,10 @@ static int vimc_deb_s_stream(struct v4l2_subdev *sd, int enable)
351 if (!vdeb->src_frame) 350 if (!vdeb->src_frame)
352 return -ENOMEM; 351 return -ENOMEM;
353 352
354 /* Turn the stream on in the subdevices directly connected */
355 ret = vimc_pipeline_s_stream(&vdeb->sd.entity, 1);
356 if (ret) {
357 vfree(vdeb->src_frame);
358 vdeb->src_frame = NULL;
359 return ret;
360 }
361 } else { 353 } else {
362 if (!vdeb->src_frame) 354 if (!vdeb->src_frame)
363 return 0; 355 return 0;
364 356
365 /* Disable streaming from the pipe */
366 ret = vimc_pipeline_s_stream(&vdeb->sd.entity, 0);
367 if (ret)
368 return ret;
369
370 vfree(vdeb->src_frame); 357 vfree(vdeb->src_frame);
371 vdeb->src_frame = NULL; 358 vdeb->src_frame = NULL;
372 } 359 }
@@ -480,9 +467,8 @@ static void vimc_deb_calc_rgb_sink(struct vimc_deb_device *vdeb,
480 } 467 }
481} 468}
482 469
483static void vimc_deb_process_frame(struct vimc_ent_device *ved, 470static void *vimc_deb_process_frame(struct vimc_ent_device *ved,
484 struct media_pad *sink, 471 const void *sink_frame)
485 const void *sink_frame)
486{ 472{
487 struct vimc_deb_device *vdeb = container_of(ved, struct vimc_deb_device, 473 struct vimc_deb_device *vdeb = container_of(ved, struct vimc_deb_device,
488 ved); 474 ved);
@@ -491,7 +477,7 @@ static void vimc_deb_process_frame(struct vimc_ent_device *ved,
491 477
492 /* If the stream in this node is not active, just return */ 478 /* If the stream in this node is not active, just return */
493 if (!vdeb->src_frame) 479 if (!vdeb->src_frame)
494 return; 480 return ERR_PTR(-EINVAL);
495 481
496 for (i = 0; i < vdeb->sink_fmt.height; i++) 482 for (i = 0; i < vdeb->sink_fmt.height; i++)
497 for (j = 0; j < vdeb->sink_fmt.width; j++) { 483 for (j = 0; j < vdeb->sink_fmt.width; j++) {
@@ -499,12 +485,8 @@ static void vimc_deb_process_frame(struct vimc_ent_device *ved,
499 vdeb->set_rgb_src(vdeb, i, j, rgb); 485 vdeb->set_rgb_src(vdeb, i, j, rgb);
500 } 486 }
501 487
502 /* Propagate the frame through all source pads */ 488 return vdeb->src_frame;
503 for (i = 1; i < vdeb->sd.entity.num_pads; i++) {
504 struct media_pad *pad = &vdeb->sd.entity.pads[i];
505 489
506 vimc_propagate_frame(pad, vdeb->src_frame);
507 }
508} 490}
509 491
510static void vimc_deb_comp_unbind(struct device *comp, struct device *master, 492static void vimc_deb_comp_unbind(struct device *comp, struct device *master,
diff --git a/drivers/media/platform/vimc/vimc-scaler.c b/drivers/media/platform/vimc/vimc-scaler.c
index b0952ee86296..39b2a73dfcc1 100644
--- a/drivers/media/platform/vimc/vimc-scaler.c
+++ b/drivers/media/platform/vimc/vimc-scaler.c
@@ -217,7 +217,6 @@ static const struct v4l2_subdev_pad_ops vimc_sca_pad_ops = {
217static int vimc_sca_s_stream(struct v4l2_subdev *sd, int enable) 217static int vimc_sca_s_stream(struct v4l2_subdev *sd, int enable)
218{ 218{
219 struct vimc_sca_device *vsca = v4l2_get_subdevdata(sd); 219 struct vimc_sca_device *vsca = v4l2_get_subdevdata(sd);
220 int ret;
221 220
222 if (enable) { 221 if (enable) {
223 const struct vimc_pix_map *vpix; 222 const struct vimc_pix_map *vpix;
@@ -245,22 +244,10 @@ static int vimc_sca_s_stream(struct v4l2_subdev *sd, int enable)
245 if (!vsca->src_frame) 244 if (!vsca->src_frame)
246 return -ENOMEM; 245 return -ENOMEM;
247 246
248 /* Turn the stream on in the subdevices directly connected */
249 ret = vimc_pipeline_s_stream(&vsca->sd.entity, 1);
250 if (ret) {
251 vfree(vsca->src_frame);
252 vsca->src_frame = NULL;
253 return ret;
254 }
255 } else { 247 } else {
256 if (!vsca->src_frame) 248 if (!vsca->src_frame)
257 return 0; 249 return 0;
258 250
259 /* Disable streaming from the pipe */
260 ret = vimc_pipeline_s_stream(&vsca->sd.entity, 0);
261 if (ret)
262 return ret;
263
264 vfree(vsca->src_frame); 251 vfree(vsca->src_frame);
265 vsca->src_frame = NULL; 252 vsca->src_frame = NULL;
266 } 253 }
@@ -346,26 +333,19 @@ static void vimc_sca_fill_src_frame(const struct vimc_sca_device *const vsca,
346 vimc_sca_scale_pix(vsca, i, j, sink_frame); 333 vimc_sca_scale_pix(vsca, i, j, sink_frame);
347} 334}
348 335
349static void vimc_sca_process_frame(struct vimc_ent_device *ved, 336static void *vimc_sca_process_frame(struct vimc_ent_device *ved,
350 struct media_pad *sink, 337 const void *sink_frame)
351 const void *sink_frame)
352{ 338{
353 struct vimc_sca_device *vsca = container_of(ved, struct vimc_sca_device, 339 struct vimc_sca_device *vsca = container_of(ved, struct vimc_sca_device,
354 ved); 340 ved);
355 unsigned int i;
356 341
357 /* If the stream in this node is not active, just return */ 342 /* If the stream in this node is not active, just return */
358 if (!vsca->src_frame) 343 if (!vsca->src_frame)
359 return; 344 return ERR_PTR(-EINVAL);
360 345
361 vimc_sca_fill_src_frame(vsca, sink_frame); 346 vimc_sca_fill_src_frame(vsca, sink_frame);
362 347
363 /* Propagate the frame through all source pads */ 348 return vsca->src_frame;
364 for (i = 1; i < vsca->sd.entity.num_pads; i++) {
365 struct media_pad *pad = &vsca->sd.entity.pads[i];
366
367 vimc_propagate_frame(pad, vsca->src_frame);
368 }
369}; 349};
370 350
371static void vimc_sca_comp_unbind(struct device *comp, struct device *master, 351static void vimc_sca_comp_unbind(struct device *comp, struct device *master,
diff --git a/drivers/media/platform/vimc/vimc-sensor.c b/drivers/media/platform/vimc/vimc-sensor.c
index 32ca9c6172b1..93961a1e694f 100644
--- a/drivers/media/platform/vimc/vimc-sensor.c
+++ b/drivers/media/platform/vimc/vimc-sensor.c
@@ -16,8 +16,6 @@
16 */ 16 */
17 17
18#include <linux/component.h> 18#include <linux/component.h>
19#include <linux/freezer.h>
20#include <linux/kthread.h>
21#include <linux/module.h> 19#include <linux/module.h>
22#include <linux/mod_devicetable.h> 20#include <linux/mod_devicetable.h>
23#include <linux/platform_device.h> 21#include <linux/platform_device.h>
@@ -201,38 +199,27 @@ static const struct v4l2_subdev_pad_ops vimc_sen_pad_ops = {
201 .set_fmt = vimc_sen_set_fmt, 199 .set_fmt = vimc_sen_set_fmt,
202}; 200};
203 201
204static int vimc_sen_tpg_thread(void *data) 202static void *vimc_sen_process_frame(struct vimc_ent_device *ved,
203 const void *sink_frame)
205{ 204{
206 struct vimc_sen_device *vsen = data; 205 struct vimc_sen_device *vsen = container_of(ved, struct vimc_sen_device,
207 unsigned int i; 206 ved);
208 207 const struct vimc_pix_map *vpix;
209 set_freezable(); 208 unsigned int frame_size;
210 set_current_state(TASK_UNINTERRUPTIBLE);
211
212 for (;;) {
213 try_to_freeze();
214 if (kthread_should_stop())
215 break;
216
217 tpg_fill_plane_buffer(&vsen->tpg, 0, 0, vsen->frame);
218 209
219 /* Send the frame to all source pads */ 210 /* Calculate the frame size */
220 for (i = 0; i < vsen->sd.entity.num_pads; i++) 211 vpix = vimc_pix_map_by_code(vsen->mbus_format.code);
221 vimc_propagate_frame(&vsen->sd.entity.pads[i], 212 frame_size = vsen->mbus_format.width * vpix->bpp *
222 vsen->frame); 213 vsen->mbus_format.height;
223 214
224 /* 60 frames per second */ 215 tpg_fill_plane_buffer(&vsen->tpg, 0, 0, vsen->frame);
225 schedule_timeout(HZ/60); 216 return vsen->frame;
226 }
227
228 return 0;
229} 217}
230 218
231static int vimc_sen_s_stream(struct v4l2_subdev *sd, int enable) 219static int vimc_sen_s_stream(struct v4l2_subdev *sd, int enable)
232{ 220{
233 struct vimc_sen_device *vsen = 221 struct vimc_sen_device *vsen =
234 container_of(sd, struct vimc_sen_device, sd); 222 container_of(sd, struct vimc_sen_device, sd);
235 int ret;
236 223
237 if (enable) { 224 if (enable) {
238 const struct vimc_pix_map *vpix; 225 const struct vimc_pix_map *vpix;
@@ -258,26 +245,8 @@ static int vimc_sen_s_stream(struct v4l2_subdev *sd, int enable)
258 /* configure the test pattern generator */ 245 /* configure the test pattern generator */
259 vimc_sen_tpg_s_format(vsen); 246 vimc_sen_tpg_s_format(vsen);
260 247
261 /* Initialize the image generator thread */
262 vsen->kthread_sen = kthread_run(vimc_sen_tpg_thread, vsen,
263 "%s-sen", vsen->sd.v4l2_dev->name);
264 if (IS_ERR(vsen->kthread_sen)) {
265 dev_err(vsen->dev, "%s: kernel_thread() failed\n",
266 vsen->sd.name);
267 vfree(vsen->frame);
268 vsen->frame = NULL;
269 return PTR_ERR(vsen->kthread_sen);
270 }
271 } else { 248 } else {
272 if (!vsen->kthread_sen)
273 return 0;
274
275 /* Stop image generator */
276 ret = kthread_stop(vsen->kthread_sen);
277 if (ret)
278 return ret;
279 249
280 vsen->kthread_sen = NULL;
281 vfree(vsen->frame); 250 vfree(vsen->frame);
282 vsen->frame = NULL; 251 vsen->frame = NULL;
283 return 0; 252 return 0;
@@ -413,6 +382,7 @@ static int vimc_sen_comp_bind(struct device *comp, struct device *master,
413 if (ret) 382 if (ret)
414 goto err_free_hdl; 383 goto err_free_hdl;
415 384
385 vsen->ved.process_frame = vimc_sen_process_frame;
416 dev_set_drvdata(comp, &vsen->ved); 386 dev_set_drvdata(comp, &vsen->ved);
417 vsen->dev = comp; 387 vsen->dev = comp;
418 388
diff --git a/drivers/media/platform/vimc/vimc-streamer.c b/drivers/media/platform/vimc/vimc-streamer.c
new file mode 100644
index 000000000000..fcc897fb247b
--- /dev/null
+++ b/drivers/media/platform/vimc/vimc-streamer.c
@@ -0,0 +1,188 @@
1// SPDX-License-Identifier: GPL-2.0+
2/*
3 * vimc-streamer.c Virtual Media Controller Driver
4 *
5 * Copyright (C) 2018 Lucas A. M. Magalhães <lucmaga@gmail.com>
6 *
7 */
8
9#include <linux/init.h>
10#include <linux/module.h>
11#include <linux/freezer.h>
12#include <linux/kthread.h>
13
14#include "vimc-streamer.h"
15
16/**
17 * vimc_get_source_entity - get the entity connected with the first sink pad
18 *
19 * @ent: reference media_entity
20 *
21 * Helper function that returns the media entity containing the source pad
22 * linked with the first sink pad from the given media entity pad list.
23 */
24static struct media_entity *vimc_get_source_entity(struct media_entity *ent)
25{
26 struct media_pad *pad;
27 int i;
28
29 for (i = 0; i < ent->num_pads; i++) {
30 if (ent->pads[i].flags & MEDIA_PAD_FL_SOURCE)
31 continue;
32 pad = media_entity_remote_pad(&ent->pads[i]);
33 return pad ? pad->entity : NULL;
34 }
35 return NULL;
36}
37
38/*
39 * vimc_streamer_pipeline_terminate - Disable stream in all ved in stream
40 *
41 * @stream: the pointer to the stream structure with the pipeline to be
42 * disabled.
43 *
44 * Calls s_stream to disable the stream in each entity of the pipeline
45 *
46 */
47static void vimc_streamer_pipeline_terminate(struct vimc_stream *stream)
48{
49 struct media_entity *entity;
50 struct v4l2_subdev *sd;
51
52 while (stream->pipe_size) {
53 stream->pipe_size--;
54 entity = stream->ved_pipeline[stream->pipe_size]->ent;
55 entity = vimc_get_source_entity(entity);
56 stream->ved_pipeline[stream->pipe_size] = NULL;
57
58 if (!is_media_entity_v4l2_subdev(entity))
59 continue;
60
61 sd = media_entity_to_v4l2_subdev(entity);
62 v4l2_subdev_call(sd, video, s_stream, 0);
63 }
64}
65
66/*
67 * vimc_streamer_pipeline_init - initializes the stream structure
68 *
69 * @stream: the pointer to the stream structure to be initialized
70 * @ved: the pointer to the vimc entity initializing the stream
71 *
72 * Initializes the stream structure. Walks through the entity graph to
73 * construct the pipeline used later on the streamer thread.
74 * Calls s_stream to enable stream in all entities of the pipeline.
75 */
76static int vimc_streamer_pipeline_init(struct vimc_stream *stream,
77 struct vimc_ent_device *ved)
78{
79 struct media_entity *entity;
80 struct video_device *vdev;
81 struct v4l2_subdev *sd;
82 int ret = 0;
83
84 stream->pipe_size = 0;
85 while (stream->pipe_size < VIMC_STREAMER_PIPELINE_MAX_SIZE) {
86 if (!ved) {
87 vimc_streamer_pipeline_terminate(stream);
88 return -EINVAL;
89 }
90 stream->ved_pipeline[stream->pipe_size++] = ved;
91
92 entity = vimc_get_source_entity(ved->ent);
93 /* Check if the end of the pipeline was reached*/
94 if (!entity)
95 return 0;
96
97 if (is_media_entity_v4l2_subdev(entity)) {
98 sd = media_entity_to_v4l2_subdev(entity);
99 ret = v4l2_subdev_call(sd, video, s_stream, 1);
100 if (ret && ret != -ENOIOCTLCMD) {
101 vimc_streamer_pipeline_terminate(stream);
102 return ret;
103 }
104 ved = v4l2_get_subdevdata(sd);
105 } else {
106 vdev = container_of(entity,
107 struct video_device,
108 entity);
109 ved = video_get_drvdata(vdev);
110 }
111 }
112
113 vimc_streamer_pipeline_terminate(stream);
114 return -EINVAL;
115}
116
117static int vimc_streamer_thread(void *data)
118{
119 struct vimc_stream *stream = data;
120 int i;
121
122 set_freezable();
123 set_current_state(TASK_UNINTERRUPTIBLE);
124
125 for (;;) {
126 try_to_freeze();
127 if (kthread_should_stop())
128 break;
129
130 for (i = stream->pipe_size - 1; i >= 0; i--) {
131 stream->frame = stream->ved_pipeline[i]->process_frame(
132 stream->ved_pipeline[i],
133 stream->frame);
134 if (!stream->frame)
135 break;
136 if (IS_ERR(stream->frame))
137 break;
138 }
139 //wait for 60hz
140 schedule_timeout(HZ / 60);
141 }
142
143 return 0;
144}
145
146int vimc_streamer_s_stream(struct vimc_stream *stream,
147 struct vimc_ent_device *ved,
148 int enable)
149{
150 int ret;
151
152 if (!stream || !ved)
153 return -EINVAL;
154
155 if (enable) {
156 if (stream->kthread)
157 return 0;
158
159 ret = vimc_streamer_pipeline_init(stream, ved);
160 if (ret)
161 return ret;
162
163 stream->kthread = kthread_run(vimc_streamer_thread, stream,
164 "vimc-streamer thread");
165
166 if (IS_ERR(stream->kthread))
167 return PTR_ERR(stream->kthread);
168
169 } else {
170 if (!stream->kthread)
171 return 0;
172
173 ret = kthread_stop(stream->kthread);
174 if (ret)
175 return ret;
176
177 stream->kthread = NULL;
178
179 vimc_streamer_pipeline_terminate(stream);
180 }
181
182 return 0;
183}
184EXPORT_SYMBOL_GPL(vimc_streamer_s_stream);
185
186MODULE_DESCRIPTION("Virtual Media Controller Driver (VIMC) Streamer");
187MODULE_AUTHOR("Lucas A. M. Magalhães <lucmaga@gmail.com>");
188MODULE_LICENSE("GPL");
diff --git a/drivers/media/platform/vimc/vimc-streamer.h b/drivers/media/platform/vimc/vimc-streamer.h
new file mode 100644
index 000000000000..752af2e2d5a2
--- /dev/null
+++ b/drivers/media/platform/vimc/vimc-streamer.h
@@ -0,0 +1,38 @@
1/* SPDX-License-Identifier: GPL-2.0+ */
2/*
3 * vimc-streamer.h Virtual Media Controller Driver
4 *
5 * Copyright (C) 2018 Lucas A. M. Magalhães <lucmaga@gmail.com>
6 *
7 */
8
9#ifndef _VIMC_STREAMER_H_
10#define _VIMC_STREAMER_H_
11
12#include <media/media-device.h>
13
14#include "vimc-common.h"
15
16#define VIMC_STREAMER_PIPELINE_MAX_SIZE 16
17
18struct vimc_stream {
19 struct media_pipeline pipe;
20 struct vimc_ent_device *ved_pipeline[VIMC_STREAMER_PIPELINE_MAX_SIZE];
21 unsigned int pipe_size;
22 u8 *frame;
23 struct task_struct *kthread;
24};
25
26/**
27 * vimc_streamer_s_streamer - start/stop the stream
28 *
29 * @stream: the pointer to the stream to start or stop
30 * @ved: The last entity of the streamer pipeline
31 * @enable: any non-zero number start the stream, zero stop
32 *
33 */
34int vimc_streamer_s_stream(struct vimc_stream *stream,
35 struct vimc_ent_device *ved,
36 int enable);
37
38#endif //_VIMC_STREAMER_H_