// SPDX-License-Identifier: GPL-2.0 /* * Support for Intel Camera Imaging ISP subsystem. * Copyright (c) 2010 - 2015, Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. */ #include "platform_support.h" #include "ia_css_inputfifo.h" #include "device_access.h" #define __INLINE_SP__ #include "sp.h" #define __INLINE_ISP__ #include "isp.h" #define __INLINE_IRQ__ #include "irq.h" #define __INLINE_FIFO_MONITOR__ #include "fifo_monitor.h" #define __INLINE_EVENT__ #include "event_fifo.h" #define __INLINE_SP__ #include "input_system.h" /* MIPI_PREDICTOR_NONE,... */ #include "assert_support.h" /* System independent */ #include "sh_css_internal.h" #include "ia_css_isys.h" #define HBLANK_CYCLES (187) #define MARKER_CYCLES (6) #include /* The data type is used to send special cases: * yuv420: odd lines (1, 3 etc) are twice as wide as even * lines (0, 2, 4 etc). * rgb: for two pixels per clock, the R and B values are sent * to output_0 while only G is sent to output_1. This means * that output_1 only gets half the number of values of output_0. * WARNING: This type should also be used for Legacy YUV420. * regular: used for all other data types (RAW, YUV422, etc) */ enum inputfifo_mipi_data_type { inputfifo_mipi_data_type_regular, inputfifo_mipi_data_type_yuv420, inputfifo_mipi_data_type_yuv420_legacy, inputfifo_mipi_data_type_rgb, }; static unsigned int inputfifo_curr_ch_id, inputfifo_curr_fmt_type; struct inputfifo_instance { unsigned int ch_id; enum atomisp_input_format input_format; bool two_ppc; bool streaming; unsigned int hblank_cycles; unsigned int marker_cycles; unsigned int fmt_type; enum inputfifo_mipi_data_type type; }; /* * Maintain a basic streaming to Mipi administration with ch_id as index * ch_id maps on the "Mipi virtual channel ID" and can have value 0..3 */ #define INPUTFIFO_NR_OF_S2M_CHANNELS (4) static struct inputfifo_instance inputfifo_inst_admin[INPUTFIFO_NR_OF_S2M_CHANNELS]; /* Streaming to MIPI */ static unsigned int inputfifo_wrap_marker( /* static inline unsigned inputfifo_wrap_marker( */ unsigned int marker) { return marker | (inputfifo_curr_ch_id << HIVE_STR_TO_MIPI_CH_ID_LSB) | (inputfifo_curr_fmt_type << _HIVE_STR_TO_MIPI_FMT_TYPE_LSB); } static inline void _sh_css_fifo_snd(unsigned int token) { while (!can_event_send_token(STR2MIPI_EVENT_ID)) udelay(1); event_send_token(STR2MIPI_EVENT_ID, token); return; } static void inputfifo_send_data_a( /* static inline void inputfifo_send_data_a( */ unsigned int data) { unsigned int token = (1 << HIVE_STR_TO_MIPI_VALID_A_BIT) | (data << HIVE_STR_TO_MIPI_DATA_A_LSB); _sh_css_fifo_snd(token); return; } static void inputfifo_send_data_b( /* static inline void inputfifo_send_data_b( */ unsigned int data) { unsigned int token = (1 << HIVE_STR_TO_MIPI_VALID_B_BIT) | (data << _HIVE_STR_TO_MIPI_DATA_B_LSB); _sh_css_fifo_snd(token); return; } static void inputfifo_send_data( /* static inline void inputfifo_send_data( */ unsigned int a, unsigned int b) { unsigned int token = ((1 << HIVE_STR_TO_MIPI_VALID_A_BIT) | (1 << HIVE_STR_TO_MIPI_VALID_B_BIT) | (a << HIVE_STR_TO_MIPI_DATA_A_LSB) | (b << _HIVE_STR_TO_MIPI_DATA_B_LSB)); _sh_css_fifo_snd(token); return; } static void inputfifo_send_sol(void) /* static inline void inputfifo_send_sol(void) */ { hrt_data token = inputfifo_wrap_marker( 1 << HIVE_STR_TO_MIPI_SOL_BIT); _sh_css_fifo_snd(token); return; } static void inputfifo_send_eol(void) /* static inline void inputfifo_send_eol(void) */ { hrt_data token = inputfifo_wrap_marker( 1 << HIVE_STR_TO_MIPI_EOL_BIT); _sh_css_fifo_snd(token); return; } static void inputfifo_send_sof(void) /* static inline void inputfifo_send_sof(void) */ { hrt_data token = inputfifo_wrap_marker( 1 << HIVE_STR_TO_MIPI_SOF_BIT); _sh_css_fifo_snd(token); return; } static void inputfifo_send_eof(void) /* static inline void inputfifo_send_eof(void) */ { hrt_data token = inputfifo_wrap_marker( 1 << HIVE_STR_TO_MIPI_EOF_BIT); _sh_css_fifo_snd(token); return; } static void inputfifo_send_ch_id_and_fmt_type( /* static inline void inputfifo_send_ch_id_and_fmt_type( */ unsigned int ch_id, unsigned int fmt_type) { hrt_data token; inputfifo_curr_ch_id = ch_id & _HIVE_ISP_CH_ID_MASK; inputfifo_curr_fmt_type = fmt_type & _HIVE_ISP_FMT_TYPE_MASK; /* we send an zero marker, this will wrap the ch_id and * fmt_type automatically. */ token = inputfifo_wrap_marker(0); _sh_css_fifo_snd(token); return; } static void inputfifo_send_empty_token(void) /* static inline void inputfifo_send_empty_token(void) */ { hrt_data token = inputfifo_wrap_marker(0); _sh_css_fifo_snd(token); return; } static void inputfifo_start_frame( /* static inline void inputfifo_start_frame( */ unsigned int ch_id, unsigned int fmt_type) { inputfifo_send_ch_id_and_fmt_type(ch_id, fmt_type); inputfifo_send_sof(); return; } static void inputfifo_end_frame( unsigned int marker_cycles) { unsigned int i; for (i = 0; i < marker_cycles; i++) inputfifo_send_empty_token(); inputfifo_send_eof(); return; } static void inputfifo_send_line2( const unsigned short *data, unsigned int width, const unsigned short *data2, unsigned int width2, unsigned int hblank_cycles, unsigned int marker_cycles, unsigned int two_ppc, enum inputfifo_mipi_data_type type) { unsigned int i, is_rgb = 0, is_legacy = 0; assert(data); assert((data2) || (width2 == 0)); if (type == inputfifo_mipi_data_type_rgb) is_rgb = 1; if (type == inputfifo_mipi_data_type_yuv420_legacy) is_legacy = 1; for (i = 0; i < hblank_cycles; i++) inputfifo_send_empty_token(); inputfifo_send_sol(); for (i = 0; i < marker_cycles; i++) inputfifo_send_empty_token(); for (i = 0; i < width; i++, data++) { /* for RGB in two_ppc, we only actually send 2 pixels per * clock in the even pixels (0, 2 etc). In the other cycles, * we only send 1 pixel, to data[0]. */ unsigned int send_two_pixels = two_ppc; if ((is_rgb || is_legacy) && (i % 3 == 2)) send_two_pixels = 0; if (send_two_pixels) { if (i + 1 == width) { /* for jpg (binary) copy, this can occur * if the file contains an odd number of bytes. */ inputfifo_send_data( data[0], 0); } else { inputfifo_send_data( data[0], data[1]); } /* Additional increment because we send 2 pixels */ data++; i++; } else if (two_ppc && is_legacy) { inputfifo_send_data_b(data[0]); } else { inputfifo_send_data_a(data[0]); } } for (i = 0; i < width2; i++, data2++) { /* for RGB in two_ppc, we only actually send 2 pixels per * clock in the even pixels (0, 2 etc). In the other cycles, * we only send 1 pixel, to data2[0]. */ unsigned int send_two_pixels = two_ppc; if ((is_rgb || is_legacy) && (i % 3 == 2)) send_two_pixels = 0; if (send_two_pixels) { if (i + 1 == width2) { /* for jpg (binary) copy, this can occur * if the file contains an odd number of bytes. */ inputfifo_send_data( data2[0], 0); } else { inputfifo_send_data( data2[0], data2[1]); } /* Additional increment because we send 2 pixels */ data2++; i++; } else if (two_ppc && is_legacy) { inputfifo_send_data_b(data2[0]); } else { inputfifo_send_data_a(data2[0]); } } for (i = 0; i < hblank_cycles; i++) inputfifo_send_empty_token(); inputfifo_send_eol(); return; } static void inputfifo_send_line(const unsigned short *data, unsigned int width, unsigned int hblank_cycles, unsigned int marker_cycles, unsigned int two_ppc, enum inputfifo_mipi_data_type type) { assert(data); inputfifo_send_line2(data, width, NULL, 0, hblank_cycles, marker_cycles, two_ppc, type); } /* Send a frame of data into the input network via the GP FIFO. * Parameters: * - data: array of 16 bit values that contains all data for the frame. * - width: width of a line in number of subpixels, for yuv420 it is the * number of Y components per line. * - height: height of the frame in number of lines. * - ch_id: channel ID. * - fmt_type: format type. * - hblank_cycles: length of horizontal blanking in cycles. * - marker_cycles: number of empty cycles after start-of-line and before * end-of-frame. * - two_ppc: boolean, describes whether to send one or two pixels per clock * cycle. In this mode, we sent pixels N and N+1 in the same cycle, * to IF_PRIM_A and IF_PRIM_B respectively. The caller must make * sure the input data has been formatted correctly for this. * For example, for RGB formats this means that unused values * must be inserted. * - yuv420: boolean, describes whether (non-legacy) yuv420 data is used. In * this mode, the odd lines (1,3,5 etc) are half as long as the * even lines (2,4,6 etc). * Note that the first line is odd (1) and the second line is even * (2). * * This function does not do any reordering of pixels, the caller must make * sure the data is in the righ format. Please refer to the CSS receiver * documentation for details on the data formats. */ static void inputfifo_send_frame( const unsigned short *data, unsigned int width, unsigned int height, unsigned int ch_id, unsigned int fmt_type, unsigned int hblank_cycles, unsigned int marker_cycles, unsigned int two_ppc, enum inputfifo_mipi_data_type type) { unsigned int i; assert(data); inputfifo_start_frame(ch_id, fmt_type); for (i = 0; i < height; i++) { if ((type == inputfifo_mipi_data_type_yuv420) && (i & 1) == 1) { inputfifo_send_line(data, 2 * width, hblank_cycles, marker_cycles, two_ppc, type); data += 2 * width; } else { inputfifo_send_line(data, width, hblank_cycles, marker_cycles, two_ppc, type); data += width; } } inputfifo_end_frame(marker_cycles); return; } static enum inputfifo_mipi_data_type inputfifo_determine_type( enum atomisp_input_format input_format) { enum inputfifo_mipi_data_type type; type = inputfifo_mipi_data_type_regular; if (input_format == ATOMISP_INPUT_FORMAT_YUV420_8_LEGACY) { type = inputfifo_mipi_data_type_yuv420_legacy; } else if (input_format == ATOMISP_INPUT_FORMAT_YUV420_8 || input_format == ATOMISP_INPUT_FORMAT_YUV420_10 || input_format == ATOMISP_INPUT_FORMAT_YUV420_16) { type = inputfifo_mipi_data_type_yuv420; } else if (input_format >= ATOMISP_INPUT_FORMAT_RGB_444 && input_format <= ATOMISP_INPUT_FORMAT_RGB_888) { type = inputfifo_mipi_data_type_rgb; } return type; } static struct inputfifo_instance *inputfifo_get_inst( unsigned int ch_id) { return &inputfifo_inst_admin[ch_id]; } void ia_css_inputfifo_send_input_frame( const unsigned short *data, unsigned int width, unsigned int height, unsigned int ch_id, enum atomisp_input_format input_format, bool two_ppc) { unsigned int fmt_type, hblank_cycles, marker_cycles; enum inputfifo_mipi_data_type type; assert(data); hblank_cycles = HBLANK_CYCLES; marker_cycles = MARKER_CYCLES; ia_css_isys_convert_stream_format_to_mipi_format(input_format, MIPI_PREDICTOR_NONE, &fmt_type); type = inputfifo_determine_type(input_format); inputfifo_send_frame(data, width, height, ch_id, fmt_type, hblank_cycles, marker_cycles, two_ppc, type); } void ia_css_inputfifo_start_frame( unsigned int ch_id, enum atomisp_input_format input_format, bool two_ppc) { struct inputfifo_instance *s2mi; s2mi = inputfifo_get_inst(ch_id); s2mi->ch_id = ch_id; ia_css_isys_convert_stream_format_to_mipi_format(input_format, MIPI_PREDICTOR_NONE, &s2mi->fmt_type); s2mi->two_ppc = two_ppc; s2mi->type = inputfifo_determine_type(input_format); s2mi->hblank_cycles = HBLANK_CYCLES; s2mi->marker_cycles = MARKER_CYCLES; s2mi->streaming = true; inputfifo_start_frame(ch_id, s2mi->fmt_type); return; } void ia_css_inputfifo_send_line( unsigned int ch_id, const unsigned short *data, unsigned int width, const unsigned short *data2, unsigned int width2) { struct inputfifo_instance *s2mi; assert(data); assert((data2) || (width2 == 0)); s2mi = inputfifo_get_inst(ch_id); /* Set global variables that indicate channel_id and format_type */ inputfifo_curr_ch_id = (s2mi->ch_id) & _HIVE_ISP_CH_ID_MASK; inputfifo_curr_fmt_type = (s2mi->fmt_type) & _HIVE_ISP_FMT_TYPE_MASK; inputfifo_send_line2(data, width, data2, width2, s2mi->hblank_cycles, s2mi->marker_cycles, s2mi->two_ppc, s2mi->type); } void ia_css_inputfifo_send_embedded_line( unsigned int ch_id, enum atomisp_input_format data_type, const unsigned short *data, unsigned int width) { struct inputfifo_instance *s2mi; unsigned int fmt_type; assert(data); s2mi = inputfifo_get_inst(ch_id); ia_css_isys_convert_stream_format_to_mipi_format(data_type, MIPI_PREDICTOR_NONE, &fmt_type); /* Set format_type for metadata line. */ inputfifo_curr_fmt_type = fmt_type & _HIVE_ISP_FMT_TYPE_MASK; inputfifo_send_line(data, width, s2mi->hblank_cycles, s2mi->marker_cycles, s2mi->two_ppc, inputfifo_mipi_data_type_regular); } void ia_css_inputfifo_end_frame( unsigned int ch_id) { struct inputfifo_instance *s2mi; s2mi = inputfifo_get_inst(ch_id); /* Set global variables that indicate channel_id and format_type */ inputfifo_curr_ch_id = (s2mi->ch_id) & _HIVE_ISP_CH_ID_MASK; inputfifo_curr_fmt_type = (s2mi->fmt_type) & _HIVE_ISP_FMT_TYPE_MASK; /* Call existing HRT function */ inputfifo_end_frame(s2mi->marker_cycles); s2mi->streaming = false; return; }