1/*
2 * soc-camera media bus helper routines
3 *
4 * Copyright (C) 2009, Guennadi Liakhovetski <g.liakhovetski@gmx.de>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 as
8 * published by the Free Software Foundation.
9 */
10
11#include <linux/kernel.h>
12#include <linux/module.h>
13
14#include <media/v4l2-device.h>
15#include <media/v4l2-mediabus.h>
16#include <media/soc_mediabus.h>
17
18static const struct soc_mbus_lookup mbus_fmt[] = {
19{
20	.code = MEDIA_BUS_FMT_YUYV8_2X8,
21	.fmt = {
22		.fourcc			= V4L2_PIX_FMT_YUYV,
23		.name			= "YUYV",
24		.bits_per_sample	= 8,
25		.packing		= SOC_MBUS_PACKING_2X8_PADHI,
26		.order			= SOC_MBUS_ORDER_LE,
27		.layout			= SOC_MBUS_LAYOUT_PACKED,
28	},
29}, {
30	.code = MEDIA_BUS_FMT_YVYU8_2X8,
31	.fmt = {
32		.fourcc			= V4L2_PIX_FMT_YVYU,
33		.name			= "YVYU",
34		.bits_per_sample	= 8,
35		.packing		= SOC_MBUS_PACKING_2X8_PADHI,
36		.order			= SOC_MBUS_ORDER_LE,
37		.layout			= SOC_MBUS_LAYOUT_PACKED,
38	},
39}, {
40	.code = MEDIA_BUS_FMT_UYVY8_2X8,
41	.fmt = {
42		.fourcc			= V4L2_PIX_FMT_UYVY,
43		.name			= "UYVY",
44		.bits_per_sample	= 8,
45		.packing		= SOC_MBUS_PACKING_2X8_PADHI,
46		.order			= SOC_MBUS_ORDER_LE,
47		.layout			= SOC_MBUS_LAYOUT_PACKED,
48	},
49}, {
50	.code = MEDIA_BUS_FMT_VYUY8_2X8,
51	.fmt = {
52		.fourcc			= V4L2_PIX_FMT_VYUY,
53		.name			= "VYUY",
54		.bits_per_sample	= 8,
55		.packing		= SOC_MBUS_PACKING_2X8_PADHI,
56		.order			= SOC_MBUS_ORDER_LE,
57		.layout			= SOC_MBUS_LAYOUT_PACKED,
58	},
59}, {
60	.code = MEDIA_BUS_FMT_RGB555_2X8_PADHI_LE,
61	.fmt = {
62		.fourcc			= V4L2_PIX_FMT_RGB555,
63		.name			= "RGB555",
64		.bits_per_sample	= 8,
65		.packing		= SOC_MBUS_PACKING_2X8_PADHI,
66		.order			= SOC_MBUS_ORDER_LE,
67		.layout			= SOC_MBUS_LAYOUT_PACKED,
68	},
69}, {
70	.code = MEDIA_BUS_FMT_RGB555_2X8_PADHI_BE,
71	.fmt = {
72		.fourcc			= V4L2_PIX_FMT_RGB555X,
73		.name			= "RGB555X",
74		.bits_per_sample	= 8,
75		.packing		= SOC_MBUS_PACKING_2X8_PADHI,
76		.order			= SOC_MBUS_ORDER_BE,
77		.layout			= SOC_MBUS_LAYOUT_PACKED,
78	},
79}, {
80	.code = MEDIA_BUS_FMT_RGB565_2X8_LE,
81	.fmt = {
82		.fourcc			= V4L2_PIX_FMT_RGB565,
83		.name			= "RGB565",
84		.bits_per_sample	= 8,
85		.packing		= SOC_MBUS_PACKING_2X8_PADHI,
86		.order			= SOC_MBUS_ORDER_LE,
87		.layout			= SOC_MBUS_LAYOUT_PACKED,
88	},
89}, {
90	.code = MEDIA_BUS_FMT_RGB565_2X8_BE,
91	.fmt = {
92		.fourcc			= V4L2_PIX_FMT_RGB565X,
93		.name			= "RGB565X",
94		.bits_per_sample	= 8,
95		.packing		= SOC_MBUS_PACKING_2X8_PADHI,
96		.order			= SOC_MBUS_ORDER_BE,
97		.layout			= SOC_MBUS_LAYOUT_PACKED,
98	},
99}, {
100	.code = MEDIA_BUS_FMT_RGB666_1X18,
101	.fmt = {
102		.fourcc			= V4L2_PIX_FMT_RGB32,
103		.name			= "RGB666/32bpp",
104		.bits_per_sample	= 18,
105		.packing		= SOC_MBUS_PACKING_EXTEND32,
106		.order			= SOC_MBUS_ORDER_LE,
107	},
108}, {
109	.code = MEDIA_BUS_FMT_RGB888_1X24,
110	.fmt = {
111		.fourcc			= V4L2_PIX_FMT_RGB32,
112		.name			= "RGB888/32bpp",
113		.bits_per_sample	= 24,
114		.packing		= SOC_MBUS_PACKING_EXTEND32,
115		.order			= SOC_MBUS_ORDER_LE,
116	},
117}, {
118	.code = MEDIA_BUS_FMT_RGB888_2X12_BE,
119	.fmt = {
120		.fourcc			= V4L2_PIX_FMT_RGB32,
121		.name			= "RGB888/32bpp",
122		.bits_per_sample	= 12,
123		.packing		= SOC_MBUS_PACKING_EXTEND32,
124		.order			= SOC_MBUS_ORDER_BE,
125	},
126}, {
127	.code = MEDIA_BUS_FMT_RGB888_2X12_LE,
128	.fmt = {
129		.fourcc			= V4L2_PIX_FMT_RGB32,
130		.name			= "RGB888/32bpp",
131		.bits_per_sample	= 12,
132		.packing		= SOC_MBUS_PACKING_EXTEND32,
133		.order			= SOC_MBUS_ORDER_LE,
134	},
135}, {
136	.code = MEDIA_BUS_FMT_SBGGR8_1X8,
137	.fmt = {
138		.fourcc			= V4L2_PIX_FMT_SBGGR8,
139		.name			= "Bayer 8 BGGR",
140		.bits_per_sample	= 8,
141		.packing		= SOC_MBUS_PACKING_NONE,
142		.order			= SOC_MBUS_ORDER_LE,
143		.layout			= SOC_MBUS_LAYOUT_PACKED,
144	},
145}, {
146	.code = MEDIA_BUS_FMT_SBGGR10_1X10,
147	.fmt = {
148		.fourcc			= V4L2_PIX_FMT_SBGGR10,
149		.name			= "Bayer 10 BGGR",
150		.bits_per_sample	= 10,
151		.packing		= SOC_MBUS_PACKING_EXTEND16,
152		.order			= SOC_MBUS_ORDER_LE,
153		.layout			= SOC_MBUS_LAYOUT_PACKED,
154	},
155}, {
156	.code = MEDIA_BUS_FMT_Y8_1X8,
157	.fmt = {
158		.fourcc			= V4L2_PIX_FMT_GREY,
159		.name			= "Grey",
160		.bits_per_sample	= 8,
161		.packing		= SOC_MBUS_PACKING_NONE,
162		.order			= SOC_MBUS_ORDER_LE,
163		.layout			= SOC_MBUS_LAYOUT_PACKED,
164	},
165}, {
166	.code = MEDIA_BUS_FMT_Y10_1X10,
167	.fmt = {
168		.fourcc			= V4L2_PIX_FMT_Y10,
169		.name			= "Grey 10bit",
170		.bits_per_sample	= 10,
171		.packing		= SOC_MBUS_PACKING_EXTEND16,
172		.order			= SOC_MBUS_ORDER_LE,
173		.layout			= SOC_MBUS_LAYOUT_PACKED,
174	},
175}, {
176	.code = MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_LE,
177	.fmt = {
178		.fourcc			= V4L2_PIX_FMT_SBGGR10,
179		.name			= "Bayer 10 BGGR",
180		.bits_per_sample	= 8,
181		.packing		= SOC_MBUS_PACKING_2X8_PADHI,
182		.order			= SOC_MBUS_ORDER_LE,
183		.layout			= SOC_MBUS_LAYOUT_PACKED,
184	},
185}, {
186	.code = MEDIA_BUS_FMT_SBGGR10_2X8_PADLO_LE,
187	.fmt = {
188		.fourcc			= V4L2_PIX_FMT_SBGGR10,
189		.name			= "Bayer 10 BGGR",
190		.bits_per_sample	= 8,
191		.packing		= SOC_MBUS_PACKING_2X8_PADLO,
192		.order			= SOC_MBUS_ORDER_LE,
193		.layout			= SOC_MBUS_LAYOUT_PACKED,
194	},
195}, {
196	.code = MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_BE,
197	.fmt = {
198		.fourcc			= V4L2_PIX_FMT_SBGGR10,
199		.name			= "Bayer 10 BGGR",
200		.bits_per_sample	= 8,
201		.packing		= SOC_MBUS_PACKING_2X8_PADHI,
202		.order			= SOC_MBUS_ORDER_BE,
203		.layout			= SOC_MBUS_LAYOUT_PACKED,
204	},
205}, {
206	.code = MEDIA_BUS_FMT_SBGGR10_2X8_PADLO_BE,
207	.fmt = {
208		.fourcc			= V4L2_PIX_FMT_SBGGR10,
209		.name			= "Bayer 10 BGGR",
210		.bits_per_sample	= 8,
211		.packing		= SOC_MBUS_PACKING_2X8_PADLO,
212		.order			= SOC_MBUS_ORDER_BE,
213		.layout			= SOC_MBUS_LAYOUT_PACKED,
214	},
215}, {
216	.code = MEDIA_BUS_FMT_JPEG_1X8,
217	.fmt = {
218		.fourcc                 = V4L2_PIX_FMT_JPEG,
219		.name                   = "JPEG",
220		.bits_per_sample        = 8,
221		.packing                = SOC_MBUS_PACKING_VARIABLE,
222		.order                  = SOC_MBUS_ORDER_LE,
223		.layout			= SOC_MBUS_LAYOUT_PACKED,
224	},
225}, {
226	.code = MEDIA_BUS_FMT_RGB444_2X8_PADHI_BE,
227	.fmt = {
228		.fourcc			= V4L2_PIX_FMT_RGB444,
229		.name			= "RGB444",
230		.bits_per_sample	= 8,
231		.packing		= SOC_MBUS_PACKING_2X8_PADHI,
232		.order			= SOC_MBUS_ORDER_BE,
233		.layout			= SOC_MBUS_LAYOUT_PACKED,
234	},
235}, {
236	.code = MEDIA_BUS_FMT_YUYV8_1_5X8,
237	.fmt = {
238		.fourcc			= V4L2_PIX_FMT_YUV420,
239		.name			= "YUYV 4:2:0",
240		.bits_per_sample	= 8,
241		.packing		= SOC_MBUS_PACKING_1_5X8,
242		.order			= SOC_MBUS_ORDER_LE,
243		.layout			= SOC_MBUS_LAYOUT_PACKED,
244	},
245}, {
246	.code = MEDIA_BUS_FMT_YVYU8_1_5X8,
247	.fmt = {
248		.fourcc			= V4L2_PIX_FMT_YVU420,
249		.name			= "YVYU 4:2:0",
250		.bits_per_sample	= 8,
251		.packing		= SOC_MBUS_PACKING_1_5X8,
252		.order			= SOC_MBUS_ORDER_LE,
253		.layout			= SOC_MBUS_LAYOUT_PACKED,
254	},
255}, {
256	.code = MEDIA_BUS_FMT_UYVY8_1X16,
257	.fmt = {
258		.fourcc			= V4L2_PIX_FMT_UYVY,
259		.name			= "UYVY 16bit",
260		.bits_per_sample	= 16,
261		.packing		= SOC_MBUS_PACKING_EXTEND16,
262		.order			= SOC_MBUS_ORDER_LE,
263		.layout			= SOC_MBUS_LAYOUT_PACKED,
264	},
265}, {
266	.code = MEDIA_BUS_FMT_VYUY8_1X16,
267	.fmt = {
268		.fourcc			= V4L2_PIX_FMT_VYUY,
269		.name			= "VYUY 16bit",
270		.bits_per_sample	= 16,
271		.packing		= SOC_MBUS_PACKING_EXTEND16,
272		.order			= SOC_MBUS_ORDER_LE,
273		.layout			= SOC_MBUS_LAYOUT_PACKED,
274	},
275}, {
276	.code = MEDIA_BUS_FMT_YUYV8_1X16,
277	.fmt = {
278		.fourcc			= V4L2_PIX_FMT_YUYV,
279		.name			= "YUYV 16bit",
280		.bits_per_sample	= 16,
281		.packing		= SOC_MBUS_PACKING_EXTEND16,
282		.order			= SOC_MBUS_ORDER_LE,
283		.layout			= SOC_MBUS_LAYOUT_PACKED,
284	},
285}, {
286	.code = MEDIA_BUS_FMT_YVYU8_1X16,
287	.fmt = {
288		.fourcc			= V4L2_PIX_FMT_YVYU,
289		.name			= "YVYU 16bit",
290		.bits_per_sample	= 16,
291		.packing		= SOC_MBUS_PACKING_EXTEND16,
292		.order			= SOC_MBUS_ORDER_LE,
293		.layout			= SOC_MBUS_LAYOUT_PACKED,
294	},
295}, {
296	.code = MEDIA_BUS_FMT_SGRBG8_1X8,
297	.fmt = {
298		.fourcc			= V4L2_PIX_FMT_SGRBG8,
299		.name			= "Bayer 8 GRBG",
300		.bits_per_sample	= 8,
301		.packing		= SOC_MBUS_PACKING_NONE,
302		.order			= SOC_MBUS_ORDER_LE,
303		.layout			= SOC_MBUS_LAYOUT_PACKED,
304	},
305}, {
306	.code = MEDIA_BUS_FMT_SGRBG10_DPCM8_1X8,
307	.fmt = {
308		.fourcc			= V4L2_PIX_FMT_SGRBG10DPCM8,
309		.name			= "Bayer 10 BGGR DPCM 8",
310		.bits_per_sample	= 8,
311		.packing		= SOC_MBUS_PACKING_NONE,
312		.order			= SOC_MBUS_ORDER_LE,
313		.layout			= SOC_MBUS_LAYOUT_PACKED,
314	},
315}, {
316	.code = MEDIA_BUS_FMT_SGBRG10_1X10,
317	.fmt = {
318		.fourcc			= V4L2_PIX_FMT_SGBRG10,
319		.name			= "Bayer 10 GBRG",
320		.bits_per_sample	= 10,
321		.packing		= SOC_MBUS_PACKING_EXTEND16,
322		.order			= SOC_MBUS_ORDER_LE,
323		.layout			= SOC_MBUS_LAYOUT_PACKED,
324	},
325}, {
326	.code = MEDIA_BUS_FMT_SGRBG10_1X10,
327	.fmt = {
328		.fourcc			= V4L2_PIX_FMT_SGRBG10,
329		.name			= "Bayer 10 GRBG",
330		.bits_per_sample	= 10,
331		.packing		= SOC_MBUS_PACKING_EXTEND16,
332		.order			= SOC_MBUS_ORDER_LE,
333		.layout			= SOC_MBUS_LAYOUT_PACKED,
334	},
335}, {
336	.code = MEDIA_BUS_FMT_SRGGB10_1X10,
337	.fmt = {
338		.fourcc			= V4L2_PIX_FMT_SRGGB10,
339		.name			= "Bayer 10 RGGB",
340		.bits_per_sample	= 10,
341		.packing		= SOC_MBUS_PACKING_EXTEND16,
342		.order			= SOC_MBUS_ORDER_LE,
343		.layout			= SOC_MBUS_LAYOUT_PACKED,
344	},
345}, {
346	.code = MEDIA_BUS_FMT_SBGGR12_1X12,
347	.fmt = {
348		.fourcc			= V4L2_PIX_FMT_SBGGR12,
349		.name			= "Bayer 12 BGGR",
350		.bits_per_sample	= 12,
351		.packing		= SOC_MBUS_PACKING_EXTEND16,
352		.order			= SOC_MBUS_ORDER_LE,
353		.layout			= SOC_MBUS_LAYOUT_PACKED,
354	},
355}, {
356	.code = MEDIA_BUS_FMT_SGBRG12_1X12,
357	.fmt = {
358		.fourcc			= V4L2_PIX_FMT_SGBRG12,
359		.name			= "Bayer 12 GBRG",
360		.bits_per_sample	= 12,
361		.packing		= SOC_MBUS_PACKING_EXTEND16,
362		.order			= SOC_MBUS_ORDER_LE,
363		.layout			= SOC_MBUS_LAYOUT_PACKED,
364	},
365}, {
366	.code = MEDIA_BUS_FMT_SGRBG12_1X12,
367	.fmt = {
368		.fourcc			= V4L2_PIX_FMT_SGRBG12,
369		.name			= "Bayer 12 GRBG",
370		.bits_per_sample	= 12,
371		.packing		= SOC_MBUS_PACKING_EXTEND16,
372		.order			= SOC_MBUS_ORDER_LE,
373		.layout			= SOC_MBUS_LAYOUT_PACKED,
374	},
375}, {
376	.code = MEDIA_BUS_FMT_SRGGB12_1X12,
377	.fmt = {
378		.fourcc			= V4L2_PIX_FMT_SRGGB12,
379		.name			= "Bayer 12 RGGB",
380		.bits_per_sample	= 12,
381		.packing		= SOC_MBUS_PACKING_EXTEND16,
382		.order			= SOC_MBUS_ORDER_LE,
383		.layout			= SOC_MBUS_LAYOUT_PACKED,
384	},
385},
386};
387
388int soc_mbus_samples_per_pixel(const struct soc_mbus_pixelfmt *mf,
389			unsigned int *numerator, unsigned int *denominator)
390{
391	switch (mf->packing) {
392	case SOC_MBUS_PACKING_NONE:
393	case SOC_MBUS_PACKING_EXTEND16:
394		*numerator = 1;
395		*denominator = 1;
396		return 0;
397	case SOC_MBUS_PACKING_EXTEND32:
398		*numerator = 1;
399		*denominator = 1;
400		return 0;
401	case SOC_MBUS_PACKING_2X8_PADHI:
402	case SOC_MBUS_PACKING_2X8_PADLO:
403		*numerator = 2;
404		*denominator = 1;
405		return 0;
406	case SOC_MBUS_PACKING_1_5X8:
407		*numerator = 3;
408		*denominator = 2;
409		return 0;
410	case SOC_MBUS_PACKING_VARIABLE:
411		*numerator = 0;
412		*denominator = 1;
413		return 0;
414	}
415	return -EINVAL;
416}
417EXPORT_SYMBOL(soc_mbus_samples_per_pixel);
418
419s32 soc_mbus_bytes_per_line(u32 width, const struct soc_mbus_pixelfmt *mf)
420{
421	if (mf->layout != SOC_MBUS_LAYOUT_PACKED)
422		return width * mf->bits_per_sample / 8;
423
424	switch (mf->packing) {
425	case SOC_MBUS_PACKING_NONE:
426		return width * mf->bits_per_sample / 8;
427	case SOC_MBUS_PACKING_2X8_PADHI:
428	case SOC_MBUS_PACKING_2X8_PADLO:
429	case SOC_MBUS_PACKING_EXTEND16:
430		return width * 2;
431	case SOC_MBUS_PACKING_1_5X8:
432		return width * 3 / 2;
433	case SOC_MBUS_PACKING_VARIABLE:
434		return 0;
435	case SOC_MBUS_PACKING_EXTEND32:
436		return width * 4;
437	}
438	return -EINVAL;
439}
440EXPORT_SYMBOL(soc_mbus_bytes_per_line);
441
442s32 soc_mbus_image_size(const struct soc_mbus_pixelfmt *mf,
443			u32 bytes_per_line, u32 height)
444{
445	if (mf->layout == SOC_MBUS_LAYOUT_PACKED)
446		return bytes_per_line * height;
447
448	switch (mf->packing) {
449	case SOC_MBUS_PACKING_2X8_PADHI:
450	case SOC_MBUS_PACKING_2X8_PADLO:
451		return bytes_per_line * height * 2;
452	case SOC_MBUS_PACKING_1_5X8:
453		return bytes_per_line * height * 3 / 2;
454	default:
455		return -EINVAL;
456	}
457}
458EXPORT_SYMBOL(soc_mbus_image_size);
459
460const struct soc_mbus_pixelfmt *soc_mbus_find_fmtdesc(
461	u32 code,
462	const struct soc_mbus_lookup *lookup,
463	int n)
464{
465	int i;
466
467	for (i = 0; i < n; i++)
468		if (lookup[i].code == code)
469			return &lookup[i].fmt;
470
471	return NULL;
472}
473EXPORT_SYMBOL(soc_mbus_find_fmtdesc);
474
475const struct soc_mbus_pixelfmt *soc_mbus_get_fmtdesc(
476	u32 code)
477{
478	return soc_mbus_find_fmtdesc(code, mbus_fmt, ARRAY_SIZE(mbus_fmt));
479}
480EXPORT_SYMBOL(soc_mbus_get_fmtdesc);
481
482unsigned int soc_mbus_config_compatible(const struct v4l2_mbus_config *cfg,
483					unsigned int flags)
484{
485	unsigned long common_flags;
486	bool hsync = true, vsync = true, pclk, data, mode;
487	bool mipi_lanes, mipi_clock;
488
489	common_flags = cfg->flags & flags;
490
491	switch (cfg->type) {
492	case V4L2_MBUS_PARALLEL:
493		hsync = common_flags & (V4L2_MBUS_HSYNC_ACTIVE_HIGH |
494					V4L2_MBUS_HSYNC_ACTIVE_LOW);
495		vsync = common_flags & (V4L2_MBUS_VSYNC_ACTIVE_HIGH |
496					V4L2_MBUS_VSYNC_ACTIVE_LOW);
497	case V4L2_MBUS_BT656:
498		pclk = common_flags & (V4L2_MBUS_PCLK_SAMPLE_RISING |
499				       V4L2_MBUS_PCLK_SAMPLE_FALLING);
500		data = common_flags & (V4L2_MBUS_DATA_ACTIVE_HIGH |
501				       V4L2_MBUS_DATA_ACTIVE_LOW);
502		mode = common_flags & (V4L2_MBUS_MASTER | V4L2_MBUS_SLAVE);
503		return (!hsync || !vsync || !pclk || !data || !mode) ?
504			0 : common_flags;
505	case V4L2_MBUS_CSI2:
506		mipi_lanes = common_flags & V4L2_MBUS_CSI2_LANES;
507		mipi_clock = common_flags & (V4L2_MBUS_CSI2_NONCONTINUOUS_CLOCK |
508					     V4L2_MBUS_CSI2_CONTINUOUS_CLOCK);
509		return (!mipi_lanes || !mipi_clock) ? 0 : common_flags;
510	}
511	return 0;
512}
513EXPORT_SYMBOL(soc_mbus_config_compatible);
514
515static int __init soc_mbus_init(void)
516{
517	return 0;
518}
519
520static void __exit soc_mbus_exit(void)
521{
522}
523
524module_init(soc_mbus_init);
525module_exit(soc_mbus_exit);
526
527MODULE_DESCRIPTION("soc-camera media bus interface");
528MODULE_AUTHOR("Guennadi Liakhovetski <g.liakhovetski@gmx.de>");
529MODULE_LICENSE("GPL v2");
530