1// SPDX-License-Identifier: GPL-2.0
2/*
3 * Support for Intel Camera Imaging ISP subsystem.
4 * Copyright (c) 2010 - 2015, Intel Corporation.
5 *
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms and conditions of the GNU General Public License,
8 * version 2, as published by the Free Software Foundation.
9 *
10 * This program is distributed in the hope it will be useful, but WITHOUT
11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
13 * more details.
14 */
15
16#include "platform_support.h"
17
18#include "ia_css_inputfifo.h"
19
20#include "device_access.h"
21
22#define __INLINE_SP__
23#include "sp.h"
24#define __INLINE_ISP__
25#include "isp.h"
26#define __INLINE_IRQ__
27#include "irq.h"
28#define __INLINE_FIFO_MONITOR__
29#include "fifo_monitor.h"
30
31#define __INLINE_EVENT__
32#include "event_fifo.h"
33#define __INLINE_SP__
34
35#include "input_system.h"	/* MIPI_PREDICTOR_NONE,... */
36
37#include "assert_support.h"
38
39/* System independent */
40#include "sh_css_internal.h"
41#include "ia_css_isys.h"
42
43#define HBLANK_CYCLES (187)
44#define MARKER_CYCLES (6)
45
46#include <hive_isp_css_streaming_to_mipi_types_hrt.h>
47
48/* The data type is used to send special cases:
49 * yuv420: odd lines (1, 3 etc) are twice as wide as even
50 *         lines (0, 2, 4 etc).
51 * rgb: for two pixels per clock, the R and B values are sent
52 *      to output_0 while only G is sent to output_1. This means
53 *      that output_1 only gets half the number of values of output_0.
54 *      WARNING: This type should also be used for Legacy YUV420.
55 * regular: used for all other data types (RAW, YUV422, etc)
56 */
57enum inputfifo_mipi_data_type {
58	inputfifo_mipi_data_type_regular,
59	inputfifo_mipi_data_type_yuv420,
60	inputfifo_mipi_data_type_yuv420_legacy,
61	inputfifo_mipi_data_type_rgb,
62};
63
64static unsigned int inputfifo_curr_ch_id, inputfifo_curr_fmt_type;
65struct inputfifo_instance {
66	unsigned int				ch_id;
67	enum atomisp_input_format	input_format;
68	bool						two_ppc;
69	bool						streaming;
70	unsigned int				hblank_cycles;
71	unsigned int				marker_cycles;
72	unsigned int				fmt_type;
73	enum inputfifo_mipi_data_type	type;
74};
75
76/*
77 * Maintain a basic streaming to Mipi administration with ch_id as index
78 * ch_id maps on the "Mipi virtual channel ID" and can have value 0..3
79 */
80#define INPUTFIFO_NR_OF_S2M_CHANNELS	(4)
81static struct inputfifo_instance
82	inputfifo_inst_admin[INPUTFIFO_NR_OF_S2M_CHANNELS];
83
84/* Streaming to MIPI */
85static unsigned int inputfifo_wrap_marker(
86    /* static inline unsigned inputfifo_wrap_marker( */
87    unsigned int marker)
88{
89	return marker |
90	       (inputfifo_curr_ch_id << HIVE_STR_TO_MIPI_CH_ID_LSB) |
91	       (inputfifo_curr_fmt_type << _HIVE_STR_TO_MIPI_FMT_TYPE_LSB);
92}
93
94static inline void
95_sh_css_fifo_snd(unsigned int token)
96{
97	while (!can_event_send_token(STR2MIPI_EVENT_ID))
98		udelay(1);
99	event_send_token(STR2MIPI_EVENT_ID, token);
100	return;
101}
102
103static void inputfifo_send_data_a(
104    /* static inline void inputfifo_send_data_a( */
105    unsigned int data)
106{
107	unsigned int token = (1 << HIVE_STR_TO_MIPI_VALID_A_BIT) |
108			     (data << HIVE_STR_TO_MIPI_DATA_A_LSB);
109	_sh_css_fifo_snd(token);
110	return;
111}
112
113static void inputfifo_send_data_b(
114    /* static inline void inputfifo_send_data_b( */
115    unsigned int data)
116{
117	unsigned int token = (1 << HIVE_STR_TO_MIPI_VALID_B_BIT) |
118			     (data << _HIVE_STR_TO_MIPI_DATA_B_LSB);
119	_sh_css_fifo_snd(token);
120	return;
121}
122
123static void inputfifo_send_data(
124    /* static inline void inputfifo_send_data( */
125    unsigned int a,
126    unsigned int b)
127{
128	unsigned int token = ((1 << HIVE_STR_TO_MIPI_VALID_A_BIT) |
129			      (1 << HIVE_STR_TO_MIPI_VALID_B_BIT) |
130			      (a << HIVE_STR_TO_MIPI_DATA_A_LSB) |
131			      (b << _HIVE_STR_TO_MIPI_DATA_B_LSB));
132	_sh_css_fifo_snd(token);
133	return;
134}
135
136static void inputfifo_send_sol(void)
137/* static inline void inputfifo_send_sol(void) */
138{
139	hrt_data	token = inputfifo_wrap_marker(
140				1 << HIVE_STR_TO_MIPI_SOL_BIT);
141
142	_sh_css_fifo_snd(token);
143	return;
144}
145
146static void inputfifo_send_eol(void)
147/* static inline void inputfifo_send_eol(void) */
148{
149	hrt_data	token = inputfifo_wrap_marker(
150				1 << HIVE_STR_TO_MIPI_EOL_BIT);
151	_sh_css_fifo_snd(token);
152	return;
153}
154
155static void inputfifo_send_sof(void)
156/* static inline void inputfifo_send_sof(void) */
157{
158	hrt_data	token = inputfifo_wrap_marker(
159				1 << HIVE_STR_TO_MIPI_SOF_BIT);
160
161	_sh_css_fifo_snd(token);
162	return;
163}
164
165static void inputfifo_send_eof(void)
166/* static inline void inputfifo_send_eof(void) */
167{
168	hrt_data	token = inputfifo_wrap_marker(
169				1 << HIVE_STR_TO_MIPI_EOF_BIT);
170	_sh_css_fifo_snd(token);
171	return;
172}
173
174static void inputfifo_send_ch_id_and_fmt_type(
175    /* static inline
176    void inputfifo_send_ch_id_and_fmt_type( */
177    unsigned int ch_id,
178    unsigned int fmt_type)
179{
180	hrt_data	token;
181
182	inputfifo_curr_ch_id = ch_id & _HIVE_ISP_CH_ID_MASK;
183	inputfifo_curr_fmt_type = fmt_type & _HIVE_ISP_FMT_TYPE_MASK;
184	/* we send an zero marker, this will wrap the ch_id and
185	 * fmt_type automatically.
186	 */
187	token = inputfifo_wrap_marker(0);
188	_sh_css_fifo_snd(token);
189	return;
190}
191
192static void inputfifo_send_empty_token(void)
193/* static inline void inputfifo_send_empty_token(void) */
194{
195	hrt_data	token = inputfifo_wrap_marker(0);
196
197	_sh_css_fifo_snd(token);
198	return;
199}
200
201static void inputfifo_start_frame(
202    /* static inline void inputfifo_start_frame( */
203    unsigned int ch_id,
204    unsigned int fmt_type)
205{
206	inputfifo_send_ch_id_and_fmt_type(ch_id, fmt_type);
207	inputfifo_send_sof();
208	return;
209}
210
211static void inputfifo_end_frame(
212    unsigned int marker_cycles)
213{
214	unsigned int i;
215
216	for (i = 0; i < marker_cycles; i++)
217		inputfifo_send_empty_token();
218	inputfifo_send_eof();
219	return;
220}
221
222static void inputfifo_send_line2(
223    const unsigned short *data,
224    unsigned int width,
225    const unsigned short *data2,
226    unsigned int width2,
227    unsigned int hblank_cycles,
228    unsigned int marker_cycles,
229    unsigned int two_ppc,
230    enum inputfifo_mipi_data_type type)
231{
232	unsigned int i, is_rgb = 0, is_legacy = 0;
233
234	assert(data);
235	assert((data2) || (width2 == 0));
236	if (type == inputfifo_mipi_data_type_rgb)
237		is_rgb = 1;
238
239	if (type == inputfifo_mipi_data_type_yuv420_legacy)
240		is_legacy = 1;
241
242	for (i = 0; i < hblank_cycles; i++)
243		inputfifo_send_empty_token();
244	inputfifo_send_sol();
245	for (i = 0; i < marker_cycles; i++)
246		inputfifo_send_empty_token();
247	for (i = 0; i < width; i++, data++) {
248		/* for RGB in two_ppc, we only actually send 2 pixels per
249		 * clock in the even pixels (0, 2 etc). In the other cycles,
250		 * we only send 1 pixel, to data[0].
251		 */
252		unsigned int send_two_pixels = two_ppc;
253
254		if ((is_rgb || is_legacy) && (i % 3 == 2))
255			send_two_pixels = 0;
256		if (send_two_pixels) {
257			if (i + 1 == width) {
258				/* for jpg (binary) copy, this can occur
259				 * if the file contains an odd number of bytes.
260				 */
261				inputfifo_send_data(
262				    data[0], 0);
263			} else {
264				inputfifo_send_data(
265				    data[0], data[1]);
266			}
267			/* Additional increment because we send 2 pixels */
268			data++;
269			i++;
270		} else if (two_ppc && is_legacy) {
271			inputfifo_send_data_b(data[0]);
272		} else {
273			inputfifo_send_data_a(data[0]);
274		}
275	}
276
277	for (i = 0; i < width2; i++, data2++) {
278		/* for RGB in two_ppc, we only actually send 2 pixels per
279		 * clock in the even pixels (0, 2 etc). In the other cycles,
280		 * we only send 1 pixel, to data2[0].
281		 */
282		unsigned int send_two_pixels = two_ppc;
283
284		if ((is_rgb || is_legacy) && (i % 3 == 2))
285			send_two_pixels = 0;
286		if (send_two_pixels) {
287			if (i + 1 == width2) {
288				/* for jpg (binary) copy, this can occur
289				 * if the file contains an odd number of bytes.
290				 */
291				inputfifo_send_data(
292				    data2[0], 0);
293			} else {
294				inputfifo_send_data(
295				    data2[0], data2[1]);
296			}
297			/* Additional increment because we send 2 pixels */
298			data2++;
299			i++;
300		} else if (two_ppc && is_legacy) {
301			inputfifo_send_data_b(data2[0]);
302		} else {
303			inputfifo_send_data_a(data2[0]);
304		}
305	}
306	for (i = 0; i < hblank_cycles; i++)
307		inputfifo_send_empty_token();
308	inputfifo_send_eol();
309	return;
310}
311
312static void
313inputfifo_send_line(const unsigned short *data,
314		    unsigned int width,
315		    unsigned int hblank_cycles,
316		    unsigned int marker_cycles,
317		    unsigned int two_ppc,
318		    enum inputfifo_mipi_data_type type)
319{
320	assert(data);
321	inputfifo_send_line2(data, width, NULL, 0,
322			     hblank_cycles,
323			     marker_cycles,
324			     two_ppc,
325			     type);
326}
327
328/* Send a frame of data into the input network via the GP FIFO.
329 *  Parameters:
330 *   - data: array of 16 bit values that contains all data for the frame.
331 *   - width: width of a line in number of subpixels, for yuv420 it is the
332 *            number of Y components per line.
333 *   - height: height of the frame in number of lines.
334 *   - ch_id: channel ID.
335 *   - fmt_type: format type.
336 *   - hblank_cycles: length of horizontal blanking in cycles.
337 *   - marker_cycles: number of empty cycles after start-of-line and before
338 *                    end-of-frame.
339 *   - two_ppc: boolean, describes whether to send one or two pixels per clock
340 *              cycle. In this mode, we sent pixels N and N+1 in the same cycle,
341 *              to IF_PRIM_A and IF_PRIM_B respectively. The caller must make
342 *              sure the input data has been formatted correctly for this.
343 *              For example, for RGB formats this means that unused values
344 *              must be inserted.
345 *   - yuv420: boolean, describes whether (non-legacy) yuv420 data is used. In
346 *             this mode, the odd lines (1,3,5 etc) are half as long as the
347 *             even lines (2,4,6 etc).
348 *             Note that the first line is odd (1) and the second line is even
349 *             (2).
350 *
351 * This function does not do any reordering of pixels, the caller must make
352 * sure the data is in the righ format. Please refer to the CSS receiver
353 * documentation for details on the data formats.
354 */
355
356static void inputfifo_send_frame(
357    const unsigned short *data,
358    unsigned int width,
359    unsigned int height,
360    unsigned int ch_id,
361    unsigned int fmt_type,
362    unsigned int hblank_cycles,
363    unsigned int marker_cycles,
364    unsigned int two_ppc,
365    enum inputfifo_mipi_data_type type)
366{
367	unsigned int i;
368
369	assert(data);
370	inputfifo_start_frame(ch_id, fmt_type);
371
372	for (i = 0; i < height; i++) {
373		if ((type == inputfifo_mipi_data_type_yuv420) &&
374		    (i & 1) == 1) {
375			inputfifo_send_line(data, 2 * width,
376					    hblank_cycles,
377					    marker_cycles,
378					    two_ppc, type);
379			data += 2 * width;
380		} else {
381			inputfifo_send_line(data, width,
382					    hblank_cycles,
383					    marker_cycles,
384					    two_ppc, type);
385			data += width;
386		}
387	}
388	inputfifo_end_frame(marker_cycles);
389	return;
390}
391
392static enum inputfifo_mipi_data_type inputfifo_determine_type(
393    enum atomisp_input_format input_format)
394{
395	enum inputfifo_mipi_data_type type;
396
397	type = inputfifo_mipi_data_type_regular;
398	if (input_format == ATOMISP_INPUT_FORMAT_YUV420_8_LEGACY) {
399		type =
400		    inputfifo_mipi_data_type_yuv420_legacy;
401	} else if (input_format == ATOMISP_INPUT_FORMAT_YUV420_8  ||
402		   input_format == ATOMISP_INPUT_FORMAT_YUV420_10 ||
403		   input_format == ATOMISP_INPUT_FORMAT_YUV420_16) {
404		type =
405		    inputfifo_mipi_data_type_yuv420;
406	} else if (input_format >= ATOMISP_INPUT_FORMAT_RGB_444 &&
407		   input_format <= ATOMISP_INPUT_FORMAT_RGB_888) {
408		type =
409		    inputfifo_mipi_data_type_rgb;
410	}
411	return type;
412}
413
414static struct inputfifo_instance *inputfifo_get_inst(
415    unsigned int ch_id)
416{
417	return &inputfifo_inst_admin[ch_id];
418}
419
420void ia_css_inputfifo_send_input_frame(
421    const unsigned short *data,
422    unsigned int width,
423    unsigned int height,
424    unsigned int ch_id,
425    enum atomisp_input_format input_format,
426    bool two_ppc)
427{
428	unsigned int fmt_type, hblank_cycles, marker_cycles;
429	enum inputfifo_mipi_data_type type;
430
431	assert(data);
432	hblank_cycles = HBLANK_CYCLES;
433	marker_cycles = MARKER_CYCLES;
434	ia_css_isys_convert_stream_format_to_mipi_format(input_format,
435		MIPI_PREDICTOR_NONE,
436		&fmt_type);
437
438	type = inputfifo_determine_type(input_format);
439
440	inputfifo_send_frame(data, width, height,
441			     ch_id, fmt_type, hblank_cycles, marker_cycles,
442			     two_ppc, type);
443}
444
445void ia_css_inputfifo_start_frame(
446    unsigned int ch_id,
447    enum atomisp_input_format input_format,
448    bool two_ppc)
449{
450	struct inputfifo_instance *s2mi;
451
452	s2mi = inputfifo_get_inst(ch_id);
453
454	s2mi->ch_id = ch_id;
455	ia_css_isys_convert_stream_format_to_mipi_format(input_format,
456		MIPI_PREDICTOR_NONE,
457		&s2mi->fmt_type);
458	s2mi->two_ppc = two_ppc;
459	s2mi->type = inputfifo_determine_type(input_format);
460	s2mi->hblank_cycles = HBLANK_CYCLES;
461	s2mi->marker_cycles = MARKER_CYCLES;
462	s2mi->streaming = true;
463
464	inputfifo_start_frame(ch_id, s2mi->fmt_type);
465	return;
466}
467
468void ia_css_inputfifo_send_line(
469    unsigned int ch_id,
470    const unsigned short *data,
471    unsigned int width,
472    const unsigned short *data2,
473    unsigned int width2)
474{
475	struct inputfifo_instance *s2mi;
476
477	assert(data);
478	assert((data2) || (width2 == 0));
479	s2mi = inputfifo_get_inst(ch_id);
480
481	/* Set global variables that indicate channel_id and format_type */
482	inputfifo_curr_ch_id = (s2mi->ch_id) & _HIVE_ISP_CH_ID_MASK;
483	inputfifo_curr_fmt_type = (s2mi->fmt_type) & _HIVE_ISP_FMT_TYPE_MASK;
484
485	inputfifo_send_line2(data, width, data2, width2,
486			     s2mi->hblank_cycles,
487			     s2mi->marker_cycles,
488			     s2mi->two_ppc,
489			     s2mi->type);
490}
491
492void ia_css_inputfifo_send_embedded_line(
493    unsigned int	ch_id,
494    enum atomisp_input_format	data_type,
495    const unsigned short	*data,
496    unsigned int	width)
497{
498	struct inputfifo_instance *s2mi;
499	unsigned int fmt_type;
500
501	assert(data);
502	s2mi = inputfifo_get_inst(ch_id);
503	ia_css_isys_convert_stream_format_to_mipi_format(data_type,
504		MIPI_PREDICTOR_NONE, &fmt_type);
505
506	/* Set format_type for metadata line. */
507	inputfifo_curr_fmt_type = fmt_type & _HIVE_ISP_FMT_TYPE_MASK;
508
509	inputfifo_send_line(data, width, s2mi->hblank_cycles, s2mi->marker_cycles,
510			    s2mi->two_ppc, inputfifo_mipi_data_type_regular);
511}
512
513void ia_css_inputfifo_end_frame(
514    unsigned int	ch_id)
515{
516	struct inputfifo_instance *s2mi;
517
518	s2mi = inputfifo_get_inst(ch_id);
519
520	/* Set global variables that indicate channel_id and format_type */
521	inputfifo_curr_ch_id = (s2mi->ch_id) & _HIVE_ISP_CH_ID_MASK;
522	inputfifo_curr_fmt_type = (s2mi->fmt_type) & _HIVE_ISP_FMT_TYPE_MASK;
523
524	/* Call existing HRT function */
525	inputfifo_end_frame(s2mi->marker_cycles);
526
527	s2mi->streaming = false;
528	return;
529}
530