From 5ba0ae43dfdea82bd8f45883e604dcd43454cf24 Mon Sep 17 00:00:00 2001 From: Helen Fornazier Date: Mon, 19 Jun 2017 14:00:11 -0300 Subject: [media] vimc: Move common code from the core Remove helper functions from vimc-core and add it in vimc-common to clean up the core. Signed-off-by: Helen Koike Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vimc/vimc-common.c | 221 ++++++++++++++++++++++++++++++ 1 file changed, 221 insertions(+) create mode 100644 drivers/media/platform/vimc/vimc-common.c (limited to 'drivers/media/platform/vimc/vimc-common.c') diff --git a/drivers/media/platform/vimc/vimc-common.c b/drivers/media/platform/vimc/vimc-common.c new file mode 100644 index 000000000000..42f779a47f15 --- /dev/null +++ b/drivers/media/platform/vimc/vimc-common.c @@ -0,0 +1,221 @@ +/* + * vimc-common.c Virtual Media Controller Driver + * + * Copyright (C) 2015-2017 Helen Koike + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include "vimc-common.h" + +static const struct vimc_pix_map vimc_pix_map_list[] = { + /* TODO: add all missing formats */ + + /* RGB formats */ + { + .code = MEDIA_BUS_FMT_BGR888_1X24, + .pixelformat = V4L2_PIX_FMT_BGR24, + .bpp = 3, + }, + { + .code = MEDIA_BUS_FMT_RGB888_1X24, + .pixelformat = V4L2_PIX_FMT_RGB24, + .bpp = 3, + }, + { + .code = MEDIA_BUS_FMT_ARGB8888_1X32, + .pixelformat = V4L2_PIX_FMT_ARGB32, + .bpp = 4, + }, + + /* Bayer formats */ + { + .code = MEDIA_BUS_FMT_SBGGR8_1X8, + .pixelformat = V4L2_PIX_FMT_SBGGR8, + .bpp = 1, + }, + { + .code = MEDIA_BUS_FMT_SGBRG8_1X8, + .pixelformat = V4L2_PIX_FMT_SGBRG8, + .bpp = 1, + }, + { + .code = MEDIA_BUS_FMT_SGRBG8_1X8, + .pixelformat = V4L2_PIX_FMT_SGRBG8, + .bpp = 1, + }, + { + .code = MEDIA_BUS_FMT_SRGGB8_1X8, + .pixelformat = V4L2_PIX_FMT_SRGGB8, + .bpp = 1, + }, + { + .code = MEDIA_BUS_FMT_SBGGR10_1X10, + .pixelformat = V4L2_PIX_FMT_SBGGR10, + .bpp = 2, + }, + { + .code = MEDIA_BUS_FMT_SGBRG10_1X10, + .pixelformat = V4L2_PIX_FMT_SGBRG10, + .bpp = 2, + }, + { + .code = MEDIA_BUS_FMT_SGRBG10_1X10, + .pixelformat = V4L2_PIX_FMT_SGRBG10, + .bpp = 2, + }, + { + .code = MEDIA_BUS_FMT_SRGGB10_1X10, + .pixelformat = V4L2_PIX_FMT_SRGGB10, + .bpp = 2, + }, + + /* 10bit raw bayer a-law compressed to 8 bits */ + { + .code = MEDIA_BUS_FMT_SBGGR10_ALAW8_1X8, + .pixelformat = V4L2_PIX_FMT_SBGGR10ALAW8, + .bpp = 1, + }, + { + .code = MEDIA_BUS_FMT_SGBRG10_ALAW8_1X8, + .pixelformat = V4L2_PIX_FMT_SGBRG10ALAW8, + .bpp = 1, + }, + { + .code = MEDIA_BUS_FMT_SGRBG10_ALAW8_1X8, + .pixelformat = V4L2_PIX_FMT_SGRBG10ALAW8, + .bpp = 1, + }, + { + .code = MEDIA_BUS_FMT_SRGGB10_ALAW8_1X8, + .pixelformat = V4L2_PIX_FMT_SRGGB10ALAW8, + .bpp = 1, + }, + + /* 10bit raw bayer DPCM compressed to 8 bits */ + { + .code = MEDIA_BUS_FMT_SBGGR10_DPCM8_1X8, + .pixelformat = V4L2_PIX_FMT_SBGGR10DPCM8, + .bpp = 1, + }, + { + .code = MEDIA_BUS_FMT_SGBRG10_DPCM8_1X8, + .pixelformat = V4L2_PIX_FMT_SGBRG10DPCM8, + .bpp = 1, + }, + { + .code = MEDIA_BUS_FMT_SGRBG10_DPCM8_1X8, + .pixelformat = V4L2_PIX_FMT_SGRBG10DPCM8, + .bpp = 1, + }, + { + .code = MEDIA_BUS_FMT_SRGGB10_DPCM8_1X8, + .pixelformat = V4L2_PIX_FMT_SRGGB10DPCM8, + .bpp = 1, + }, + { + .code = MEDIA_BUS_FMT_SBGGR12_1X12, + .pixelformat = V4L2_PIX_FMT_SBGGR12, + .bpp = 2, + }, + { + .code = MEDIA_BUS_FMT_SGBRG12_1X12, + .pixelformat = V4L2_PIX_FMT_SGBRG12, + .bpp = 2, + }, + { + .code = MEDIA_BUS_FMT_SGRBG12_1X12, + .pixelformat = V4L2_PIX_FMT_SGRBG12, + .bpp = 2, + }, + { + .code = MEDIA_BUS_FMT_SRGGB12_1X12, + .pixelformat = V4L2_PIX_FMT_SRGGB12, + .bpp = 2, + }, +}; + +const struct vimc_pix_map *vimc_pix_map_by_code(u32 code) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(vimc_pix_map_list); i++) { + if (vimc_pix_map_list[i].code == code) + return &vimc_pix_map_list[i]; + } + return NULL; +} + +const struct vimc_pix_map *vimc_pix_map_by_pixelformat(u32 pixelformat) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(vimc_pix_map_list); i++) { + if (vimc_pix_map_list[i].pixelformat == pixelformat) + return &vimc_pix_map_list[i]; + } + return NULL; +} + +int vimc_propagate_frame(struct media_pad *src, const void *frame) +{ + struct media_link *link; + + if (!(src->flags & MEDIA_PAD_FL_SOURCE)) + return -EINVAL; + + /* Send this frame to all sink pads that are direct linked */ + list_for_each_entry(link, &src->entity->links, list) { + if (link->source == src && + (link->flags & MEDIA_LNK_FL_ENABLED)) { + struct vimc_ent_device *ved = NULL; + struct media_entity *entity = link->sink->entity; + + if (is_media_entity_v4l2_subdev(entity)) { + struct v4l2_subdev *sd = + container_of(entity, struct v4l2_subdev, + entity); + ved = v4l2_get_subdevdata(sd); + } else if (is_media_entity_v4l2_video_device(entity)) { + struct video_device *vdev = + container_of(entity, + struct video_device, + entity); + ved = video_get_drvdata(vdev); + } + if (ved && ved->process_frame) + ved->process_frame(ved, link->sink, frame); + } + } + + return 0; +} + +/* Helper function to allocate and initialize pads */ +struct media_pad *vimc_pads_init(u16 num_pads, const unsigned long *pads_flag) +{ + struct media_pad *pads; + unsigned int i; + + /* Allocate memory for the pads */ + pads = kcalloc(num_pads, sizeof(*pads), GFP_KERNEL); + if (!pads) + return ERR_PTR(-ENOMEM); + + /* Initialize the pads */ + for (i = 0; i < num_pads; i++) { + pads[i].index = i; + pads[i].flags = pads_flag[i]; + } + + return pads; +} -- cgit v1.2.3 From c149543ef46e3dcc80280a4cc9fbcd05294d3c58 Mon Sep 17 00:00:00 2001 From: Helen Fornazier Date: Mon, 19 Jun 2017 14:00:12 -0300 Subject: [media] vimc: common: Add vimc_ent_sd_* helper As all the subdevices in the topology will be initialized in the same way, to avoid code repetition the vimc_ent_sd_{register, unregister} helper functions were created Signed-off-by: Helen Koike Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vimc/vimc-common.c | 66 +++++++++++++++++++++++++++++++ drivers/media/platform/vimc/vimc-common.h | 39 ++++++++++++++++++ drivers/media/platform/vimc/vimc-sensor.c | 58 +++++---------------------- 3 files changed, 114 insertions(+), 49 deletions(-) (limited to 'drivers/media/platform/vimc/vimc-common.c') diff --git a/drivers/media/platform/vimc/vimc-common.c b/drivers/media/platform/vimc/vimc-common.c index 42f779a47f15..3afbabd739a5 100644 --- a/drivers/media/platform/vimc/vimc-common.c +++ b/drivers/media/platform/vimc/vimc-common.c @@ -219,3 +219,69 @@ struct media_pad *vimc_pads_init(u16 num_pads, const unsigned long *pads_flag) return pads; } + +static const struct media_entity_operations vimc_ent_sd_mops = { + .link_validate = v4l2_subdev_link_validate, +}; + +int vimc_ent_sd_register(struct vimc_ent_device *ved, + struct v4l2_subdev *sd, + struct v4l2_device *v4l2_dev, + const char *const name, + u32 function, + u16 num_pads, + const unsigned long *pads_flag, + const struct v4l2_subdev_ops *sd_ops, + void (*sd_destroy)(struct vimc_ent_device *)) +{ + int ret; + + /* Allocate the pads */ + ved->pads = vimc_pads_init(num_pads, pads_flag); + if (IS_ERR(ved->pads)) + return PTR_ERR(ved->pads); + + /* Fill the vimc_ent_device struct */ + ved->destroy = sd_destroy; + ved->ent = &sd->entity; + + /* Initialize the subdev */ + v4l2_subdev_init(sd, sd_ops); + sd->entity.function = function; + sd->entity.ops = &vimc_ent_sd_mops; + sd->owner = THIS_MODULE; + strlcpy(sd->name, name, sizeof(sd->name)); + v4l2_set_subdevdata(sd, ved); + + /* Expose this subdev to user space */ + sd->flags = V4L2_SUBDEV_FL_HAS_DEVNODE; + + /* Initialize the media entity */ + ret = media_entity_pads_init(&sd->entity, num_pads, ved->pads); + if (ret) + goto err_clean_pads; + + /* Register the subdev with the v4l2 and the media framework */ + ret = v4l2_device_register_subdev(v4l2_dev, sd); + if (ret) { + dev_err(v4l2_dev->dev, + "%s: subdev register failed (err=%d)\n", + name, ret); + goto err_clean_m_ent; + } + + return 0; + +err_clean_m_ent: + media_entity_cleanup(&sd->entity); +err_clean_pads: + vimc_pads_cleanup(ved->pads); + return ret; +} + +void vimc_ent_sd_unregister(struct vimc_ent_device *ved, struct v4l2_subdev *sd) +{ + v4l2_device_unregister_subdev(sd); + media_entity_cleanup(ved->ent); + vimc_pads_cleanup(ved->pads); +} diff --git a/drivers/media/platform/vimc/vimc-common.h b/drivers/media/platform/vimc/vimc-common.h index 00d3da4bfdbc..9ec361c8d7e4 100644 --- a/drivers/media/platform/vimc/vimc-common.h +++ b/drivers/media/platform/vimc/vimc-common.h @@ -110,4 +110,43 @@ const struct vimc_pix_map *vimc_pix_map_by_code(u32 code); */ const struct vimc_pix_map *vimc_pix_map_by_pixelformat(u32 pixelformat); +/** + * vimc_ent_sd_register - initialize and register a subdev node + * + * @ved: the vimc_ent_device struct to be initialize + * @sd: the v4l2_subdev struct to be initialize and registered + * @v4l2_dev: the v4l2 device to register the v4l2_subdev + * @name: name of the sub-device. Please notice that the name must be + * unique. + * @function: media entity function defined by MEDIA_ENT_F_* macros + * @num_pads: number of pads to initialize + * @pads_flag: flags to use in each pad + * @sd_ops: pointer to &struct v4l2_subdev_ops. + * @sd_destroy: callback to destroy the node + * + * Helper function initialize and register the struct vimc_ent_device and struct + * v4l2_subdev which represents a subdev node in the topology + */ +int vimc_ent_sd_register(struct vimc_ent_device *ved, + struct v4l2_subdev *sd, + struct v4l2_device *v4l2_dev, + const char *const name, + u32 function, + u16 num_pads, + const unsigned long *pads_flag, + const struct v4l2_subdev_ops *sd_ops, + void (*sd_destroy)(struct vimc_ent_device *)); + +/** + * vimc_ent_sd_register - initialize and register a subdev node + * + * @ved: the vimc_ent_device struct to be initialize + * @sd: the v4l2_subdev struct to be initialize and registered + * + * Helper function cleanup and unregister the struct vimc_ent_device and struct + * v4l2_subdev which represents a subdev node in the topology + */ +void vimc_ent_sd_unregister(struct vimc_ent_device *ved, + struct v4l2_subdev *sd); + #endif diff --git a/drivers/media/platform/vimc/vimc-sensor.c b/drivers/media/platform/vimc/vimc-sensor.c index 2e834878da0d..6386ac12856c 100644 --- a/drivers/media/platform/vimc/vimc-sensor.c +++ b/drivers/media/platform/vimc/vimc-sensor.c @@ -113,11 +113,6 @@ static const struct v4l2_subdev_pad_ops vimc_sen_pad_ops = { .set_fmt = vimc_sen_get_fmt, }; -/* media operations */ -static const struct media_entity_operations vimc_sen_mops = { - .link_validate = v4l2_subdev_link_validate, -}; - static int vimc_sen_tpg_thread(void *data) { struct vimc_sen_device *vsen = data; @@ -217,9 +212,8 @@ static void vimc_sen_destroy(struct vimc_ent_device *ved) struct vimc_sen_device *vsen = container_of(ved, struct vimc_sen_device, ved); + vimc_ent_sd_unregister(ved, &vsen->sd); tpg_free(&vsen->tpg); - v4l2_device_unregister_subdev(&vsen->sd); - media_entity_cleanup(ved->ent); kfree(vsen); } @@ -246,33 +240,12 @@ struct vimc_ent_device *vimc_sen_create(struct v4l2_device *v4l2_dev, if (!vsen) return ERR_PTR(-ENOMEM); - /* Allocate the pads */ - vsen->ved.pads = vimc_pads_init(num_pads, pads_flag); - if (IS_ERR(vsen->ved.pads)) { - ret = PTR_ERR(vsen->ved.pads); - goto err_free_vsen; - } - - /* Fill the vimc_ent_device struct */ - vsen->ved.destroy = vimc_sen_destroy; - vsen->ved.ent = &vsen->sd.entity; - - /* Initialize the subdev */ - v4l2_subdev_init(&vsen->sd, &vimc_sen_ops); - vsen->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; - vsen->sd.entity.ops = &vimc_sen_mops; - vsen->sd.owner = THIS_MODULE; - strlcpy(vsen->sd.name, name, sizeof(vsen->sd.name)); - v4l2_set_subdevdata(&vsen->sd, &vsen->ved); - - /* Expose this subdev to user space */ - vsen->sd.flags = V4L2_SUBDEV_FL_HAS_DEVNODE; - - /* Initialize the media entity */ - ret = media_entity_pads_init(&vsen->sd.entity, - num_pads, vsen->ved.pads); + /* Initialize ved and sd */ + ret = vimc_ent_sd_register(&vsen->ved, &vsen->sd, v4l2_dev, name, + MEDIA_ENT_F_CAM_SENSOR, num_pads, pads_flag, + &vimc_sen_ops, vimc_sen_destroy); if (ret) - goto err_clean_pads; + goto err_free_vsen; /* Set the active frame format (this is hardcoded for now) */ vsen->mbus_format.width = 640; @@ -288,25 +261,12 @@ struct vimc_ent_device *vimc_sen_create(struct v4l2_device *v4l2_dev, vsen->mbus_format.height); ret = tpg_alloc(&vsen->tpg, VIMC_SEN_FRAME_MAX_WIDTH); if (ret) - goto err_clean_m_ent; - - /* Register the subdev with the v4l2 and the media framework */ - ret = v4l2_device_register_subdev(v4l2_dev, &vsen->sd); - if (ret) { - dev_err(vsen->sd.v4l2_dev->dev, - "%s: subdev register failed (err=%d)\n", - vsen->sd.name, ret); - goto err_free_tpg; - } + goto err_unregister_ent_sd; return &vsen->ved; -err_free_tpg: - tpg_free(&vsen->tpg); -err_clean_m_ent: - media_entity_cleanup(&vsen->sd.entity); -err_clean_pads: - vimc_pads_cleanup(vsen->ved.pads); +err_unregister_ent_sd: + vimc_ent_sd_unregister(&vsen->ved, &vsen->sd); err_free_vsen: kfree(vsen); -- cgit v1.2.3 From bf5fb95c261de259e912e49973c66347a0a5b3d3 Mon Sep 17 00:00:00 2001 From: Helen Fornazier Date: Mon, 19 Jun 2017 14:00:13 -0300 Subject: [media] vimc: common: Add vimc_pipeline_s_stream helper Move the vimc_cap_pipeline_s_stream from the vimc-cap.c to vimc-common.c as this core will be reused by other subdevices to activate the stream in their directly connected nodes Signed-off-by: Helen Koike Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vimc/vimc-capture.c | 29 ++------------------------- drivers/media/platform/vimc/vimc-common.c | 32 ++++++++++++++++++++++++++++++ drivers/media/platform/vimc/vimc-common.h | 11 ++++++++++ 3 files changed, 45 insertions(+), 27 deletions(-) (limited to 'drivers/media/platform/vimc/vimc-common.c') diff --git a/drivers/media/platform/vimc/vimc-capture.c b/drivers/media/platform/vimc/vimc-capture.c index 9adb06d7e13d..93f6a0916329 100644 --- a/drivers/media/platform/vimc/vimc-capture.c +++ b/drivers/media/platform/vimc/vimc-capture.c @@ -132,31 +132,6 @@ static void vimc_cap_return_all_buffers(struct vimc_cap_device *vcap, spin_unlock(&vcap->qlock); } -static int vimc_cap_pipeline_s_stream(struct vimc_cap_device *vcap, int enable) -{ - struct v4l2_subdev *sd; - struct media_pad *pad; - int ret; - - /* Start the stream in the subdevice direct connected */ - pad = media_entity_remote_pad(&vcap->vdev.entity.pads[0]); - - /* - * if it is a raw node from vimc-core, there is nothing to activate - * TODO: remove this when there are no more raw nodes in the - * core and return error instead - */ - if (pad->entity->obj_type == MEDIA_ENTITY_TYPE_BASE) - return 0; - - sd = media_entity_to_v4l2_subdev(pad->entity); - ret = v4l2_subdev_call(sd, video, s_stream, enable); - if (ret && ret != -ENOIOCTLCMD) - return ret; - - return 0; -} - static int vimc_cap_start_streaming(struct vb2_queue *vq, unsigned int count) { struct vimc_cap_device *vcap = vb2_get_drv_priv(vq); @@ -173,7 +148,7 @@ static int vimc_cap_start_streaming(struct vb2_queue *vq, unsigned int count) } /* Enable streaming from the pipe */ - ret = vimc_cap_pipeline_s_stream(vcap, 1); + ret = vimc_pipeline_s_stream(&vcap->vdev.entity, 1); if (ret) { media_pipeline_stop(entity); vimc_cap_return_all_buffers(vcap, VB2_BUF_STATE_QUEUED); @@ -192,7 +167,7 @@ static void vimc_cap_stop_streaming(struct vb2_queue *vq) struct vimc_cap_device *vcap = vb2_get_drv_priv(vq); /* Disable streaming from the pipe */ - vimc_cap_pipeline_s_stream(vcap, 0); + vimc_pipeline_s_stream(&vcap->vdev.entity, 0); /* Stop the media pipeline */ media_pipeline_stop(&vcap->vdev.entity); diff --git a/drivers/media/platform/vimc/vimc-common.c b/drivers/media/platform/vimc/vimc-common.c index 3afbabd739a5..f809a9d756b9 100644 --- a/drivers/media/platform/vimc/vimc-common.c +++ b/drivers/media/platform/vimc/vimc-common.c @@ -220,6 +220,38 @@ struct media_pad *vimc_pads_init(u16 num_pads, const unsigned long *pads_flag) return pads; } +int vimc_pipeline_s_stream(struct media_entity *ent, int enable) +{ + struct v4l2_subdev *sd; + struct media_pad *pad; + unsigned int i; + int ret; + + for (i = 0; i < ent->num_pads; i++) { + if (ent->pads[i].flags & MEDIA_PAD_FL_SOURCE) + continue; + + /* Start the stream in the subdevice direct connected */ + pad = media_entity_remote_pad(&ent->pads[i]); + + /* + * if this is a raw node from vimc-core, then there is + * nothing to activate + * TODO: remove this when there are no more raw nodes in the + * core and return error instead + */ + if (pad->entity->obj_type == MEDIA_ENTITY_TYPE_BASE) + continue; + + sd = media_entity_to_v4l2_subdev(pad->entity); + ret = v4l2_subdev_call(sd, video, s_stream, enable); + if (ret && ret != -ENOIOCTLCMD) + return ret; + } + + return 0; +} + static const struct media_entity_operations vimc_ent_sd_mops = { .link_validate = v4l2_subdev_link_validate, }; diff --git a/drivers/media/platform/vimc/vimc-common.h b/drivers/media/platform/vimc/vimc-common.h index 9ec361c8d7e4..73e7e9479956 100644 --- a/drivers/media/platform/vimc/vimc-common.h +++ b/drivers/media/platform/vimc/vimc-common.h @@ -96,6 +96,17 @@ static inline void vimc_pads_cleanup(struct media_pad *pads) kfree(pads); } +/** + * vimc_pipeline_s_stream - start stream through the pipeline + * + * @ent: the pointer to struct media_entity for the node + * @enable: 1 to start the stream and 0 to stop + * + * Helper function to call the s_stream of the subdevices connected + * in all the sink pads of the entity + */ +int vimc_pipeline_s_stream(struct media_entity *ent, int enable); + /** * vimc_pix_map_by_code - get vimc_pix_map struct by media bus code * -- cgit v1.2.3 From 288a22d45c631f5be5e1c8b83977a78841083d3c Mon Sep 17 00:00:00 2001 From: Helen Fornazier Date: Mon, 19 Jun 2017 14:00:14 -0300 Subject: [media] vimc: common: Add vimc_link_validate All links will be checked in the same way. Adding a helper function for that Signed-off-by: Helen Koike Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vimc/vimc-capture.c | 78 +++---------------- drivers/media/platform/vimc/vimc-common.c | 121 ++++++++++++++++++++++++++++- drivers/media/platform/vimc/vimc-common.h | 14 ++++ 3 files changed, 145 insertions(+), 68 deletions(-) (limited to 'drivers/media/platform/vimc/vimc-common.c') diff --git a/drivers/media/platform/vimc/vimc-capture.c b/drivers/media/platform/vimc/vimc-capture.c index 93f6a0916329..5bdecd103d55 100644 --- a/drivers/media/platform/vimc/vimc-capture.c +++ b/drivers/media/platform/vimc/vimc-capture.c @@ -64,6 +64,15 @@ static int vimc_cap_querycap(struct file *file, void *priv, return 0; } +static void vimc_cap_get_format(struct vimc_ent_device *ved, + struct v4l2_pix_format *fmt) +{ + struct vimc_cap_device *vcap = container_of(ved, struct vimc_cap_device, + ved); + + *fmt = vcap->format; +} + static int vimc_cap_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { @@ -231,74 +240,8 @@ static const struct vb2_ops vimc_cap_qops = { .wait_finish = vb2_ops_wait_finish, }; -/* - * NOTE: this function is a copy of v4l2_subdev_link_validate_get_format - * maybe the v4l2 function should be public - */ -static int vimc_cap_v4l2_subdev_link_validate_get_format(struct media_pad *pad, - struct v4l2_subdev_format *fmt) -{ - struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(pad->entity); - - fmt->which = V4L2_SUBDEV_FORMAT_ACTIVE; - fmt->pad = pad->index; - - return v4l2_subdev_call(sd, pad, get_fmt, NULL, fmt); -} - -static int vimc_cap_link_validate(struct media_link *link) -{ - struct v4l2_subdev_format source_fmt; - const struct vimc_pix_map *vpix; - struct vimc_cap_device *vcap = container_of(link->sink->entity, - struct vimc_cap_device, - vdev.entity); - struct v4l2_pix_format *sink_fmt = &vcap->format; - int ret; - - /* - * if it is a raw node from vimc-core, ignore the link for now - * TODO: remove this when there are no more raw nodes in the - * core and return error instead - */ - if (link->source->entity->obj_type == MEDIA_ENTITY_TYPE_BASE) - return 0; - - /* Get the the format of the subdev */ - ret = vimc_cap_v4l2_subdev_link_validate_get_format(link->source, - &source_fmt); - if (ret) - return ret; - - dev_dbg(vcap->vdev.v4l2_dev->dev, - "%s: link validate formats src:%dx%d %d sink:%dx%d %d\n", - vcap->vdev.name, - source_fmt.format.width, source_fmt.format.height, - source_fmt.format.code, - sink_fmt->width, sink_fmt->height, - sink_fmt->pixelformat); - - /* The width, height and code must match. */ - vpix = vimc_pix_map_by_pixelformat(sink_fmt->pixelformat); - if (source_fmt.format.width != sink_fmt->width - || source_fmt.format.height != sink_fmt->height - || vpix->code != source_fmt.format.code) - return -EPIPE; - - /* - * The field order must match, or the sink field order must be NONE - * to support interlaced hardware connected to bridges that support - * progressive formats only. - */ - if (source_fmt.format.field != sink_fmt->field && - sink_fmt->field != V4L2_FIELD_NONE) - return -EPIPE; - - return 0; -} - static const struct media_entity_operations vimc_cap_mops = { - .link_validate = vimc_cap_link_validate, + .link_validate = vimc_link_validate, }; static void vimc_cap_destroy(struct vimc_ent_device *ved) @@ -434,6 +377,7 @@ struct vimc_ent_device *vimc_cap_create(struct v4l2_device *v4l2_dev, vcap->ved.destroy = vimc_cap_destroy; vcap->ved.ent = &vcap->vdev.entity; vcap->ved.process_frame = vimc_cap_process_frame; + vcap->ved.vdev_get_format = vimc_cap_get_format; /* Initialize the video_device struct */ vdev = &vcap->vdev; diff --git a/drivers/media/platform/vimc/vimc-common.c b/drivers/media/platform/vimc/vimc-common.c index f809a9d756b9..6ad77fdc04ad 100644 --- a/drivers/media/platform/vimc/vimc-common.c +++ b/drivers/media/platform/vimc/vimc-common.c @@ -252,8 +252,127 @@ int vimc_pipeline_s_stream(struct media_entity *ent, int enable) return 0; } +static int vimc_get_mbus_format(struct media_pad *pad, + struct v4l2_subdev_format *fmt) +{ + if (is_media_entity_v4l2_subdev(pad->entity)) { + struct v4l2_subdev *sd = + media_entity_to_v4l2_subdev(pad->entity); + int ret; + + fmt->which = V4L2_SUBDEV_FORMAT_ACTIVE; + fmt->pad = pad->index; + + ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, fmt); + if (ret) + return ret; + + } else if (is_media_entity_v4l2_video_device(pad->entity)) { + struct video_device *vdev = container_of(pad->entity, + struct video_device, + entity); + struct vimc_ent_device *ved = video_get_drvdata(vdev); + const struct vimc_pix_map *vpix; + struct v4l2_pix_format vdev_fmt; + + if (!ved->vdev_get_format) + return -ENOIOCTLCMD; + + ved->vdev_get_format(ved, &vdev_fmt); + vpix = vimc_pix_map_by_pixelformat(vdev_fmt.pixelformat); + v4l2_fill_mbus_format(&fmt->format, &vdev_fmt, vpix->code); + } else { + return -EINVAL; + } + + return 0; +} + +int vimc_link_validate(struct media_link *link) +{ + struct v4l2_subdev_format source_fmt, sink_fmt; + int ret; + + /* + * if it is a raw node from vimc-core, ignore the link for now + * TODO: remove this when there are no more raw nodes in the + * core and return error instead + */ + if (link->source->entity->obj_type == MEDIA_ENTITY_TYPE_BASE) + return 0; + + ret = vimc_get_mbus_format(link->source, &source_fmt); + if (ret) + return ret; + + ret = vimc_get_mbus_format(link->sink, &sink_fmt); + if (ret) + return ret; + + pr_info("vimc link validate: " + "%s:src:%dx%d (0x%x, %d, %d, %d, %d) " + "%s:snk:%dx%d (0x%x, %d, %d, %d, %d)\n", + /* src */ + link->source->entity->name, + source_fmt.format.width, source_fmt.format.height, + source_fmt.format.code, source_fmt.format.colorspace, + source_fmt.format.quantization, source_fmt.format.xfer_func, + source_fmt.format.ycbcr_enc, + /* sink */ + link->sink->entity->name, + sink_fmt.format.width, sink_fmt.format.height, + sink_fmt.format.code, sink_fmt.format.colorspace, + sink_fmt.format.quantization, sink_fmt.format.xfer_func, + sink_fmt.format.ycbcr_enc); + + /* The width, height and code must match. */ + if (source_fmt.format.width != sink_fmt.format.width + || source_fmt.format.height != sink_fmt.format.height + || source_fmt.format.code != sink_fmt.format.code) + return -EPIPE; + + /* + * The field order must match, or the sink field order must be NONE + * to support interlaced hardware connected to bridges that support + * progressive formats only. + */ + if (source_fmt.format.field != sink_fmt.format.field && + sink_fmt.format.field != V4L2_FIELD_NONE) + return -EPIPE; + + /* + * If colorspace is DEFAULT, then assume all the colorimetry is also + * DEFAULT, return 0 to skip comparing the other colorimetry parameters + */ + if (source_fmt.format.colorspace == V4L2_COLORSPACE_DEFAULT + || sink_fmt.format.colorspace == V4L2_COLORSPACE_DEFAULT) + return 0; + + /* Colorspace must match. */ + if (source_fmt.format.colorspace != sink_fmt.format.colorspace) + return -EPIPE; + + /* Colorimetry must match if they are not set to DEFAULT */ + if (source_fmt.format.ycbcr_enc != V4L2_YCBCR_ENC_DEFAULT + && sink_fmt.format.ycbcr_enc != V4L2_YCBCR_ENC_DEFAULT + && source_fmt.format.ycbcr_enc != sink_fmt.format.ycbcr_enc) + return -EPIPE; + + if (source_fmt.format.quantization != V4L2_QUANTIZATION_DEFAULT + && sink_fmt.format.quantization != V4L2_QUANTIZATION_DEFAULT + && source_fmt.format.quantization != sink_fmt.format.quantization) + return -EPIPE; + + if (source_fmt.format.xfer_func != V4L2_XFER_FUNC_DEFAULT + && sink_fmt.format.xfer_func != V4L2_XFER_FUNC_DEFAULT + && source_fmt.format.xfer_func != sink_fmt.format.xfer_func) + return -EPIPE; + + return 0; +} + static const struct media_entity_operations vimc_ent_sd_mops = { - .link_validate = v4l2_subdev_link_validate, + .link_validate = vimc_link_validate, }; int vimc_ent_sd_register(struct vimc_ent_device *ved, diff --git a/drivers/media/platform/vimc/vimc-common.h b/drivers/media/platform/vimc/vimc-common.h index 73e7e9479956..60ebde28f56b 100644 --- a/drivers/media/platform/vimc/vimc-common.h +++ b/drivers/media/platform/vimc/vimc-common.h @@ -45,6 +45,9 @@ struct vimc_pix_map { * @pads: the list of pads of the node * @destroy: callback to destroy the node * @process_frame: callback send a frame to that node + * @vdev_get_format: callback that returns the current format a pad, used + * only when is_media_entity_v4l2_video_device(ent) returns + * true * * Each node of the topology must create a vimc_ent_device struct. Depending on * the node it will be of an instance of v4l2_subdev or video_device struct @@ -60,6 +63,8 @@ struct vimc_ent_device { void (*destroy)(struct vimc_ent_device *); void (*process_frame)(struct vimc_ent_device *ved, struct media_pad *sink, const void *frame); + void (*vdev_get_format)(struct vimc_ent_device *ved, + struct v4l2_pix_format *fmt); }; /** @@ -160,4 +165,13 @@ int vimc_ent_sd_register(struct vimc_ent_device *ved, void vimc_ent_sd_unregister(struct vimc_ent_device *ved, struct v4l2_subdev *sd); +/** + * vimc_link_validate - validates a media link + * + * @link: pointer to &struct media_link + * + * This function calls validates if a media link is valid for streaming. + */ +int vimc_link_validate(struct media_link *link); + #endif -- cgit v1.2.3 From 88ad71aab1a7931ac3d35c3acaa431c3dc05afd9 Mon Sep 17 00:00:00 2001 From: Helen Fornazier Date: Mon, 19 Jun 2017 14:00:16 -0300 Subject: [media] vimc: sen: Support several image formats Allow user space to change the image format as the frame size, the media bus pixel format, colorspace, quantization, field YCbCr encoding and the transfer function Signed-off-by: Helen Koike Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vimc/vimc-common.c | 8 ++ drivers/media/platform/vimc/vimc-common.h | 12 +++ drivers/media/platform/vimc/vimc-sensor.c | 130 +++++++++++++++++++++++------- 3 files changed, 121 insertions(+), 29 deletions(-) (limited to 'drivers/media/platform/vimc/vimc-common.c') diff --git a/drivers/media/platform/vimc/vimc-common.c b/drivers/media/platform/vimc/vimc-common.c index 6ad77fdc04ad..b69805574c67 100644 --- a/drivers/media/platform/vimc/vimc-common.c +++ b/drivers/media/platform/vimc/vimc-common.c @@ -144,6 +144,14 @@ static const struct vimc_pix_map vimc_pix_map_list[] = { }, }; +const struct vimc_pix_map *vimc_pix_map_by_index(unsigned int i) +{ + if (i >= ARRAY_SIZE(vimc_pix_map_list)) + return NULL; + + return &vimc_pix_map_list[i]; +} + const struct vimc_pix_map *vimc_pix_map_by_code(u32 code) { unsigned int i; diff --git a/drivers/media/platform/vimc/vimc-common.h b/drivers/media/platform/vimc/vimc-common.h index 43483eecb116..fb3463c06185 100644 --- a/drivers/media/platform/vimc/vimc-common.h +++ b/drivers/media/platform/vimc/vimc-common.h @@ -22,6 +22,11 @@ #include #include +#define VIMC_FRAME_MAX_WIDTH 4096 +#define VIMC_FRAME_MAX_HEIGHT 2160 +#define VIMC_FRAME_MIN_WIDTH 16 +#define VIMC_FRAME_MIN_HEIGHT 16 + /** * struct vimc_colorimetry_clamp - Adjust colorimetry parameters * @@ -138,6 +143,13 @@ static inline void vimc_pads_cleanup(struct media_pad *pads) */ int vimc_pipeline_s_stream(struct media_entity *ent, int enable); +/** + * vimc_pix_map_by_index - get vimc_pix_map struct by its index + * + * @i: index of the vimc_pix_map struct in vimc_pix_map_list + */ +const struct vimc_pix_map *vimc_pix_map_by_index(unsigned int i); + /** * vimc_pix_map_by_code - get vimc_pix_map struct by media bus code * diff --git a/drivers/media/platform/vimc/vimc-sensor.c b/drivers/media/platform/vimc/vimc-sensor.c index 6386ac12856c..d4f97050b544 100644 --- a/drivers/media/platform/vimc/vimc-sensor.c +++ b/drivers/media/platform/vimc/vimc-sensor.c @@ -24,8 +24,6 @@ #include "vimc-sensor.h" -#define VIMC_SEN_FRAME_MAX_WIDTH 4096 - struct vimc_sen_device { struct vimc_ent_device ved; struct v4l2_subdev sd; @@ -36,18 +34,39 @@ struct vimc_sen_device { struct v4l2_mbus_framefmt mbus_format; }; +static const struct v4l2_mbus_framefmt fmt_default = { + .width = 640, + .height = 480, + .code = MEDIA_BUS_FMT_RGB888_1X24, + .field = V4L2_FIELD_NONE, + .colorspace = V4L2_COLORSPACE_DEFAULT, +}; + +static int vimc_sen_init_cfg(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg) +{ + unsigned int i; + + for (i = 0; i < sd->entity.num_pads; i++) { + struct v4l2_mbus_framefmt *mf; + + mf = v4l2_subdev_get_try_format(sd, cfg, i); + *mf = fmt_default; + } + + return 0; +} + static int vimc_sen_enum_mbus_code(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_mbus_code_enum *code) { - struct vimc_sen_device *vsen = - container_of(sd, struct vimc_sen_device, sd); + const struct vimc_pix_map *vpix = vimc_pix_map_by_index(code->index); - /* TODO: Add support for other codes */ - if (code->index) + if (!vpix) return -EINVAL; - code->code = vsen->mbus_format.code; + code->code = vpix->code; return 0; } @@ -56,33 +75,34 @@ static int vimc_sen_enum_frame_size(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_frame_size_enum *fse) { - struct vimc_sen_device *vsen = - container_of(sd, struct vimc_sen_device, sd); + const struct vimc_pix_map *vpix; - /* TODO: Add support to other formats */ if (fse->index) return -EINVAL; - /* TODO: Add support for other codes */ - if (fse->code != vsen->mbus_format.code) + /* Only accept code in the pix map table */ + vpix = vimc_pix_map_by_code(fse->code); + if (!vpix) return -EINVAL; - fse->min_width = vsen->mbus_format.width; - fse->max_width = vsen->mbus_format.width; - fse->min_height = vsen->mbus_format.height; - fse->max_height = vsen->mbus_format.height; + fse->min_width = VIMC_FRAME_MIN_WIDTH; + fse->max_width = VIMC_FRAME_MAX_WIDTH; + fse->min_height = VIMC_FRAME_MIN_HEIGHT; + fse->max_height = VIMC_FRAME_MAX_HEIGHT; return 0; } static int vimc_sen_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_format *format) + struct v4l2_subdev_format *fmt) { struct vimc_sen_device *vsen = container_of(sd, struct vimc_sen_device, sd); - format->format = vsen->mbus_format; + fmt->format = fmt->which == V4L2_SUBDEV_FORMAT_TRY ? + *v4l2_subdev_get_try_format(sd, cfg, fmt->pad) : + vsen->mbus_format; return 0; } @@ -105,12 +125,70 @@ static void vimc_sen_tpg_s_format(struct vimc_sen_device *vsen) tpg_s_xfer_func(&vsen->tpg, vsen->mbus_format.xfer_func); } +static void vimc_sen_adjust_fmt(struct v4l2_mbus_framefmt *fmt) +{ + const struct vimc_pix_map *vpix; + + /* Only accept code in the pix map table */ + vpix = vimc_pix_map_by_code(fmt->code); + if (!vpix) + fmt->code = fmt_default.code; + + fmt->width = clamp_t(u32, fmt->width, VIMC_FRAME_MIN_WIDTH, + VIMC_FRAME_MAX_WIDTH) & ~1; + fmt->height = clamp_t(u32, fmt->height, VIMC_FRAME_MIN_HEIGHT, + VIMC_FRAME_MAX_HEIGHT) & ~1; + + /* TODO: add support for V4L2_FIELD_ALTERNATE */ + if (fmt->field == V4L2_FIELD_ANY || fmt->field == V4L2_FIELD_ALTERNATE) + fmt->field = fmt_default.field; + + vimc_colorimetry_clamp(fmt); +} + +static int vimc_sen_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct vimc_sen_device *vsen = v4l2_get_subdevdata(sd); + struct v4l2_mbus_framefmt *mf; + + if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) { + /* Do not change the format while stream is on */ + if (vsen->frame) + return -EBUSY; + + mf = &vsen->mbus_format; + } else { + mf = v4l2_subdev_get_try_format(sd, cfg, fmt->pad); + } + + /* Set the new format */ + vimc_sen_adjust_fmt(&fmt->format); + + dev_dbg(vsen->sd.v4l2_dev->mdev->dev, "%s: format update: " + "old:%dx%d (0x%x, %d, %d, %d, %d) " + "new:%dx%d (0x%x, %d, %d, %d, %d)\n", vsen->sd.name, + /* old */ + mf->width, mf->height, mf->code, + mf->colorspace, mf->quantization, + mf->xfer_func, mf->ycbcr_enc, + /* new */ + fmt->format.width, fmt->format.height, fmt->format.code, + fmt->format.colorspace, fmt->format.quantization, + fmt->format.xfer_func, fmt->format.ycbcr_enc); + + *mf = fmt->format; + + return 0; +} + static const struct v4l2_subdev_pad_ops vimc_sen_pad_ops = { + .init_cfg = vimc_sen_init_cfg, .enum_mbus_code = vimc_sen_enum_mbus_code, .enum_frame_size = vimc_sen_enum_frame_size, .get_fmt = vimc_sen_get_fmt, - /* TODO: Add support to other formats */ - .set_fmt = vimc_sen_get_fmt, + .set_fmt = vimc_sen_set_fmt, }; static int vimc_sen_tpg_thread(void *data) @@ -247,19 +325,13 @@ struct vimc_ent_device *vimc_sen_create(struct v4l2_device *v4l2_dev, if (ret) goto err_free_vsen; - /* Set the active frame format (this is hardcoded for now) */ - vsen->mbus_format.width = 640; - vsen->mbus_format.height = 480; - vsen->mbus_format.code = MEDIA_BUS_FMT_RGB888_1X24; - vsen->mbus_format.field = V4L2_FIELD_NONE; - vsen->mbus_format.colorspace = V4L2_COLORSPACE_SRGB; - vsen->mbus_format.quantization = V4L2_QUANTIZATION_FULL_RANGE; - vsen->mbus_format.xfer_func = V4L2_XFER_FUNC_SRGB; + /* Initialize the frame format */ + vsen->mbus_format = fmt_default; /* Initialize the test pattern generator */ tpg_init(&vsen->tpg, vsen->mbus_format.width, vsen->mbus_format.height); - ret = tpg_alloc(&vsen->tpg, VIMC_SEN_FRAME_MAX_WIDTH); + ret = tpg_alloc(&vsen->tpg, VIMC_FRAME_MAX_WIDTH); if (ret) goto err_unregister_ent_sd; -- cgit v1.2.3 From 4a29b70907496aa9da79112ec31cf9cf2b972c3f Mon Sep 17 00:00:00 2001 From: Helen Fornazier Date: Mon, 19 Jun 2017 14:00:18 -0300 Subject: [media] vimc: Subdevices as modules Change the core structure for adding subdevices in the topology. Instead of calling the specific create function for each subdevice, inject a child platform_device with the driver's name. Each type of node in the topology (sensor, capture, debayer, scaler) will register a platform_driver with the corresponding name through the component subsystem. Implementing a new subdevice type doesn't require vimc-core to be altered. This facilitates future implementation of dynamic entities, where hotpluging an entity in the topology is just a matter of registering/unregistering a platform_device in the system. It also facilitates other implementations of different nodes without touching the core code and remove the need of a header file for each type of node. Signed-off-by: Helen Koike Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vimc/Makefile | 7 +- drivers/media/platform/vimc/vimc-capture.c | 99 ++++--- drivers/media/platform/vimc/vimc-capture.h | 28 -- drivers/media/platform/vimc/vimc-common.c | 38 +-- drivers/media/platform/vimc/vimc-common.h | 29 ++- drivers/media/platform/vimc/vimc-core.c | 405 +++++++++++------------------ drivers/media/platform/vimc/vimc-sensor.c | 93 +++++-- drivers/media/platform/vimc/vimc-sensor.h | 28 -- 8 files changed, 339 insertions(+), 388 deletions(-) delete mode 100644 drivers/media/platform/vimc/vimc-capture.h delete mode 100644 drivers/media/platform/vimc/vimc-sensor.h (limited to 'drivers/media/platform/vimc/vimc-common.c') diff --git a/drivers/media/platform/vimc/Makefile b/drivers/media/platform/vimc/Makefile index 6b6ddf45e3bf..0e5d5ce37a18 100644 --- a/drivers/media/platform/vimc/Makefile +++ b/drivers/media/platform/vimc/Makefile @@ -1,3 +1,6 @@ -vimc-objs := vimc-core.o vimc-capture.o vimc-common.o vimc-sensor.o +vimc-objs := vimc-core.o +vimc_capture-objs := vimc-capture.o +vimc_common-objs := vimc-common.o +vimc_sensor-objs := vimc-sensor.o -obj-$(CONFIG_VIDEO_VIMC) += vimc.o +obj-$(CONFIG_VIDEO_VIMC) += vimc.o vimc_capture.o vimc_common.o vimc_sensor.o diff --git a/drivers/media/platform/vimc/vimc-capture.c b/drivers/media/platform/vimc/vimc-capture.c index 359f59efb44e..14cb32e21130 100644 --- a/drivers/media/platform/vimc/vimc-capture.c +++ b/drivers/media/platform/vimc/vimc-capture.c @@ -15,15 +15,21 @@ * */ +#include +#include +#include #include #include #include -#include "vimc-capture.h" +#include "vimc-common.h" + +#define VIMC_CAP_DRV_NAME "vimc-capture" struct vimc_cap_device { struct vimc_ent_device ved; struct video_device vdev; + struct device *dev; struct v4l2_pix_format format; struct vb2_queue queue; struct list_head buf_list; @@ -131,7 +137,7 @@ static int vimc_cap_s_fmt_vid_cap(struct file *file, void *priv, vimc_cap_try_fmt_vid_cap(file, priv, f); - dev_dbg(vcap->vdev.v4l2_dev->dev, "%s: format update: " + dev_dbg(vcap->dev, "%s: format update: " "old:%dx%d (0x%x, %d, %d, %d, %d) " "new:%dx%d (0x%x, %d, %d, %d, %d)\n", vcap->vdev.name, /* old */ @@ -309,8 +315,7 @@ static int vimc_cap_buffer_prepare(struct vb2_buffer *vb) unsigned long size = vcap->format.sizeimage; if (vb2_plane_size(vb, 0) < size) { - dev_err(vcap->vdev.v4l2_dev->dev, - "%s: buffer too small (%lu < %lu)\n", + dev_err(vcap->dev, "%s: buffer too small (%lu < %lu)\n", vcap->vdev.name, vb2_plane_size(vb, 0), size); return -EINVAL; } @@ -335,8 +340,10 @@ static const struct media_entity_operations vimc_cap_mops = { .link_validate = vimc_link_validate, }; -static void vimc_cap_destroy(struct vimc_ent_device *ved) +static void vimc_cap_comp_unbind(struct device *comp, struct device *master, + void *master_data) { + struct vimc_ent_device *ved = dev_get_drvdata(comp); struct vimc_cap_device *vcap = container_of(ved, struct vimc_cap_device, ved); @@ -385,42 +392,35 @@ static void vimc_cap_process_frame(struct vimc_ent_device *ved, vb2_buffer_done(&vimc_buf->vb2.vb2_buf, VB2_BUF_STATE_DONE); } -struct vimc_ent_device *vimc_cap_create(struct v4l2_device *v4l2_dev, - const char *const name, - u16 num_pads, - const unsigned long *pads_flag) +static int vimc_cap_comp_bind(struct device *comp, struct device *master, + void *master_data) { + struct v4l2_device *v4l2_dev = master_data; + struct vimc_platform_data *pdata = comp->platform_data; const struct vimc_pix_map *vpix; struct vimc_cap_device *vcap; struct video_device *vdev; struct vb2_queue *q; int ret; - /* - * Check entity configuration params - * NOTE: we only support a single sink pad - */ - if (!name || num_pads != 1 || !pads_flag || - !(pads_flag[0] & MEDIA_PAD_FL_SINK)) - return ERR_PTR(-EINVAL); - /* Allocate the vimc_cap_device struct */ vcap = kzalloc(sizeof(*vcap), GFP_KERNEL); if (!vcap) - return ERR_PTR(-ENOMEM); + return -ENOMEM; /* Allocate the pads */ - vcap->ved.pads = vimc_pads_init(num_pads, pads_flag); + vcap->ved.pads = + vimc_pads_init(1, (const unsigned long[1]) {MEDIA_PAD_FL_SINK}); if (IS_ERR(vcap->ved.pads)) { ret = PTR_ERR(vcap->ved.pads); goto err_free_vcap; } /* Initialize the media entity */ - vcap->vdev.entity.name = name; + vcap->vdev.entity.name = pdata->entity_name; vcap->vdev.entity.function = MEDIA_ENT_F_IO_V4L; ret = media_entity_pads_init(&vcap->vdev.entity, - num_pads, vcap->ved.pads); + 1, vcap->ved.pads); if (ret) goto err_clean_pads; @@ -441,9 +441,8 @@ struct vimc_ent_device *vimc_cap_create(struct v4l2_device *v4l2_dev, ret = vb2_queue_init(q); if (ret) { - dev_err(vcap->vdev.v4l2_dev->dev, - "%s: vb2 queue init failed (err=%d)\n", - vcap->vdev.name, ret); + dev_err(comp, "%s: vb2 queue init failed (err=%d)\n", + pdata->entity_name, ret); goto err_clean_m_ent; } @@ -459,10 +458,11 @@ struct vimc_ent_device *vimc_cap_create(struct v4l2_device *v4l2_dev, vcap->format.height; /* Fill the vimc_ent_device struct */ - vcap->ved.destroy = vimc_cap_destroy; vcap->ved.ent = &vcap->vdev.entity; vcap->ved.process_frame = vimc_cap_process_frame; vcap->ved.vdev_get_format = vimc_cap_get_format; + dev_set_drvdata(comp, &vcap->ved); + vcap->dev = comp; /* Initialize the video_device struct */ vdev = &vcap->vdev; @@ -475,19 +475,18 @@ struct vimc_ent_device *vimc_cap_create(struct v4l2_device *v4l2_dev, vdev->queue = q; vdev->v4l2_dev = v4l2_dev; vdev->vfl_dir = VFL_DIR_RX; - strlcpy(vdev->name, name, sizeof(vdev->name)); + strlcpy(vdev->name, pdata->entity_name, sizeof(vdev->name)); video_set_drvdata(vdev, &vcap->ved); /* Register the video_device with the v4l2 and the media framework */ ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1); if (ret) { - dev_err(vcap->vdev.v4l2_dev->dev, - "%s: video register failed (err=%d)\n", + dev_err(comp, "%s: video register failed (err=%d)\n", vcap->vdev.name, ret); goto err_release_queue; } - return &vcap->ved; + return 0; err_release_queue: vb2_queue_release(q); @@ -498,5 +497,45 @@ err_clean_pads: err_free_vcap: kfree(vcap); - return ERR_PTR(ret); + return ret; } + +static const struct component_ops vimc_cap_comp_ops = { + .bind = vimc_cap_comp_bind, + .unbind = vimc_cap_comp_unbind, +}; + +static int vimc_cap_probe(struct platform_device *pdev) +{ + return component_add(&pdev->dev, &vimc_cap_comp_ops); +} + +static int vimc_cap_remove(struct platform_device *pdev) +{ + component_del(&pdev->dev, &vimc_cap_comp_ops); + + return 0; +} + +static struct platform_driver vimc_cap_pdrv = { + .probe = vimc_cap_probe, + .remove = vimc_cap_remove, + .driver = { + .name = VIMC_CAP_DRV_NAME, + }, +}; + +static const struct platform_device_id vimc_cap_driver_ids[] = { + { + .name = VIMC_CAP_DRV_NAME, + }, + { } +}; + +module_platform_driver(vimc_cap_pdrv); + +MODULE_DEVICE_TABLE(platform, vimc_cap_driver_ids); + +MODULE_DESCRIPTION("Virtual Media Controller Driver (VIMC) Capture"); +MODULE_AUTHOR("Helen Mae Koike Fornazier "); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/platform/vimc/vimc-capture.h b/drivers/media/platform/vimc/vimc-capture.h deleted file mode 100644 index 7e5c7073bcc1..000000000000 --- a/drivers/media/platform/vimc/vimc-capture.h +++ /dev/null @@ -1,28 +0,0 @@ -/* - * vimc-capture.h Virtual Media Controller Driver - * - * Copyright (C) 2015-2017 Helen Koike - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - */ - -#ifndef _VIMC_CAPTURE_H_ -#define _VIMC_CAPTURE_H_ - -#include "vimc-common.h" - -struct vimc_ent_device *vimc_cap_create(struct v4l2_device *v4l2_dev, - const char *const name, - u16 num_pads, - const unsigned long *pads_flag); - -#endif diff --git a/drivers/media/platform/vimc/vimc-common.c b/drivers/media/platform/vimc/vimc-common.c index b69805574c67..da7f2b7a2e58 100644 --- a/drivers/media/platform/vimc/vimc-common.c +++ b/drivers/media/platform/vimc/vimc-common.c @@ -15,6 +15,9 @@ * */ +#include +#include + #include "vimc-common.h" static const struct vimc_pix_map vimc_pix_map_list[] = { @@ -151,6 +154,7 @@ const struct vimc_pix_map *vimc_pix_map_by_index(unsigned int i) return &vimc_pix_map_list[i]; } +EXPORT_SYMBOL_GPL(vimc_pix_map_by_index); const struct vimc_pix_map *vimc_pix_map_by_code(u32 code) { @@ -162,6 +166,7 @@ const struct vimc_pix_map *vimc_pix_map_by_code(u32 code) } return NULL; } +EXPORT_SYMBOL_GPL(vimc_pix_map_by_code); const struct vimc_pix_map *vimc_pix_map_by_pixelformat(u32 pixelformat) { @@ -173,6 +178,7 @@ const struct vimc_pix_map *vimc_pix_map_by_pixelformat(u32 pixelformat) } return NULL; } +EXPORT_SYMBOL_GPL(vimc_pix_map_by_pixelformat); int vimc_propagate_frame(struct media_pad *src, const void *frame) { @@ -207,6 +213,7 @@ int vimc_propagate_frame(struct media_pad *src, const void *frame) return 0; } +EXPORT_SYMBOL_GPL(vimc_propagate_frame); /* Helper function to allocate and initialize pads */ struct media_pad *vimc_pads_init(u16 num_pads, const unsigned long *pads_flag) @@ -227,6 +234,7 @@ struct media_pad *vimc_pads_init(u16 num_pads, const unsigned long *pads_flag) return pads; } +EXPORT_SYMBOL_GPL(vimc_pads_init); int vimc_pipeline_s_stream(struct media_entity *ent, int enable) { @@ -242,14 +250,8 @@ int vimc_pipeline_s_stream(struct media_entity *ent, int enable) /* Start the stream in the subdevice direct connected */ pad = media_entity_remote_pad(&ent->pads[i]); - /* - * if this is a raw node from vimc-core, then there is - * nothing to activate - * TODO: remove this when there are no more raw nodes in the - * core and return error instead - */ - if (pad->entity->obj_type == MEDIA_ENTITY_TYPE_BASE) - continue; + if (!is_media_entity_v4l2_subdev(pad->entity)) + return -EINVAL; sd = media_entity_to_v4l2_subdev(pad->entity); ret = v4l2_subdev_call(sd, video, s_stream, enable); @@ -259,6 +261,7 @@ int vimc_pipeline_s_stream(struct media_entity *ent, int enable) return 0; } +EXPORT_SYMBOL_GPL(vimc_pipeline_s_stream); static int vimc_get_mbus_format(struct media_pad *pad, struct v4l2_subdev_format *fmt) @@ -301,14 +304,6 @@ int vimc_link_validate(struct media_link *link) struct v4l2_subdev_format source_fmt, sink_fmt; int ret; - /* - * if it is a raw node from vimc-core, ignore the link for now - * TODO: remove this when there are no more raw nodes in the - * core and return error instead - */ - if (link->source->entity->obj_type == MEDIA_ENTITY_TYPE_BASE) - return 0; - ret = vimc_get_mbus_format(link->source, &source_fmt); if (ret) return ret; @@ -378,6 +373,7 @@ int vimc_link_validate(struct media_link *link) return 0; } +EXPORT_SYMBOL_GPL(vimc_link_validate); static const struct media_entity_operations vimc_ent_sd_mops = { .link_validate = vimc_link_validate, @@ -390,8 +386,7 @@ int vimc_ent_sd_register(struct vimc_ent_device *ved, u32 function, u16 num_pads, const unsigned long *pads_flag, - const struct v4l2_subdev_ops *sd_ops, - void (*sd_destroy)(struct vimc_ent_device *)) + const struct v4l2_subdev_ops *sd_ops) { int ret; @@ -401,7 +396,6 @@ int vimc_ent_sd_register(struct vimc_ent_device *ved, return PTR_ERR(ved->pads); /* Fill the vimc_ent_device struct */ - ved->destroy = sd_destroy; ved->ent = &sd->entity; /* Initialize the subdev */ @@ -437,6 +431,7 @@ err_clean_pads: vimc_pads_cleanup(ved->pads); return ret; } +EXPORT_SYMBOL_GPL(vimc_ent_sd_register); void vimc_ent_sd_unregister(struct vimc_ent_device *ved, struct v4l2_subdev *sd) { @@ -444,3 +439,8 @@ void vimc_ent_sd_unregister(struct vimc_ent_device *ved, struct v4l2_subdev *sd) media_entity_cleanup(ved->ent); vimc_pads_cleanup(ved->pads); } +EXPORT_SYMBOL_GPL(vimc_ent_sd_unregister); + +MODULE_DESCRIPTION("Virtual Media Controller Driver (VIMC) Common"); +MODULE_AUTHOR("Helen Koike "); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/platform/vimc/vimc-common.h b/drivers/media/platform/vimc/vimc-common.h index fb3463c06185..a9c1cfdc0dff 100644 --- a/drivers/media/platform/vimc/vimc-common.h +++ b/drivers/media/platform/vimc/vimc-common.h @@ -1,5 +1,5 @@ /* - * vimc-ccommon.h Virtual Media Controller Driver + * vimc-common.h Virtual Media Controller Driver * * Copyright (C) 2015-2017 Helen Koike * @@ -53,6 +53,21 @@ do { \ (fmt)->xfer_func = V4L2_XFER_FUNC_DEFAULT; \ } while (0) +/** + * struct vimc_platform_data - platform data to components + * + * @entity_name: The name of the entity to be created + * + * Board setup code will often provide additional information using the device's + * platform_data field to hold additional information. + * When injecting a new platform_device in the component system the core needs + * to provide to the corresponding submodules the name of the entity that should + * be used when registering the subdevice in the Media Controller system. + */ +struct vimc_platform_data { + char entity_name[32]; +}; + /** * struct vimc_pix_map - maps media bus code with v4l2 pixel format * @@ -74,7 +89,6 @@ struct vimc_pix_map { * * @ent: the pointer to struct media_entity for the node * @pads: the list of pads of the node - * @destroy: callback to destroy the node * @process_frame: callback send a frame to that node * @vdev_get_format: callback that returns the current format a pad, used * only when is_media_entity_v4l2_video_device(ent) returns @@ -91,7 +105,6 @@ struct vimc_pix_map { struct vimc_ent_device { struct media_entity *ent; struct media_pad *pads; - void (*destroy)(struct vimc_ent_device *); void (*process_frame)(struct vimc_ent_device *ved, struct media_pad *sink, const void *frame); void (*vdev_get_format)(struct vimc_ent_device *ved, @@ -176,7 +189,6 @@ const struct vimc_pix_map *vimc_pix_map_by_pixelformat(u32 pixelformat); * @num_pads: number of pads to initialize * @pads_flag: flags to use in each pad * @sd_ops: pointer to &struct v4l2_subdev_ops. - * @sd_destroy: callback to destroy the node * * Helper function initialize and register the struct vimc_ent_device and struct * v4l2_subdev which represents a subdev node in the topology @@ -188,14 +200,13 @@ int vimc_ent_sd_register(struct vimc_ent_device *ved, u32 function, u16 num_pads, const unsigned long *pads_flag, - const struct v4l2_subdev_ops *sd_ops, - void (*sd_destroy)(struct vimc_ent_device *)); + const struct v4l2_subdev_ops *sd_ops); /** - * vimc_ent_sd_register - initialize and register a subdev node + * vimc_ent_sd_unregister - cleanup and unregister a subdev node * - * @ved: the vimc_ent_device struct to be initialize - * @sd: the v4l2_subdev struct to be initialize and registered + * @ved: the vimc_ent_device struct to be cleaned up + * @sd: the v4l2_subdev struct to be unregistered * * Helper function cleanup and unregister the struct vimc_ent_device and struct * v4l2_subdev which represents a subdev node in the topology diff --git a/drivers/media/platform/vimc/vimc-core.c b/drivers/media/platform/vimc/vimc-core.c index afc79e2f3029..51c0eee61ca6 100644 --- a/drivers/media/platform/vimc/vimc-core.c +++ b/drivers/media/platform/vimc/vimc-core.c @@ -15,15 +15,14 @@ * */ +#include #include #include #include #include #include -#include "vimc-capture.h" #include "vimc-common.h" -#include "vimc-sensor.h" #define VIMC_PDEV_NAME "vimc" #define VIMC_MDEV_MODEL_NAME "VIMC MDEV" @@ -37,10 +36,10 @@ } struct vimc_device { - /* - * The pipeline configuration - * (filled before calling vimc_device_register) - */ + /* The platform device */ + struct platform_device pdev; + + /* The pipeline configuration */ const struct vimc_pipeline_config *pipe_cfg; /* The Associated media_device parent */ @@ -49,43 +48,14 @@ struct vimc_device { /* Internal v4l2 parent device*/ struct v4l2_device v4l2_dev; - /* Internal topology */ - struct vimc_ent_device **ved; -}; - -/** - * enum vimc_ent_node - Select the functionality of a node in the topology - * @VIMC_ENT_NODE_SENSOR: A node of type SENSOR simulates a camera sensor - * generating internal images in bayer format and - * propagating those images through the pipeline - * @VIMC_ENT_NODE_CAPTURE: A node of type CAPTURE is a v4l2 video_device - * that exposes the received image from the - * pipeline to the user space - * @VIMC_ENT_NODE_INPUT: A node of type INPUT is a v4l2 video_device that - * receives images from the user space and - * propagates them through the pipeline - * @VIMC_ENT_NODE_DEBAYER: A node type DEBAYER expects to receive a frame - * in bayer format converts it to RGB - * @VIMC_ENT_NODE_SCALER: A node of type SCALER scales the received image - * by a given multiplier - * - * This enum is used in the entity configuration struct to allow the definition - * of a custom topology specifying the role of each node on it. - */ -enum vimc_ent_node { - VIMC_ENT_NODE_SENSOR, - VIMC_ENT_NODE_CAPTURE, - VIMC_ENT_NODE_INPUT, - VIMC_ENT_NODE_DEBAYER, - VIMC_ENT_NODE_SCALER, + /* Subdevices */ + struct platform_device **subdevs; }; /* Structure which describes individual configuration for each entity */ struct vimc_ent_config { const char *name; - size_t pads_qty; - const unsigned long *pads_flag; - enum vimc_ent_node node; + const char *drv; }; /* Structure which describes links between entities */ @@ -112,60 +82,40 @@ struct vimc_pipeline_config { static const struct vimc_ent_config ent_config[] = { { .name = "Sensor A", - .pads_qty = 1, - .pads_flag = (const unsigned long[]){MEDIA_PAD_FL_SOURCE}, - .node = VIMC_ENT_NODE_SENSOR, + .drv = "vimc-sensor", }, { .name = "Sensor B", - .pads_qty = 1, - .pads_flag = (const unsigned long[]){MEDIA_PAD_FL_SOURCE}, - .node = VIMC_ENT_NODE_SENSOR, + .drv = "vimc-sensor", }, { .name = "Debayer A", - .pads_qty = 2, - .pads_flag = (const unsigned long[]){MEDIA_PAD_FL_SINK, - MEDIA_PAD_FL_SOURCE}, - .node = VIMC_ENT_NODE_DEBAYER, + .drv = "vimc-debayer", }, { .name = "Debayer B", - .pads_qty = 2, - .pads_flag = (const unsigned long[]){MEDIA_PAD_FL_SINK, - MEDIA_PAD_FL_SOURCE}, - .node = VIMC_ENT_NODE_DEBAYER, + .drv = "vimc-debayer", }, { .name = "Raw Capture 0", - .pads_qty = 1, - .pads_flag = (const unsigned long[]){MEDIA_PAD_FL_SINK}, - .node = VIMC_ENT_NODE_CAPTURE, + .drv = "vimc-capture", }, { .name = "Raw Capture 1", - .pads_qty = 1, - .pads_flag = (const unsigned long[]){MEDIA_PAD_FL_SINK}, - .node = VIMC_ENT_NODE_CAPTURE, + .drv = "vimc-capture", }, { .name = "RGB/YUV Input", - .pads_qty = 1, - .pads_flag = (const unsigned long[]){MEDIA_PAD_FL_SOURCE}, - .node = VIMC_ENT_NODE_INPUT, + /* TODO: change this to vimc-input when it is implemented */ + .drv = "vimc-sensor", }, { .name = "Scaler", - .pads_qty = 2, - .pads_flag = (const unsigned long[]){MEDIA_PAD_FL_SINK, - MEDIA_PAD_FL_SOURCE}, - .node = VIMC_ENT_NODE_SCALER, + .drv = "vimc-scaler", }, { .name = "RGB/YUV Capture", - .pads_qty = 1, - .pads_flag = (const unsigned long[]){MEDIA_PAD_FL_SINK}, - .node = VIMC_ENT_NODE_CAPTURE, + .drv = "vimc-capture", }, }; @@ -197,111 +147,40 @@ static const struct vimc_pipeline_config pipe_cfg = { /* -------------------------------------------------------------------------- */ -static void vimc_device_unregister(struct vimc_device *vimc) +static int vimc_create_links(struct vimc_device *vimc) { unsigned int i; - - media_device_unregister(&vimc->mdev); - /* Cleanup (only initialized) entities */ - for (i = 0; i < vimc->pipe_cfg->num_ents; i++) { - if (vimc->ved[i] && vimc->ved[i]->destroy) - vimc->ved[i]->destroy(vimc->ved[i]); - - vimc->ved[i] = NULL; - } - v4l2_device_unregister(&vimc->v4l2_dev); - media_device_cleanup(&vimc->mdev); -} - -/* - * TODO: remove this function when all the - * entities specific code are implemented - */ -static void vimc_raw_destroy(struct vimc_ent_device *ved) -{ - media_device_unregister_entity(ved->ent); - - media_entity_cleanup(ved->ent); - - vimc_pads_cleanup(ved->pads); - - kfree(ved->ent); - - kfree(ved); -} - -/* - * TODO: remove this function when all the - * entities specific code are implemented - */ -static struct vimc_ent_device *vimc_raw_create(struct v4l2_device *v4l2_dev, - const char *const name, - u16 num_pads, - const unsigned long *pads_flag) -{ - struct vimc_ent_device *ved; int ret; - /* Allocate the main ved struct */ - ved = kzalloc(sizeof(*ved), GFP_KERNEL); - if (!ved) - return ERR_PTR(-ENOMEM); - - /* Allocate the media entity */ - ved->ent = kzalloc(sizeof(*ved->ent), GFP_KERNEL); - if (!ved->ent) { - ret = -ENOMEM; - goto err_free_ved; - } - - /* Allocate the pads */ - ved->pads = vimc_pads_init(num_pads, pads_flag); - if (IS_ERR(ved->pads)) { - ret = PTR_ERR(ved->pads); - goto err_free_ent; + /* Initialize the links between entities */ + for (i = 0; i < vimc->pipe_cfg->num_links; i++) { + const struct vimc_ent_link *link = &vimc->pipe_cfg->links[i]; + /* + * TODO: Check another way of retrieving ved struct without + * relying on platform_get_drvdata + */ + struct vimc_ent_device *ved_src = + platform_get_drvdata(vimc->subdevs[link->src_ent]); + struct vimc_ent_device *ved_sink = + platform_get_drvdata(vimc->subdevs[link->sink_ent]); + + ret = media_create_pad_link(ved_src->ent, link->src_pad, + ved_sink->ent, link->sink_pad, + link->flags); + if (ret) + return ret; } - /* Initialize the media entity */ - ved->ent->name = name; - ved->ent->function = MEDIA_ENT_F_V4L2_SUBDEV_UNKNOWN; - ret = media_entity_pads_init(ved->ent, num_pads, ved->pads); - if (ret) - goto err_cleanup_pads; - - /* Register the media entity */ - ret = media_device_register_entity(v4l2_dev->mdev, ved->ent); - if (ret) - goto err_cleanup_entity; - - /* Fill out the destroy function and return */ - ved->destroy = vimc_raw_destroy; - return ved; - -err_cleanup_entity: - media_entity_cleanup(ved->ent); -err_cleanup_pads: - vimc_pads_cleanup(ved->pads); -err_free_ent: - kfree(ved->ent); -err_free_ved: - kfree(ved); - - return ERR_PTR(ret); + return 0; } -static int vimc_device_register(struct vimc_device *vimc) +static int vimc_comp_bind(struct device *master) { - unsigned int i; + struct vimc_device *vimc = container_of(to_platform_device(master), + struct vimc_device, pdev); int ret; - /* Allocate memory for the vimc_ent_devices pointers */ - vimc->ved = devm_kcalloc(vimc->mdev.dev, vimc->pipe_cfg->num_ents, - sizeof(*vimc->ved), GFP_KERNEL); - if (!vimc->ved) - return -ENOMEM; - - /* Link the media device within the v4l2_device */ - vimc->v4l2_dev.mdev = &vimc->mdev; + dev_dbg(master, "bind"); /* Register the v4l2 struct */ ret = v4l2_device_register(vimc->mdev.dev, &vimc->v4l2_dev); @@ -311,66 +190,22 @@ static int vimc_device_register(struct vimc_device *vimc) return ret; } - /* Initialize entities */ - for (i = 0; i < vimc->pipe_cfg->num_ents; i++) { - struct vimc_ent_device *(*create_func)(struct v4l2_device *, - const char *const, - u16, - const unsigned long *); - - /* Register the specific node */ - switch (vimc->pipe_cfg->ents[i].node) { - case VIMC_ENT_NODE_SENSOR: - create_func = vimc_sen_create; - break; - - case VIMC_ENT_NODE_CAPTURE: - create_func = vimc_cap_create; - break; - - /* TODO: Instantiate the specific topology node */ - case VIMC_ENT_NODE_INPUT: - case VIMC_ENT_NODE_DEBAYER: - case VIMC_ENT_NODE_SCALER: - default: - /* - * TODO: remove this when all the entities specific - * code are implemented - */ - create_func = vimc_raw_create; - break; - } - - vimc->ved[i] = create_func(&vimc->v4l2_dev, - vimc->pipe_cfg->ents[i].name, - vimc->pipe_cfg->ents[i].pads_qty, - vimc->pipe_cfg->ents[i].pads_flag); - if (IS_ERR(vimc->ved[i])) { - ret = PTR_ERR(vimc->ved[i]); - vimc->ved[i] = NULL; - goto err; - } - } - - /* Initialize the links between entities */ - for (i = 0; i < vimc->pipe_cfg->num_links; i++) { - const struct vimc_ent_link *link = &vimc->pipe_cfg->links[i]; + /* Bind subdevices */ + ret = component_bind_all(master, &vimc->v4l2_dev); + if (ret) + goto err_v4l2_unregister; - ret = media_create_pad_link(vimc->ved[link->src_ent]->ent, - link->src_pad, - vimc->ved[link->sink_ent]->ent, - link->sink_pad, - link->flags); - if (ret) - goto err; - } + /* Initialize links */ + ret = vimc_create_links(vimc); + if (ret) + goto err_comp_unbind_all; /* Register the media device */ ret = media_device_register(&vimc->mdev); if (ret) { dev_err(vimc->mdev.dev, "media device register failed (err=%d)\n", ret); - return ret; + goto err_comp_unbind_all; } /* Expose all subdev's nodes*/ @@ -379,32 +214,106 @@ static int vimc_device_register(struct vimc_device *vimc) dev_err(vimc->mdev.dev, "vimc subdev nodes registration failed (err=%d)\n", ret); - goto err; + goto err_mdev_unregister; } return 0; -err: - /* Destroy the so far created topology */ - vimc_device_unregister(vimc); +err_mdev_unregister: + media_device_unregister(&vimc->mdev); +err_comp_unbind_all: + component_unbind_all(master, NULL); +err_v4l2_unregister: + v4l2_device_unregister(&vimc->v4l2_dev); return ret; } +static void vimc_comp_unbind(struct device *master) +{ + struct vimc_device *vimc = container_of(to_platform_device(master), + struct vimc_device, pdev); + + dev_dbg(master, "unbind"); + + media_device_unregister(&vimc->mdev); + component_unbind_all(master, NULL); + v4l2_device_unregister(&vimc->v4l2_dev); +} + +static int vimc_comp_compare(struct device *comp, void *data) +{ + const struct platform_device *pdev = to_platform_device(comp); + const char *name = data; + + return !strcmp(pdev->dev.platform_data, name); +} + +static struct component_match *vimc_add_subdevs(struct vimc_device *vimc) +{ + struct component_match *match = NULL; + struct vimc_platform_data pdata; + int i; + + for (i = 0; i < vimc->pipe_cfg->num_ents; i++) { + dev_dbg(&vimc->pdev.dev, "new pdev for %s\n", + vimc->pipe_cfg->ents[i].drv); + + strlcpy(pdata.entity_name, vimc->pipe_cfg->ents[i].name, + sizeof(pdata.entity_name)); + + vimc->subdevs[i] = platform_device_register_data(&vimc->pdev.dev, + vimc->pipe_cfg->ents[i].drv, + PLATFORM_DEVID_AUTO, + &pdata, + sizeof(pdata)); + if (!vimc->subdevs[i]) { + while (--i >= 0) + platform_device_unregister(vimc->subdevs[i]); + + return ERR_PTR(-ENOMEM); + } + + component_match_add(&vimc->pdev.dev, &match, vimc_comp_compare, + (void *)vimc->pipe_cfg->ents[i].name); + } + + return match; +} + +static void vimc_rm_subdevs(struct vimc_device *vimc) +{ + unsigned int i; + + for (i = 0; i < vimc->pipe_cfg->num_ents; i++) + platform_device_unregister(vimc->subdevs[i]); +} + +static const struct component_master_ops vimc_comp_ops = { + .bind = vimc_comp_bind, + .unbind = vimc_comp_unbind, +}; + static int vimc_probe(struct platform_device *pdev) { - struct vimc_device *vimc; + struct vimc_device *vimc = container_of(pdev, struct vimc_device, pdev); + struct component_match *match = NULL; int ret; - /* Prepare the vimc topology structure */ + dev_dbg(&pdev->dev, "probe"); - /* Allocate memory for the vimc structure */ - vimc = kzalloc(sizeof(*vimc), GFP_KERNEL); - if (!vimc) + /* Create platform_device for each entity in the topology*/ + vimc->subdevs = devm_kcalloc(&vimc->pdev.dev, vimc->pipe_cfg->num_ents, + sizeof(*vimc->subdevs), GFP_KERNEL); + if (!vimc->subdevs) return -ENOMEM; - /* Set the pipeline configuration struct */ - vimc->pipe_cfg = &pipe_cfg; + match = vimc_add_subdevs(vimc); + if (IS_ERR(match)) + return PTR_ERR(match); + + /* Link the media device within the v4l2_device */ + vimc->v4l2_dev.mdev = &vimc->mdev; /* Initialize media device */ strlcpy(vimc->mdev.model, VIMC_MDEV_MODEL_NAME, @@ -412,28 +321,27 @@ static int vimc_probe(struct platform_device *pdev) vimc->mdev.dev = &pdev->dev; media_device_init(&vimc->mdev); - /* Create vimc topology */ - ret = vimc_device_register(vimc); + /* Add self to the component system */ + ret = component_master_add_with_match(&pdev->dev, &vimc_comp_ops, + match); if (ret) { - dev_err(vimc->mdev.dev, - "vimc device registration failed (err=%d)\n", ret); + media_device_cleanup(&vimc->mdev); + vimc_rm_subdevs(vimc); kfree(vimc); return ret; } - /* Link the topology object with the platform device object */ - platform_set_drvdata(pdev, vimc); - return 0; } static int vimc_remove(struct platform_device *pdev) { - struct vimc_device *vimc = platform_get_drvdata(pdev); + struct vimc_device *vimc = container_of(pdev, struct vimc_device, pdev); - /* Destroy all the topology */ - vimc_device_unregister(vimc); - kfree(vimc); + dev_dbg(&pdev->dev, "remove"); + + component_master_del(&pdev->dev, &vimc_comp_ops); + vimc_rm_subdevs(vimc); return 0; } @@ -442,9 +350,12 @@ static void vimc_dev_release(struct device *dev) { } -static struct platform_device vimc_pdev = { - .name = VIMC_PDEV_NAME, - .dev.release = vimc_dev_release, +static struct vimc_device vimc_dev = { + .pipe_cfg = &pipe_cfg, + .pdev = { + .name = VIMC_PDEV_NAME, + .dev.release = vimc_dev_release, + } }; static struct platform_driver vimc_pdrv = { @@ -459,29 +370,29 @@ static int __init vimc_init(void) { int ret; - ret = platform_device_register(&vimc_pdev); + ret = platform_device_register(&vimc_dev.pdev); if (ret) { - dev_err(&vimc_pdev.dev, + dev_err(&vimc_dev.pdev.dev, "platform device registration failed (err=%d)\n", ret); return ret; } ret = platform_driver_register(&vimc_pdrv); if (ret) { - dev_err(&vimc_pdev.dev, + dev_err(&vimc_dev.pdev.dev, "platform driver registration failed (err=%d)\n", ret); - - platform_device_unregister(&vimc_pdev); + platform_driver_unregister(&vimc_pdrv); + return ret; } - return ret; + return 0; } static void __exit vimc_exit(void) { platform_driver_unregister(&vimc_pdrv); - platform_device_unregister(&vimc_pdev); + platform_device_unregister(&vimc_dev.pdev); } module_init(vimc_init); diff --git a/drivers/media/platform/vimc/vimc-sensor.c b/drivers/media/platform/vimc/vimc-sensor.c index d4f97050b544..5ea7b0853936 100644 --- a/drivers/media/platform/vimc/vimc-sensor.c +++ b/drivers/media/platform/vimc/vimc-sensor.c @@ -15,18 +15,24 @@ * */ +#include #include #include +#include +#include #include #include #include #include -#include "vimc-sensor.h" +#include "vimc-common.h" + +#define VIMC_SEN_DRV_NAME "vimc-sensor" struct vimc_sen_device { struct vimc_ent_device ved; struct v4l2_subdev sd; + struct device *dev; struct tpg_data tpg; struct task_struct *kthread_sen; u8 *frame; @@ -166,7 +172,7 @@ static int vimc_sen_set_fmt(struct v4l2_subdev *sd, /* Set the new format */ vimc_sen_adjust_fmt(&fmt->format); - dev_dbg(vsen->sd.v4l2_dev->mdev->dev, "%s: format update: " + dev_dbg(vsen->dev, "%s: format update: " "old:%dx%d (0x%x, %d, %d, %d, %d) " "new:%dx%d (0x%x, %d, %d, %d, %d)\n", vsen->sd.name, /* old */ @@ -252,8 +258,8 @@ static int vimc_sen_s_stream(struct v4l2_subdev *sd, int enable) vsen->kthread_sen = kthread_run(vimc_sen_tpg_thread, vsen, "%s-sen", vsen->sd.v4l2_dev->name); if (IS_ERR(vsen->kthread_sen)) { - dev_err(vsen->sd.v4l2_dev->dev, - "%s: kernel_thread() failed\n", vsen->sd.name); + dev_err(vsen->dev, "%s: kernel_thread() failed\n", + vsen->sd.name); vfree(vsen->frame); vsen->frame = NULL; return PTR_ERR(vsen->kthread_sen); @@ -285,8 +291,10 @@ static const struct v4l2_subdev_ops vimc_sen_ops = { .video = &vimc_sen_video_ops, }; -static void vimc_sen_destroy(struct vimc_ent_device *ved) +static void vimc_sen_comp_unbind(struct device *comp, struct device *master, + void *master_data) { + struct vimc_ent_device *ved = dev_get_drvdata(comp); struct vimc_sen_device *vsen = container_of(ved, struct vimc_sen_device, ved); @@ -295,36 +303,31 @@ static void vimc_sen_destroy(struct vimc_ent_device *ved) kfree(vsen); } -struct vimc_ent_device *vimc_sen_create(struct v4l2_device *v4l2_dev, - const char *const name, - u16 num_pads, - const unsigned long *pads_flag) +static int vimc_sen_comp_bind(struct device *comp, struct device *master, + void *master_data) { + struct v4l2_device *v4l2_dev = master_data; + struct vimc_platform_data *pdata = comp->platform_data; struct vimc_sen_device *vsen; - unsigned int i; int ret; - /* NOTE: a sensor node may be created with more then one pad */ - if (!name || !num_pads || !pads_flag) - return ERR_PTR(-EINVAL); - - /* check if all pads are sources */ - for (i = 0; i < num_pads; i++) - if (!(pads_flag[i] & MEDIA_PAD_FL_SOURCE)) - return ERR_PTR(-EINVAL); - /* Allocate the vsen struct */ vsen = kzalloc(sizeof(*vsen), GFP_KERNEL); if (!vsen) - return ERR_PTR(-ENOMEM); + return -ENOMEM; /* Initialize ved and sd */ - ret = vimc_ent_sd_register(&vsen->ved, &vsen->sd, v4l2_dev, name, - MEDIA_ENT_F_CAM_SENSOR, num_pads, pads_flag, - &vimc_sen_ops, vimc_sen_destroy); + ret = vimc_ent_sd_register(&vsen->ved, &vsen->sd, v4l2_dev, + pdata->entity_name, + MEDIA_ENT_F_ATV_DECODER, 1, + (const unsigned long[1]) {MEDIA_PAD_FL_SOURCE}, + &vimc_sen_ops); if (ret) goto err_free_vsen; + dev_set_drvdata(comp, &vsen->ved); + vsen->dev = comp; + /* Initialize the frame format */ vsen->mbus_format = fmt_default; @@ -335,12 +338,52 @@ struct vimc_ent_device *vimc_sen_create(struct v4l2_device *v4l2_dev, if (ret) goto err_unregister_ent_sd; - return &vsen->ved; + return 0; err_unregister_ent_sd: vimc_ent_sd_unregister(&vsen->ved, &vsen->sd); err_free_vsen: kfree(vsen); - return ERR_PTR(ret); + return ret; } + +static const struct component_ops vimc_sen_comp_ops = { + .bind = vimc_sen_comp_bind, + .unbind = vimc_sen_comp_unbind, +}; + +static int vimc_sen_probe(struct platform_device *pdev) +{ + return component_add(&pdev->dev, &vimc_sen_comp_ops); +} + +static int vimc_sen_remove(struct platform_device *pdev) +{ + component_del(&pdev->dev, &vimc_sen_comp_ops); + + return 0; +} + +static struct platform_driver vimc_sen_pdrv = { + .probe = vimc_sen_probe, + .remove = vimc_sen_remove, + .driver = { + .name = VIMC_SEN_DRV_NAME, + }, +}; + +static const struct platform_device_id vimc_sen_driver_ids[] = { + { + .name = VIMC_SEN_DRV_NAME, + }, + { } +}; + +module_platform_driver(vimc_sen_pdrv); + +MODULE_DEVICE_TABLE(platform, vimc_sen_driver_ids); + +MODULE_DESCRIPTION("Virtual Media Controller Driver (VIMC) Sensor"); +MODULE_AUTHOR("Helen Mae Koike Fornazier "); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/platform/vimc/vimc-sensor.h b/drivers/media/platform/vimc/vimc-sensor.h deleted file mode 100644 index 580dcec3f79c..000000000000 --- a/drivers/media/platform/vimc/vimc-sensor.h +++ /dev/null @@ -1,28 +0,0 @@ -/* - * vimc-sensor.h Virtual Media Controller Driver - * - * Copyright (C) 2015-2017 Helen Koike - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - */ - -#ifndef _VIMC_SENSOR_H_ -#define _VIMC_SENSOR_H_ - -#include "vimc-common.h" - -struct vimc_ent_device *vimc_sen_create(struct v4l2_device *v4l2_dev, - const char *const name, - u16 num_pads, - const unsigned long *pads_flag); - -#endif -- cgit v1.2.3 From 6856ba77607a467b0f8f94cc7f0dc6152f8786e5 Mon Sep 17 00:00:00 2001 From: Helen Fornazier Date: Mon, 19 Jun 2017 14:00:20 -0300 Subject: [media] vimc: sca: Add scaler Implement scaler and integrated with the core Signed-off-by: Helen Koike Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vimc/Makefile | 3 +- drivers/media/platform/vimc/vimc-common.c | 27 ++ drivers/media/platform/vimc/vimc-common.h | 1 + drivers/media/platform/vimc/vimc-scaler.c | 455 ++++++++++++++++++++++++++++++ 4 files changed, 485 insertions(+), 1 deletion(-) create mode 100644 drivers/media/platform/vimc/vimc-scaler.c (limited to 'drivers/media/platform/vimc/vimc-common.c') diff --git a/drivers/media/platform/vimc/Makefile b/drivers/media/platform/vimc/Makefile index 4fba8ef07498..68c5d9804c11 100644 --- a/drivers/media/platform/vimc/Makefile +++ b/drivers/media/platform/vimc/Makefile @@ -2,7 +2,8 @@ vimc-objs := vimc-core.o vimc_capture-objs := vimc-capture.o vimc_common-objs := vimc-common.o vimc_debayer-objs := vimc-debayer.o +vimc_scaler-objs := vimc-scaler.o vimc_sensor-objs := vimc-sensor.o obj-$(CONFIG_VIDEO_VIMC) += vimc.o vimc_capture.o vimc_common.o vimc-debayer.o \ - vimc_sensor.o + vimc_scaler.o vimc_sensor.o diff --git a/drivers/media/platform/vimc/vimc-common.c b/drivers/media/platform/vimc/vimc-common.c index da7f2b7a2e58..9d63c84a9876 100644 --- a/drivers/media/platform/vimc/vimc-common.c +++ b/drivers/media/platform/vimc/vimc-common.c @@ -20,6 +20,10 @@ #include "vimc-common.h" +/* + * NOTE: non-bayer formats need to come first (necessary for enum_mbus_code + * in the scaler) + */ static const struct vimc_pix_map vimc_pix_map_list[] = { /* TODO: add all missing formats */ @@ -28,16 +32,19 @@ static const struct vimc_pix_map vimc_pix_map_list[] = { .code = MEDIA_BUS_FMT_BGR888_1X24, .pixelformat = V4L2_PIX_FMT_BGR24, .bpp = 3, + .bayer = false, }, { .code = MEDIA_BUS_FMT_RGB888_1X24, .pixelformat = V4L2_PIX_FMT_RGB24, .bpp = 3, + .bayer = false, }, { .code = MEDIA_BUS_FMT_ARGB8888_1X32, .pixelformat = V4L2_PIX_FMT_ARGB32, .bpp = 4, + .bayer = false, }, /* Bayer formats */ @@ -45,41 +52,49 @@ static const struct vimc_pix_map vimc_pix_map_list[] = { .code = MEDIA_BUS_FMT_SBGGR8_1X8, .pixelformat = V4L2_PIX_FMT_SBGGR8, .bpp = 1, + .bayer = true, }, { .code = MEDIA_BUS_FMT_SGBRG8_1X8, .pixelformat = V4L2_PIX_FMT_SGBRG8, .bpp = 1, + .bayer = true, }, { .code = MEDIA_BUS_FMT_SGRBG8_1X8, .pixelformat = V4L2_PIX_FMT_SGRBG8, .bpp = 1, + .bayer = true, }, { .code = MEDIA_BUS_FMT_SRGGB8_1X8, .pixelformat = V4L2_PIX_FMT_SRGGB8, .bpp = 1, + .bayer = true, }, { .code = MEDIA_BUS_FMT_SBGGR10_1X10, .pixelformat = V4L2_PIX_FMT_SBGGR10, .bpp = 2, + .bayer = true, }, { .code = MEDIA_BUS_FMT_SGBRG10_1X10, .pixelformat = V4L2_PIX_FMT_SGBRG10, .bpp = 2, + .bayer = true, }, { .code = MEDIA_BUS_FMT_SGRBG10_1X10, .pixelformat = V4L2_PIX_FMT_SGRBG10, .bpp = 2, + .bayer = true, }, { .code = MEDIA_BUS_FMT_SRGGB10_1X10, .pixelformat = V4L2_PIX_FMT_SRGGB10, .bpp = 2, + .bayer = true, }, /* 10bit raw bayer a-law compressed to 8 bits */ @@ -87,21 +102,25 @@ static const struct vimc_pix_map vimc_pix_map_list[] = { .code = MEDIA_BUS_FMT_SBGGR10_ALAW8_1X8, .pixelformat = V4L2_PIX_FMT_SBGGR10ALAW8, .bpp = 1, + .bayer = true, }, { .code = MEDIA_BUS_FMT_SGBRG10_ALAW8_1X8, .pixelformat = V4L2_PIX_FMT_SGBRG10ALAW8, .bpp = 1, + .bayer = true, }, { .code = MEDIA_BUS_FMT_SGRBG10_ALAW8_1X8, .pixelformat = V4L2_PIX_FMT_SGRBG10ALAW8, .bpp = 1, + .bayer = true, }, { .code = MEDIA_BUS_FMT_SRGGB10_ALAW8_1X8, .pixelformat = V4L2_PIX_FMT_SRGGB10ALAW8, .bpp = 1, + .bayer = true, }, /* 10bit raw bayer DPCM compressed to 8 bits */ @@ -109,41 +128,49 @@ static const struct vimc_pix_map vimc_pix_map_list[] = { .code = MEDIA_BUS_FMT_SBGGR10_DPCM8_1X8, .pixelformat = V4L2_PIX_FMT_SBGGR10DPCM8, .bpp = 1, + .bayer = true, }, { .code = MEDIA_BUS_FMT_SGBRG10_DPCM8_1X8, .pixelformat = V4L2_PIX_FMT_SGBRG10DPCM8, .bpp = 1, + .bayer = true, }, { .code = MEDIA_BUS_FMT_SGRBG10_DPCM8_1X8, .pixelformat = V4L2_PIX_FMT_SGRBG10DPCM8, .bpp = 1, + .bayer = true, }, { .code = MEDIA_BUS_FMT_SRGGB10_DPCM8_1X8, .pixelformat = V4L2_PIX_FMT_SRGGB10DPCM8, .bpp = 1, + .bayer = true, }, { .code = MEDIA_BUS_FMT_SBGGR12_1X12, .pixelformat = V4L2_PIX_FMT_SBGGR12, .bpp = 2, + .bayer = true, }, { .code = MEDIA_BUS_FMT_SGBRG12_1X12, .pixelformat = V4L2_PIX_FMT_SGBRG12, .bpp = 2, + .bayer = true, }, { .code = MEDIA_BUS_FMT_SGRBG12_1X12, .pixelformat = V4L2_PIX_FMT_SGRBG12, .bpp = 2, + .bayer = true, }, { .code = MEDIA_BUS_FMT_SRGGB12_1X12, .pixelformat = V4L2_PIX_FMT_SRGGB12, .bpp = 2, + .bayer = true, }, }; diff --git a/drivers/media/platform/vimc/vimc-common.h b/drivers/media/platform/vimc/vimc-common.h index 25ba75283ed4..dca528a316e7 100644 --- a/drivers/media/platform/vimc/vimc-common.h +++ b/drivers/media/platform/vimc/vimc-common.h @@ -84,6 +84,7 @@ struct vimc_pix_map { unsigned int code; unsigned int bpp; u32 pixelformat; + bool bayer; }; /** diff --git a/drivers/media/platform/vimc/vimc-scaler.c b/drivers/media/platform/vimc/vimc-scaler.c new file mode 100644 index 000000000000..fe77505d2679 --- /dev/null +++ b/drivers/media/platform/vimc/vimc-scaler.c @@ -0,0 +1,455 @@ +/* + * vimc-scaler.c Virtual Media Controller Driver + * + * Copyright (C) 2015-2017 Helen Koike + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include + +#include "vimc-common.h" + +#define VIMC_SCA_DRV_NAME "vimc-scaler" + +static unsigned int sca_mult = 3; +module_param(sca_mult, uint, 0000); +MODULE_PARM_DESC(sca_mult, " the image size multiplier"); + +#define IS_SINK(pad) (!pad) +#define IS_SRC(pad) (pad) +#define MAX_ZOOM 8 + +struct vimc_sca_device { + struct vimc_ent_device ved; + struct v4l2_subdev sd; + struct device *dev; + /* NOTE: the source fmt is the same as the sink + * with the width and hight multiplied by mult + */ + struct v4l2_mbus_framefmt sink_fmt; + /* Values calculated when the stream starts */ + u8 *src_frame; + unsigned int src_line_size; + unsigned int bpp; +}; + +static const struct v4l2_mbus_framefmt sink_fmt_default = { + .width = 640, + .height = 480, + .code = MEDIA_BUS_FMT_RGB888_1X24, + .field = V4L2_FIELD_NONE, + .colorspace = V4L2_COLORSPACE_DEFAULT, +}; + +static int vimc_sca_init_cfg(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg) +{ + struct v4l2_mbus_framefmt *mf; + unsigned int i; + + mf = v4l2_subdev_get_try_format(sd, cfg, 0); + *mf = sink_fmt_default; + + for (i = 1; i < sd->entity.num_pads; i++) { + mf = v4l2_subdev_get_try_format(sd, cfg, i); + *mf = sink_fmt_default; + mf->width = mf->width * sca_mult; + mf->height = mf->height * sca_mult; + } + + return 0; +} + +static int vimc_sca_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_mbus_code_enum *code) +{ + const struct vimc_pix_map *vpix = vimc_pix_map_by_index(code->index); + + /* We don't support bayer format */ + if (!vpix || vpix->bayer) + return -EINVAL; + + code->code = vpix->code; + + return 0; +} + +static int vimc_sca_enum_frame_size(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_frame_size_enum *fse) +{ + const struct vimc_pix_map *vpix; + + if (fse->index) + return -EINVAL; + + /* Only accept code in the pix map table in non bayer format */ + vpix = vimc_pix_map_by_code(fse->code); + if (!vpix || vpix->bayer) + return -EINVAL; + + fse->min_width = VIMC_FRAME_MIN_WIDTH; + fse->min_height = VIMC_FRAME_MIN_HEIGHT; + + if (IS_SINK(fse->pad)) { + fse->max_width = VIMC_FRAME_MAX_WIDTH; + fse->max_height = VIMC_FRAME_MAX_HEIGHT; + } else { + fse->max_width = VIMC_FRAME_MAX_WIDTH * MAX_ZOOM; + fse->max_height = VIMC_FRAME_MAX_HEIGHT * MAX_ZOOM; + } + + return 0; +} + +static int vimc_sca_get_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format) +{ + struct vimc_sca_device *vsca = v4l2_get_subdevdata(sd); + + /* Get the current sink format */ + format->format = (format->which == V4L2_SUBDEV_FORMAT_TRY) ? + *v4l2_subdev_get_try_format(sd, cfg, 0) : + vsca->sink_fmt; + + /* Scale the frame size for the source pad */ + if (IS_SRC(format->pad)) { + format->format.width = vsca->sink_fmt.width * sca_mult; + format->format.height = vsca->sink_fmt.height * sca_mult; + } + + return 0; +} + +static void vimc_sca_adjust_sink_fmt(struct v4l2_mbus_framefmt *fmt) +{ + const struct vimc_pix_map *vpix; + + /* Only accept code in the pix map table in non bayer format */ + vpix = vimc_pix_map_by_code(fmt->code); + if (!vpix || vpix->bayer) + fmt->code = sink_fmt_default.code; + + fmt->width = clamp_t(u32, fmt->width, VIMC_FRAME_MIN_WIDTH, + VIMC_FRAME_MAX_WIDTH) & ~1; + fmt->height = clamp_t(u32, fmt->height, VIMC_FRAME_MIN_HEIGHT, + VIMC_FRAME_MAX_HEIGHT) & ~1; + + if (fmt->field == V4L2_FIELD_ANY) + fmt->field = sink_fmt_default.field; + + vimc_colorimetry_clamp(fmt); +} + +static int vimc_sca_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct vimc_sca_device *vsca = v4l2_get_subdevdata(sd); + struct v4l2_mbus_framefmt *sink_fmt; + + if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) { + /* Do not change the format while stream is on */ + if (vsca->src_frame) + return -EBUSY; + + sink_fmt = &vsca->sink_fmt; + } else { + sink_fmt = v4l2_subdev_get_try_format(sd, cfg, 0); + } + + /* + * Do not change the format of the source pad, + * it is propagated from the sink + */ + if (IS_SRC(fmt->pad)) { + fmt->format = *sink_fmt; + fmt->format.width = sink_fmt->width * sca_mult; + fmt->format.height = sink_fmt->height * sca_mult; + } else { + /* Set the new format in the sink pad */ + vimc_sca_adjust_sink_fmt(&fmt->format); + + dev_dbg(vsca->dev, "%s: sink format update: " + "old:%dx%d (0x%x, %d, %d, %d, %d) " + "new:%dx%d (0x%x, %d, %d, %d, %d)\n", vsca->sd.name, + /* old */ + sink_fmt->width, sink_fmt->height, sink_fmt->code, + sink_fmt->colorspace, sink_fmt->quantization, + sink_fmt->xfer_func, sink_fmt->ycbcr_enc, + /* new */ + fmt->format.width, fmt->format.height, fmt->format.code, + fmt->format.colorspace, fmt->format.quantization, + fmt->format.xfer_func, fmt->format.ycbcr_enc); + + *sink_fmt = fmt->format; + } + + return 0; +} + +static const struct v4l2_subdev_pad_ops vimc_sca_pad_ops = { + .init_cfg = vimc_sca_init_cfg, + .enum_mbus_code = vimc_sca_enum_mbus_code, + .enum_frame_size = vimc_sca_enum_frame_size, + .get_fmt = vimc_sca_get_fmt, + .set_fmt = vimc_sca_set_fmt, +}; + +static int vimc_sca_s_stream(struct v4l2_subdev *sd, int enable) +{ + struct vimc_sca_device *vsca = v4l2_get_subdevdata(sd); + int ret; + + if (enable) { + const struct vimc_pix_map *vpix; + unsigned int frame_size; + + if (vsca->src_frame) + return 0; + + /* Save the bytes per pixel of the sink */ + vpix = vimc_pix_map_by_code(vsca->sink_fmt.code); + vsca->bpp = vpix->bpp; + + /* Calculate the width in bytes of the src frame */ + vsca->src_line_size = vsca->sink_fmt.width * + sca_mult * vsca->bpp; + + /* Calculate the frame size of the source pad */ + frame_size = vsca->src_line_size * vsca->sink_fmt.height * + sca_mult; + + /* Allocate the frame buffer. Use vmalloc to be able to + * allocate a large amount of memory + */ + vsca->src_frame = vmalloc(frame_size); + if (!vsca->src_frame) + return -ENOMEM; + + /* Turn the stream on in the subdevices directly connected */ + ret = vimc_pipeline_s_stream(&vsca->sd.entity, 1); + if (ret) { + vfree(vsca->src_frame); + vsca->src_frame = NULL; + return ret; + } + } else { + if (!vsca->src_frame) + return 0; + + /* Disable streaming from the pipe */ + ret = vimc_pipeline_s_stream(&vsca->sd.entity, 0); + if (ret) + return ret; + + vfree(vsca->src_frame); + vsca->src_frame = NULL; + } + + return 0; +} + +static struct v4l2_subdev_video_ops vimc_sca_video_ops = { + .s_stream = vimc_sca_s_stream, +}; + +static const struct v4l2_subdev_ops vimc_sca_ops = { + .pad = &vimc_sca_pad_ops, + .video = &vimc_sca_video_ops, +}; + +static void vimc_sca_fill_pix(u8 *const ptr, + const u8 *const pixel, + const unsigned int bpp) +{ + unsigned int i; + + /* copy the pixel to the pointer */ + for (i = 0; i < bpp; i++) + ptr[i] = pixel[i]; +} + +static void vimc_sca_scale_pix(const struct vimc_sca_device *const vsca, + const unsigned int lin, const unsigned int col, + const u8 *const sink_frame) +{ + unsigned int i, j, index; + const u8 *pixel; + + /* Point to the pixel value in position (lin, col) in the sink frame */ + index = VIMC_FRAME_INDEX(lin, col, + vsca->sink_fmt.width, + vsca->bpp); + pixel = &sink_frame[index]; + + dev_dbg(vsca->dev, + "sca: %s: --- scale_pix sink pos %dx%d, index %d ---\n", + vsca->sd.name, lin, col, index); + + /* point to the place we are going to put the first pixel + * in the scaled src frame + */ + index = VIMC_FRAME_INDEX(lin * sca_mult, col * sca_mult, + vsca->sink_fmt.width * sca_mult, vsca->bpp); + + dev_dbg(vsca->dev, "sca: %s: scale_pix src pos %dx%d, index %d\n", + vsca->sd.name, lin * sca_mult, col * sca_mult, index); + + /* Repeat this pixel mult times */ + for (i = 0; i < sca_mult; i++) { + /* Iterate through each beginning of a + * pixel repetition in a line + */ + for (j = 0; j < sca_mult * vsca->bpp; j += vsca->bpp) { + dev_dbg(vsca->dev, + "sca: %s: sca: scale_pix src pos %d\n", + vsca->sd.name, index + j); + + /* copy the pixel to the position index + j */ + vimc_sca_fill_pix(&vsca->src_frame[index + j], + pixel, vsca->bpp); + } + + /* move the index to the next line */ + index += vsca->src_line_size; + } +} + +static void vimc_sca_fill_src_frame(const struct vimc_sca_device *const vsca, + const u8 *const sink_frame) +{ + unsigned int i, j; + + /* Scale each pixel from the original sink frame */ + /* TODO: implement scale down, only scale up is supported for now */ + for (i = 0; i < vsca->sink_fmt.height; i++) + for (j = 0; j < vsca->sink_fmt.width; j++) + vimc_sca_scale_pix(vsca, i, j, sink_frame); +} + +static void vimc_sca_process_frame(struct vimc_ent_device *ved, + struct media_pad *sink, + const void *sink_frame) +{ + struct vimc_sca_device *vsca = container_of(ved, struct vimc_sca_device, + ved); + unsigned int i; + + /* If the stream in this node is not active, just return */ + if (!vsca->src_frame) + return; + + vimc_sca_fill_src_frame(vsca, sink_frame); + + /* Propagate the frame through all source pads */ + for (i = 1; i < vsca->sd.entity.num_pads; i++) { + struct media_pad *pad = &vsca->sd.entity.pads[i]; + + vimc_propagate_frame(pad, vsca->src_frame); + } +}; + +static void vimc_sca_comp_unbind(struct device *comp, struct device *master, + void *master_data) +{ + struct vimc_ent_device *ved = dev_get_drvdata(comp); + struct vimc_sca_device *vsca = container_of(ved, struct vimc_sca_device, + ved); + + vimc_ent_sd_unregister(ved, &vsca->sd); + kfree(vsca); +} + + +static int vimc_sca_comp_bind(struct device *comp, struct device *master, + void *master_data) +{ + struct v4l2_device *v4l2_dev = master_data; + struct vimc_platform_data *pdata = comp->platform_data; + struct vimc_sca_device *vsca; + int ret; + + /* Allocate the vsca struct */ + vsca = kzalloc(sizeof(*vsca), GFP_KERNEL); + if (!vsca) + return -ENOMEM; + + /* Initialize ved and sd */ + ret = vimc_ent_sd_register(&vsca->ved, &vsca->sd, v4l2_dev, + pdata->entity_name, + MEDIA_ENT_F_ATV_DECODER, 2, + (const unsigned long[2]) {MEDIA_PAD_FL_SINK, + MEDIA_PAD_FL_SOURCE}, + &vimc_sca_ops); + if (ret) { + kfree(vsca); + return ret; + } + + vsca->ved.process_frame = vimc_sca_process_frame; + dev_set_drvdata(comp, &vsca->ved); + vsca->dev = comp; + + /* Initialize the frame format */ + vsca->sink_fmt = sink_fmt_default; + + return 0; +} + +static const struct component_ops vimc_sca_comp_ops = { + .bind = vimc_sca_comp_bind, + .unbind = vimc_sca_comp_unbind, +}; + +static int vimc_sca_probe(struct platform_device *pdev) +{ + return component_add(&pdev->dev, &vimc_sca_comp_ops); +} + +static int vimc_sca_remove(struct platform_device *pdev) +{ + component_del(&pdev->dev, &vimc_sca_comp_ops); + + return 0; +} + +static struct platform_driver vimc_sca_pdrv = { + .probe = vimc_sca_probe, + .remove = vimc_sca_remove, + .driver = { + .name = VIMC_SCA_DRV_NAME, + }, +}; + +static const struct platform_device_id vimc_sca_driver_ids[] = { + { + .name = VIMC_SCA_DRV_NAME, + }, + { } +}; + +module_platform_driver(vimc_sca_pdrv); + +MODULE_DEVICE_TABLE(platform, vimc_sca_driver_ids); + +MODULE_DESCRIPTION("Virtual Media Controller Driver (VIMC) Scaler"); +MODULE_AUTHOR("Helen Mae Koike Fornazier "); +MODULE_LICENSE("GPL"); -- cgit v1.2.3