0001
0002
0003
0004
0005
0006
0007
0008
0009 #include <linux/of_graph.h>
0010 #include <linux/of_platform.h>
0011 #include <media/v4l2-ctrls.h>
0012 #include <media/v4l2-event.h>
0013 #include <media/v4l2-ioctl.h>
0014 #include <media/v4l2-mc.h>
0015 #include "imx-media.h"
0016
0017 static inline struct imx_media_dev *notifier2dev(struct v4l2_async_notifier *n)
0018 {
0019 return container_of(n, struct imx_media_dev, notifier);
0020 }
0021
0022
0023 static int imx_media_subdev_bound(struct v4l2_async_notifier *notifier,
0024 struct v4l2_subdev *sd,
0025 struct v4l2_async_subdev *asd)
0026 {
0027 struct imx_media_dev *imxmd = notifier2dev(notifier);
0028
0029 dev_dbg(imxmd->md.dev, "subdev %s bound\n", sd->name);
0030
0031 return 0;
0032 }
0033
0034
0035
0036
0037
0038 static void imx_media_create_csi2_links(struct imx_media_dev *imxmd)
0039 {
0040 struct v4l2_subdev *sd, *csi2 = NULL;
0041
0042 list_for_each_entry(sd, &imxmd->v4l2_dev.subdevs, list) {
0043 if (sd->grp_id == IMX_MEDIA_GRP_ID_CSI2) {
0044 csi2 = sd;
0045 break;
0046 }
0047 }
0048 if (!csi2)
0049 return;
0050
0051 list_for_each_entry(sd, &imxmd->v4l2_dev.subdevs, list) {
0052
0053 if (!(sd->grp_id & IMX_MEDIA_GRP_ID_IPU_CSI) &&
0054 !(sd->grp_id & IMX_MEDIA_GRP_ID_CSI) &&
0055 !(sd->grp_id & IMX_MEDIA_GRP_ID_CSI_MUX))
0056 continue;
0057
0058 v4l2_create_fwnode_links(csi2, sd);
0059 }
0060 }
0061
0062
0063
0064
0065
0066 static int imx_media_add_vdev_to_pad(struct imx_media_dev *imxmd,
0067 struct imx_media_video_dev *vdev,
0068 struct media_pad *srcpad)
0069 {
0070 struct media_entity *entity = srcpad->entity;
0071 struct imx_media_pad_vdev *pad_vdev;
0072 struct list_head *pad_vdev_list;
0073 struct media_link *link;
0074 struct v4l2_subdev *sd;
0075 int i, ret;
0076
0077
0078 if (!is_media_entity_v4l2_subdev(entity))
0079 return 0;
0080
0081 sd = media_entity_to_v4l2_subdev(entity);
0082
0083 pad_vdev_list = to_pad_vdev_list(sd, srcpad->index);
0084 if (!pad_vdev_list) {
0085 v4l2_warn(&imxmd->v4l2_dev, "%s:%u has no vdev list!\n",
0086 entity->name, srcpad->index);
0087
0088
0089
0090
0091 return 0;
0092 }
0093
0094
0095 list_for_each_entry(pad_vdev, pad_vdev_list, list) {
0096 if (pad_vdev->vdev == vdev)
0097 return 0;
0098 }
0099
0100 dev_dbg(imxmd->md.dev, "adding %s to pad %s:%u\n",
0101 vdev->vfd->entity.name, entity->name, srcpad->index);
0102
0103 pad_vdev = devm_kzalloc(imxmd->md.dev, sizeof(*pad_vdev), GFP_KERNEL);
0104 if (!pad_vdev)
0105 return -ENOMEM;
0106
0107
0108 pad_vdev->vdev = vdev;
0109 list_add_tail(&pad_vdev->list, pad_vdev_list);
0110
0111
0112 for (i = 0; i < entity->num_pads; i++) {
0113 struct media_pad *pad = &entity->pads[i];
0114
0115 if (!(pad->flags & MEDIA_PAD_FL_SINK))
0116 continue;
0117
0118 list_for_each_entry(link, &entity->links, list) {
0119 if (link->sink != pad)
0120 continue;
0121 ret = imx_media_add_vdev_to_pad(imxmd, vdev,
0122 link->source);
0123 if (ret)
0124 return ret;
0125 }
0126 }
0127
0128 return 0;
0129 }
0130
0131
0132
0133
0134
0135
0136 static int imx_media_alloc_pad_vdev_lists(struct imx_media_dev *imxmd)
0137 {
0138 struct list_head *vdev_lists;
0139 struct media_entity *entity;
0140 struct v4l2_subdev *sd;
0141 int i;
0142
0143 list_for_each_entry(sd, &imxmd->v4l2_dev.subdevs, list) {
0144 entity = &sd->entity;
0145 vdev_lists = devm_kcalloc(imxmd->md.dev,
0146 entity->num_pads, sizeof(*vdev_lists),
0147 GFP_KERNEL);
0148 if (!vdev_lists)
0149 return -ENOMEM;
0150
0151
0152 sd->host_priv = vdev_lists;
0153
0154 for (i = 0; i < entity->num_pads; i++)
0155 INIT_LIST_HEAD(to_pad_vdev_list(sd, i));
0156 }
0157
0158 return 0;
0159 }
0160
0161
0162 static int imx_media_create_pad_vdev_lists(struct imx_media_dev *imxmd)
0163 {
0164 struct imx_media_video_dev *vdev;
0165 struct media_link *link;
0166 int ret;
0167
0168 ret = imx_media_alloc_pad_vdev_lists(imxmd);
0169 if (ret)
0170 return ret;
0171
0172 list_for_each_entry(vdev, &imxmd->vdev_list, list) {
0173 link = list_first_entry(&vdev->vfd->entity.links,
0174 struct media_link, list);
0175 ret = imx_media_add_vdev_to_pad(imxmd, vdev, link->source);
0176 if (ret)
0177 return ret;
0178 }
0179
0180 return 0;
0181 }
0182
0183
0184 int imx_media_probe_complete(struct v4l2_async_notifier *notifier)
0185 {
0186 struct imx_media_dev *imxmd = notifier2dev(notifier);
0187 int ret;
0188
0189 mutex_lock(&imxmd->mutex);
0190
0191 imx_media_create_csi2_links(imxmd);
0192
0193 ret = imx_media_create_pad_vdev_lists(imxmd);
0194 if (ret)
0195 goto unlock;
0196
0197 ret = v4l2_device_register_subdev_nodes(&imxmd->v4l2_dev);
0198 unlock:
0199 mutex_unlock(&imxmd->mutex);
0200 if (ret)
0201 return ret;
0202
0203 return media_device_register(&imxmd->md);
0204 }
0205 EXPORT_SYMBOL_GPL(imx_media_probe_complete);
0206
0207
0208
0209
0210
0211 static int imx_media_inherit_controls(struct imx_media_dev *imxmd,
0212 struct video_device *vfd,
0213 struct media_entity *entity)
0214 {
0215 int i, ret = 0;
0216
0217 if (is_media_entity_v4l2_subdev(entity)) {
0218 struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity);
0219
0220 dev_dbg(imxmd->md.dev,
0221 "adding controls to %s from %s\n",
0222 vfd->entity.name, sd->entity.name);
0223
0224 ret = v4l2_ctrl_add_handler(vfd->ctrl_handler,
0225 sd->ctrl_handler,
0226 NULL, true);
0227 if (ret)
0228 return ret;
0229 }
0230
0231
0232 for (i = 0; i < entity->num_pads; i++) {
0233 struct media_pad *pad, *spad = &entity->pads[i];
0234
0235 if (!(spad->flags & MEDIA_PAD_FL_SINK))
0236 continue;
0237
0238 pad = media_pad_remote_pad_first(spad);
0239 if (!pad || !is_media_entity_v4l2_subdev(pad->entity))
0240 continue;
0241
0242 ret = imx_media_inherit_controls(imxmd, vfd, pad->entity);
0243 if (ret)
0244 break;
0245 }
0246
0247 return ret;
0248 }
0249
0250 static int imx_media_link_notify(struct media_link *link, u32 flags,
0251 unsigned int notification)
0252 {
0253 struct imx_media_dev *imxmd = container_of(link->graph_obj.mdev,
0254 struct imx_media_dev, md);
0255 struct media_entity *source = link->source->entity;
0256 struct imx_media_pad_vdev *pad_vdev;
0257 struct list_head *pad_vdev_list;
0258 struct video_device *vfd;
0259 struct v4l2_subdev *sd;
0260 int pad_idx, ret;
0261
0262 ret = v4l2_pipeline_link_notify(link, flags, notification);
0263 if (ret)
0264 return ret;
0265
0266
0267 if (!is_media_entity_v4l2_subdev(source))
0268 return 0;
0269
0270 sd = media_entity_to_v4l2_subdev(source);
0271 pad_idx = link->source->index;
0272
0273 pad_vdev_list = to_pad_vdev_list(sd, pad_idx);
0274 if (!pad_vdev_list) {
0275
0276 return 0;
0277 }
0278
0279
0280
0281
0282
0283
0284
0285
0286 if (notification == MEDIA_DEV_NOTIFY_PRE_LINK_CH &&
0287 !(flags & MEDIA_LNK_FL_ENABLED)) {
0288 list_for_each_entry(pad_vdev, pad_vdev_list, list) {
0289 vfd = pad_vdev->vdev->vfd;
0290 if (!vfd->ctrl_handler)
0291 continue;
0292 dev_dbg(imxmd->md.dev,
0293 "reset controls for %s\n",
0294 vfd->entity.name);
0295 v4l2_ctrl_handler_free(vfd->ctrl_handler);
0296 v4l2_ctrl_handler_init(vfd->ctrl_handler, 0);
0297 }
0298 } else if (notification == MEDIA_DEV_NOTIFY_POST_LINK_CH &&
0299 (link->flags & MEDIA_LNK_FL_ENABLED)) {
0300 list_for_each_entry(pad_vdev, pad_vdev_list, list) {
0301 vfd = pad_vdev->vdev->vfd;
0302 if (!vfd->ctrl_handler)
0303 continue;
0304 dev_dbg(imxmd->md.dev,
0305 "refresh controls for %s\n",
0306 vfd->entity.name);
0307 ret = imx_media_inherit_controls(imxmd, vfd,
0308 &vfd->entity);
0309 if (ret)
0310 break;
0311 }
0312 }
0313
0314 return ret;
0315 }
0316
0317 static void imx_media_notify(struct v4l2_subdev *sd, unsigned int notification,
0318 void *arg)
0319 {
0320 struct media_entity *entity = &sd->entity;
0321 int i;
0322
0323 if (notification != V4L2_DEVICE_NOTIFY_EVENT)
0324 return;
0325
0326 for (i = 0; i < entity->num_pads; i++) {
0327 struct media_pad *pad = &entity->pads[i];
0328 struct imx_media_pad_vdev *pad_vdev;
0329 struct list_head *pad_vdev_list;
0330
0331 pad_vdev_list = to_pad_vdev_list(sd, pad->index);
0332 if (!pad_vdev_list)
0333 continue;
0334 list_for_each_entry(pad_vdev, pad_vdev_list, list)
0335 v4l2_event_queue(pad_vdev->vdev->vfd, arg);
0336 }
0337 }
0338
0339 static const struct v4l2_async_notifier_operations imx_media_notifier_ops = {
0340 .bound = imx_media_subdev_bound,
0341 .complete = imx_media_probe_complete,
0342 };
0343
0344 static const struct media_device_ops imx_media_md_ops = {
0345 .link_notify = imx_media_link_notify,
0346 };
0347
0348 struct imx_media_dev *imx_media_dev_init(struct device *dev,
0349 const struct media_device_ops *ops)
0350 {
0351 struct imx_media_dev *imxmd;
0352 int ret;
0353
0354 imxmd = devm_kzalloc(dev, sizeof(*imxmd), GFP_KERNEL);
0355 if (!imxmd)
0356 return ERR_PTR(-ENOMEM);
0357
0358 dev_set_drvdata(dev, imxmd);
0359
0360 strscpy(imxmd->md.model, "imx-media", sizeof(imxmd->md.model));
0361 imxmd->md.ops = ops ? ops : &imx_media_md_ops;
0362 imxmd->md.dev = dev;
0363
0364 mutex_init(&imxmd->mutex);
0365
0366 imxmd->v4l2_dev.mdev = &imxmd->md;
0367 imxmd->v4l2_dev.notify = imx_media_notify;
0368 strscpy(imxmd->v4l2_dev.name, "imx-media",
0369 sizeof(imxmd->v4l2_dev.name));
0370 snprintf(imxmd->md.bus_info, sizeof(imxmd->md.bus_info),
0371 "platform:%s", dev_name(imxmd->md.dev));
0372
0373 media_device_init(&imxmd->md);
0374
0375 ret = v4l2_device_register(dev, &imxmd->v4l2_dev);
0376 if (ret < 0) {
0377 v4l2_err(&imxmd->v4l2_dev,
0378 "Failed to register v4l2_device: %d\n", ret);
0379 goto cleanup;
0380 }
0381
0382 INIT_LIST_HEAD(&imxmd->vdev_list);
0383
0384 v4l2_async_nf_init(&imxmd->notifier);
0385
0386 return imxmd;
0387
0388 cleanup:
0389 media_device_cleanup(&imxmd->md);
0390
0391 return ERR_PTR(ret);
0392 }
0393 EXPORT_SYMBOL_GPL(imx_media_dev_init);
0394
0395 int imx_media_dev_notifier_register(struct imx_media_dev *imxmd,
0396 const struct v4l2_async_notifier_operations *ops)
0397 {
0398 int ret;
0399
0400
0401 if (list_empty(&imxmd->notifier.asd_list)) {
0402 v4l2_err(&imxmd->v4l2_dev, "no subdevs\n");
0403 return -ENODEV;
0404 }
0405
0406
0407 imxmd->notifier.ops = ops ? ops : &imx_media_notifier_ops;
0408 ret = v4l2_async_nf_register(&imxmd->v4l2_dev, &imxmd->notifier);
0409 if (ret) {
0410 v4l2_err(&imxmd->v4l2_dev,
0411 "v4l2_async_nf_register failed with %d\n", ret);
0412 return ret;
0413 }
0414
0415 return 0;
0416 }
0417 EXPORT_SYMBOL_GPL(imx_media_dev_notifier_register);