1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Support for Medifield PNW Camera Imaging ISP subsystem.
4  *
5  * Copyright (c) 2010 Intel Corporation. All Rights Reserved.
6  *
7  * Copyright (c) 2010 Silicon Hive www.siliconhive.com.
8  *
9  * This program is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU General Public License version
11  * 2 as published by the Free Software Foundation.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  *
19  */
20 
21 #include <media/v4l2-event.h>
22 #include <media/v4l2-mediabus.h>
23 
24 #include <media/videobuf-vmalloc.h>
25 #include <linux/delay.h>
26 
27 #include "ia_css.h"
28 
29 #include "atomisp_cmd.h"
30 #include "atomisp_common.h"
31 #include "atomisp_file.h"
32 #include "atomisp_internal.h"
33 #include "atomisp_ioctl.h"
34 
file_work(struct work_struct * work)35 static void file_work(struct work_struct *work)
36 {
37 	struct atomisp_file_device *file_dev =
38 	    container_of(work, struct atomisp_file_device, work);
39 	struct atomisp_device *isp = file_dev->isp;
40 	/* only support file injection on subdev0 */
41 	struct atomisp_sub_device *asd = &isp->asd[0];
42 	struct atomisp_video_pipe *out_pipe = &asd->video_in;
43 	unsigned short *buf = videobuf_to_vmalloc(out_pipe->outq.bufs[0]);
44 	struct v4l2_mbus_framefmt isp_sink_fmt;
45 
46 	if (asd->streaming != ATOMISP_DEVICE_STREAMING_ENABLED)
47 		return;
48 
49 	dev_dbg(isp->dev, ">%s: ready to start streaming\n", __func__);
50 	isp_sink_fmt = *atomisp_subdev_get_ffmt(&asd->subdev, NULL,
51 						V4L2_SUBDEV_FORMAT_ACTIVE,
52 						ATOMISP_SUBDEV_PAD_SINK);
53 
54 	while (!ia_css_isp_has_started())
55 		usleep_range(1000, 1500);
56 
57 	ia_css_stream_send_input_frame(asd->stream_env[ATOMISP_INPUT_STREAM_GENERAL].stream,
58 				       buf, isp_sink_fmt.width,
59 				       isp_sink_fmt.height);
60 	dev_dbg(isp->dev, "<%s: streaming done\n", __func__);
61 }
62 
file_input_s_stream(struct v4l2_subdev * sd,int enable)63 static int file_input_s_stream(struct v4l2_subdev *sd, int enable)
64 {
65 	struct atomisp_file_device *file_dev = v4l2_get_subdevdata(sd);
66 	struct atomisp_device *isp = file_dev->isp;
67 	/* only support file injection on subdev0 */
68 	struct atomisp_sub_device *asd = &isp->asd[0];
69 
70 	dev_dbg(isp->dev, "%s: enable %d\n", __func__, enable);
71 	if (enable) {
72 		if (asd->streaming != ATOMISP_DEVICE_STREAMING_ENABLED)
73 			return 0;
74 
75 		queue_work(file_dev->work_queue, &file_dev->work);
76 		return 0;
77 	}
78 	cancel_work_sync(&file_dev->work);
79 	return 0;
80 }
81 
file_input_get_fmt(struct v4l2_subdev * sd,struct v4l2_subdev_state * sd_state,struct v4l2_subdev_format * format)82 static int file_input_get_fmt(struct v4l2_subdev *sd,
83 			      struct v4l2_subdev_state *sd_state,
84 			      struct v4l2_subdev_format *format)
85 {
86 	struct v4l2_mbus_framefmt *fmt = &format->format;
87 	struct atomisp_file_device *file_dev = v4l2_get_subdevdata(sd);
88 	struct atomisp_device *isp = file_dev->isp;
89 	/* only support file injection on subdev0 */
90 	struct atomisp_sub_device *asd = &isp->asd[0];
91 	struct v4l2_mbus_framefmt *isp_sink_fmt;
92 
93 	if (format->pad)
94 		return -EINVAL;
95 	isp_sink_fmt = atomisp_subdev_get_ffmt(&asd->subdev, NULL,
96 					       V4L2_SUBDEV_FORMAT_ACTIVE,
97 					       ATOMISP_SUBDEV_PAD_SINK);
98 
99 	fmt->width = isp_sink_fmt->width;
100 	fmt->height = isp_sink_fmt->height;
101 	fmt->code = isp_sink_fmt->code;
102 
103 	return 0;
104 }
105 
file_input_set_fmt(struct v4l2_subdev * sd,struct v4l2_subdev_state * sd_state,struct v4l2_subdev_format * format)106 static int file_input_set_fmt(struct v4l2_subdev *sd,
107 			      struct v4l2_subdev_state *sd_state,
108 			      struct v4l2_subdev_format *format)
109 {
110 	struct v4l2_mbus_framefmt *fmt = &format->format;
111 
112 	if (format->pad)
113 		return -EINVAL;
114 	file_input_get_fmt(sd, sd_state, format);
115 	if (format->which == V4L2_SUBDEV_FORMAT_TRY)
116 		sd_state->pads->try_fmt = *fmt;
117 	return 0;
118 }
119 
file_input_log_status(struct v4l2_subdev * sd)120 static int file_input_log_status(struct v4l2_subdev *sd)
121 {
122 	/*to fake*/
123 	return 0;
124 }
125 
file_input_s_power(struct v4l2_subdev * sd,int on)126 static int file_input_s_power(struct v4l2_subdev *sd, int on)
127 {
128 	/* to fake */
129 	return 0;
130 }
131 
file_input_enum_mbus_code(struct v4l2_subdev * sd,struct v4l2_subdev_state * sd_state,struct v4l2_subdev_mbus_code_enum * code)132 static int file_input_enum_mbus_code(struct v4l2_subdev *sd,
133 				     struct v4l2_subdev_state *sd_state,
134 				     struct v4l2_subdev_mbus_code_enum *code)
135 {
136 	/*to fake*/
137 	return 0;
138 }
139 
file_input_enum_frame_size(struct v4l2_subdev * sd,struct v4l2_subdev_state * sd_state,struct v4l2_subdev_frame_size_enum * fse)140 static int file_input_enum_frame_size(struct v4l2_subdev *sd,
141 				      struct v4l2_subdev_state *sd_state,
142 				      struct v4l2_subdev_frame_size_enum *fse)
143 {
144 	/*to fake*/
145 	return 0;
146 }
147 
file_input_enum_frame_ival(struct v4l2_subdev * sd,struct v4l2_subdev_state * sd_state,struct v4l2_subdev_frame_interval_enum * fie)148 static int file_input_enum_frame_ival(struct v4l2_subdev *sd,
149 				      struct v4l2_subdev_state *sd_state,
150 				      struct v4l2_subdev_frame_interval_enum
151 				      *fie)
152 {
153 	/*to fake*/
154 	return 0;
155 }
156 
157 static const struct v4l2_subdev_video_ops file_input_video_ops = {
158 	.s_stream = file_input_s_stream,
159 };
160 
161 static const struct v4l2_subdev_core_ops file_input_core_ops = {
162 	.log_status = file_input_log_status,
163 	.s_power = file_input_s_power,
164 };
165 
166 static const struct v4l2_subdev_pad_ops file_input_pad_ops = {
167 	.enum_mbus_code = file_input_enum_mbus_code,
168 	.enum_frame_size = file_input_enum_frame_size,
169 	.enum_frame_interval = file_input_enum_frame_ival,
170 	.get_fmt = file_input_get_fmt,
171 	.set_fmt = file_input_set_fmt,
172 };
173 
174 static const struct v4l2_subdev_ops file_input_ops = {
175 	.core = &file_input_core_ops,
176 	.video = &file_input_video_ops,
177 	.pad = &file_input_pad_ops,
178 };
179 
180 void
atomisp_file_input_unregister_entities(struct atomisp_file_device * file_dev)181 atomisp_file_input_unregister_entities(struct atomisp_file_device *file_dev)
182 {
183 	media_entity_cleanup(&file_dev->sd.entity);
184 	v4l2_device_unregister_subdev(&file_dev->sd);
185 }
186 
atomisp_file_input_register_entities(struct atomisp_file_device * file_dev,struct v4l2_device * vdev)187 int atomisp_file_input_register_entities(struct atomisp_file_device *file_dev,
188 	struct v4l2_device *vdev)
189 {
190 	/* Register the subdev and video nodes. */
191 	return  v4l2_device_register_subdev(vdev, &file_dev->sd);
192 }
193 
atomisp_file_input_cleanup(struct atomisp_device * isp)194 void atomisp_file_input_cleanup(struct atomisp_device *isp)
195 {
196 	struct atomisp_file_device *file_dev = &isp->file_dev;
197 
198 	if (file_dev->work_queue) {
199 		destroy_workqueue(file_dev->work_queue);
200 		file_dev->work_queue = NULL;
201 	}
202 }
203 
atomisp_file_input_init(struct atomisp_device * isp)204 int atomisp_file_input_init(struct atomisp_device *isp)
205 {
206 	struct atomisp_file_device *file_dev = &isp->file_dev;
207 	struct v4l2_subdev *sd = &file_dev->sd;
208 	struct media_pad *pads = file_dev->pads;
209 	struct media_entity *me = &sd->entity;
210 
211 	file_dev->isp = isp;
212 	file_dev->work_queue = alloc_workqueue(isp->v4l2_dev.name, 0, 1);
213 	if (!file_dev->work_queue) {
214 		dev_err(isp->dev, "Failed to initialize file inject workq\n");
215 		return -ENOMEM;
216 	}
217 
218 	INIT_WORK(&file_dev->work, file_work);
219 
220 	v4l2_subdev_init(sd, &file_input_ops);
221 	sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
222 	strscpy(sd->name, "file_input_subdev", sizeof(sd->name));
223 	v4l2_set_subdevdata(sd, file_dev);
224 
225 	pads[0].flags = MEDIA_PAD_FL_SINK;
226 	me->function = MEDIA_ENT_F_V4L2_SUBDEV_UNKNOWN;
227 
228 	return media_entity_pads_init(me, 1, pads);
229 }
230