| // SPDX-License-Identifier: GPL-2.0 |
| /* |
| * Driver for STM32 Digital Camera Memory Interface Pixel Processor |
| * |
| * Copyright (C) STMicroelectronics SA 2023 |
| * Authors: Hugues Fruchet <hugues.fruchet@foss.st.com> |
| * Alain Volmat <alain.volmat@foss.st.com> |
| * for STMicroelectronics. |
| */ |
| |
| #include <linux/init.h> |
| #include <linux/module.h> |
| |
| #include "dcmipp-common.h" |
| |
| /* Helper function to allocate and initialize pads */ |
| struct media_pad *dcmipp_pads_init(u16 num_pads, const unsigned long *pads_flags) |
| { |
| 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_flags[i]; |
| } |
| |
| return pads; |
| } |
| |
| static const struct media_entity_operations dcmipp_entity_ops = { |
| .link_validate = v4l2_subdev_link_validate, |
| }; |
| |
| int dcmipp_ent_sd_register(struct dcmipp_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_internal_ops *sd_int_ops, |
| const struct v4l2_subdev_ops *sd_ops, |
| irq_handler_t handler, |
| irq_handler_t thread_fn) |
| { |
| int ret; |
| |
| /* Allocate the pads. Should be released from the sd_int_op release */ |
| ved->pads = dcmipp_pads_init(num_pads, pads_flag); |
| if (IS_ERR(ved->pads)) |
| return PTR_ERR(ved->pads); |
| |
| /* Fill the dcmipp_ent_device struct */ |
| ved->ent = &sd->entity; |
| |
| /* Initialize the subdev */ |
| v4l2_subdev_init(sd, sd_ops); |
| sd->internal_ops = sd_int_ops; |
| sd->entity.function = function; |
| sd->entity.ops = &dcmipp_entity_ops; |
| sd->owner = THIS_MODULE; |
| strscpy(sd->name, name, sizeof(sd->name)); |
| v4l2_set_subdevdata(sd, ved); |
| |
| /* Expose this subdev to user space */ |
| sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; |
| if (sd->ctrl_handler) |
| sd->flags |= V4L2_SUBDEV_FL_HAS_EVENTS; |
| |
| /* Initialize the media entity */ |
| ret = media_entity_pads_init(&sd->entity, num_pads, ved->pads); |
| if (ret) |
| goto err_clean_pads; |
| |
| ret = v4l2_subdev_init_finalize(sd); |
| if (ret < 0) |
| goto err_clean_m_ent; |
| |
| /* 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; |
| } |
| |
| ved->handler = handler; |
| ved->thread_fn = thread_fn; |
| |
| return 0; |
| |
| err_clean_m_ent: |
| media_entity_cleanup(&sd->entity); |
| err_clean_pads: |
| dcmipp_pads_cleanup(ved->pads); |
| return ret; |
| } |
| |
| void |
| dcmipp_ent_sd_unregister(struct dcmipp_ent_device *ved, struct v4l2_subdev *sd) |
| { |
| media_entity_cleanup(ved->ent); |
| v4l2_device_unregister_subdev(sd); |
| } |