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