1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Driver for STM32 Digital Camera Memory Interface Pixel Processor 4 * 5 * Copyright (C) STMicroelectronics SA 2023 6 * Authors: Hugues Fruchet <hugues.fruchet@foss.st.com> 7 * Alain Volmat <alain.volmat@foss.st.com> 8 * for STMicroelectronics. 9 */ 10 11 #include <linux/init.h> 12 #include <linux/module.h> 13 14 #include "dcmipp-common.h" 15 16 /* Helper function to allocate and initialize pads */ 17 struct media_pad *dcmipp_pads_init(u16 num_pads, const unsigned long *pads_flags) 18 { 19 struct media_pad *pads; 20 unsigned int i; 21 22 /* Allocate memory for the pads */ 23 pads = kcalloc(num_pads, sizeof(*pads), GFP_KERNEL); 24 if (!pads) 25 return ERR_PTR(-ENOMEM); 26 27 /* Initialize the pads */ 28 for (i = 0; i < num_pads; i++) { 29 pads[i].index = i; 30 pads[i].flags = pads_flags[i]; 31 } 32 33 return pads; 34 } 35 36 static const struct media_entity_operations dcmipp_entity_ops = { 37 .link_validate = v4l2_subdev_link_validate, 38 }; 39 40 int dcmipp_ent_sd_register(struct dcmipp_ent_device *ved, 41 struct v4l2_subdev *sd, 42 struct v4l2_device *v4l2_dev, 43 const char *const name, 44 u32 function, 45 u16 num_pads, 46 const unsigned long *pads_flag, 47 const struct v4l2_subdev_internal_ops *sd_int_ops, 48 const struct v4l2_subdev_ops *sd_ops, 49 irq_handler_t handler, 50 irq_handler_t thread_fn) 51 { 52 int ret; 53 54 /* Allocate the pads. Should be released from the sd_int_op release */ 55 ved->pads = dcmipp_pads_init(num_pads, pads_flag); 56 if (IS_ERR(ved->pads)) 57 return PTR_ERR(ved->pads); 58 59 /* Fill the dcmipp_ent_device struct */ 60 ved->ent = &sd->entity; 61 62 /* Initialize the subdev */ 63 v4l2_subdev_init(sd, sd_ops); 64 sd->internal_ops = sd_int_ops; 65 sd->entity.function = function; 66 sd->entity.ops = &dcmipp_entity_ops; 67 sd->owner = THIS_MODULE; 68 strscpy(sd->name, name, sizeof(sd->name)); 69 v4l2_set_subdevdata(sd, ved); 70 71 /* Expose this subdev to user space */ 72 sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; 73 if (sd->ctrl_handler) 74 sd->flags |= V4L2_SUBDEV_FL_HAS_EVENTS; 75 76 /* Initialize the media entity */ 77 ret = media_entity_pads_init(&sd->entity, num_pads, ved->pads); 78 if (ret) 79 goto err_clean_pads; 80 81 ret = v4l2_subdev_init_finalize(sd); 82 if (ret < 0) 83 goto err_clean_m_ent; 84 85 /* Register the subdev with the v4l2 and the media framework */ 86 ret = v4l2_device_register_subdev(v4l2_dev, sd); 87 if (ret) { 88 dev_err(v4l2_dev->dev, 89 "%s: subdev register failed (err=%d)\n", 90 name, ret); 91 goto err_clean_m_ent; 92 } 93 94 ved->handler = handler; 95 ved->thread_fn = thread_fn; 96 97 return 0; 98 99 err_clean_m_ent: 100 media_entity_cleanup(&sd->entity); 101 err_clean_pads: 102 dcmipp_pads_cleanup(ved->pads); 103 return ret; 104 } 105 106 void 107 dcmipp_ent_sd_unregister(struct dcmipp_ent_device *ved, struct v4l2_subdev *sd) 108 { 109 media_entity_cleanup(ved->ent); 110 v4l2_device_unregister_subdev(sd); 111 } 112