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 */ 57 enum 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 64 static unsigned int inputfifo_curr_ch_id, inputfifo_curr_fmt_type; 65 struct 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) 81 static struct inputfifo_instance 82 inputfifo_inst_admin[INPUTFIFO_NR_OF_S2M_CHANNELS]; 83 84 /* Streaming to MIPI */ 85 static 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 94 static 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 103 static 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 113 static 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 123 static 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 136 static 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 146 static 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 155 static 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 165 static 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 174 static 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 192 static 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 201 static 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 211 static 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 222 static 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 312 static void 313 inputfifo_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 356 static 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 392 static 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 414 static struct inputfifo_instance *inputfifo_get_inst( 415 unsigned int ch_id) 416 { 417 return &inputfifo_inst_admin[ch_id]; 418 } 419 420 void 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 445 void 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 468 void 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 492 void 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 513 void 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