// 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 <hive_isp_css_streaming_to_mipi_types_hrt.h>
|
|
/* 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;
|
}
|