18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (C) STMicroelectronics SA 2013
48c2ecf20Sopenharmony_ci * Author: Hugues Fruchet <hugues.fruchet@st.com> for STMicroelectronics.
58c2ecf20Sopenharmony_ci */
68c2ecf20Sopenharmony_ci
78c2ecf20Sopenharmony_ci#include "delta.h"
88c2ecf20Sopenharmony_ci#include "delta-mjpeg.h"
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#define MJPEG_SOF_0  0xc0
118c2ecf20Sopenharmony_ci#define MJPEG_SOF_1  0xc1
128c2ecf20Sopenharmony_ci#define MJPEG_SOI    0xd8
138c2ecf20Sopenharmony_ci#define MJPEG_MARKER 0xff
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_cistatic char *header_str(struct mjpeg_header *header,
168c2ecf20Sopenharmony_ci			char *str,
178c2ecf20Sopenharmony_ci			unsigned int len)
188c2ecf20Sopenharmony_ci{
198c2ecf20Sopenharmony_ci	char *cur = str;
208c2ecf20Sopenharmony_ci	unsigned int left = len;
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci	if (!header)
238c2ecf20Sopenharmony_ci		return "";
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci	snprintf(cur, left, "[MJPEG header]\n"
268c2ecf20Sopenharmony_ci			"|- length     = %d\n"
278c2ecf20Sopenharmony_ci			"|- precision  = %d\n"
288c2ecf20Sopenharmony_ci			"|- width      = %d\n"
298c2ecf20Sopenharmony_ci			"|- height     = %d\n"
308c2ecf20Sopenharmony_ci			"|- components = %d\n",
318c2ecf20Sopenharmony_ci			header->length,
328c2ecf20Sopenharmony_ci			header->sample_precision,
338c2ecf20Sopenharmony_ci			header->frame_width,
348c2ecf20Sopenharmony_ci			header->frame_height,
358c2ecf20Sopenharmony_ci			header->nb_of_components);
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci	return str;
388c2ecf20Sopenharmony_ci}
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_cistatic int delta_mjpeg_read_sof(struct delta_ctx *pctx,
418c2ecf20Sopenharmony_ci				unsigned char *data, unsigned int size,
428c2ecf20Sopenharmony_ci				struct mjpeg_header *header)
438c2ecf20Sopenharmony_ci{
448c2ecf20Sopenharmony_ci	struct delta_dev *delta = pctx->dev;
458c2ecf20Sopenharmony_ci	unsigned int offset = 0;
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci	if (size < 64)
488c2ecf20Sopenharmony_ci		goto err_no_more;
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci	memset(header, 0, sizeof(*header));
518c2ecf20Sopenharmony_ci	header->length           = be16_to_cpu(*(__be16 *)(data + offset));
528c2ecf20Sopenharmony_ci	offset += sizeof(u16);
538c2ecf20Sopenharmony_ci	header->sample_precision = *(u8 *)(data + offset);
548c2ecf20Sopenharmony_ci	offset += sizeof(u8);
558c2ecf20Sopenharmony_ci	header->frame_height     = be16_to_cpu(*(__be16 *)(data + offset));
568c2ecf20Sopenharmony_ci	offset += sizeof(u16);
578c2ecf20Sopenharmony_ci	header->frame_width      = be16_to_cpu(*(__be16 *)(data + offset));
588c2ecf20Sopenharmony_ci	offset += sizeof(u16);
598c2ecf20Sopenharmony_ci	header->nb_of_components = *(u8 *)(data + offset);
608c2ecf20Sopenharmony_ci	offset += sizeof(u8);
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci	if (header->nb_of_components >= MJPEG_MAX_COMPONENTS) {
638c2ecf20Sopenharmony_ci		dev_err(delta->dev,
648c2ecf20Sopenharmony_ci			"%s   unsupported number of components (%d > %d)\n",
658c2ecf20Sopenharmony_ci			pctx->name, header->nb_of_components,
668c2ecf20Sopenharmony_ci			MJPEG_MAX_COMPONENTS);
678c2ecf20Sopenharmony_ci		return -EINVAL;
688c2ecf20Sopenharmony_ci	}
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci	if ((offset + header->nb_of_components *
718c2ecf20Sopenharmony_ci	     sizeof(header->components[0])) > size)
728c2ecf20Sopenharmony_ci		goto err_no_more;
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci	return 0;
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_cierr_no_more:
778c2ecf20Sopenharmony_ci	dev_err(delta->dev,
788c2ecf20Sopenharmony_ci		"%s   sof: reached end of %d size input stream\n",
798c2ecf20Sopenharmony_ci		pctx->name, size);
808c2ecf20Sopenharmony_ci	return -ENODATA;
818c2ecf20Sopenharmony_ci}
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ciint delta_mjpeg_read_header(struct delta_ctx *pctx,
848c2ecf20Sopenharmony_ci			    unsigned char *data, unsigned int size,
858c2ecf20Sopenharmony_ci			    struct mjpeg_header *header,
868c2ecf20Sopenharmony_ci			    unsigned int *data_offset)
878c2ecf20Sopenharmony_ci{
888c2ecf20Sopenharmony_ci	struct delta_dev *delta = pctx->dev;
898c2ecf20Sopenharmony_ci	unsigned char str[200];
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci	unsigned int ret = 0;
928c2ecf20Sopenharmony_ci	unsigned int offset = 0;
938c2ecf20Sopenharmony_ci	unsigned int soi = 0;
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci	if (size < 2)
968c2ecf20Sopenharmony_ci		goto err_no_more;
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci	offset = 0;
998c2ecf20Sopenharmony_ci	while (1) {
1008c2ecf20Sopenharmony_ci		if (data[offset] == MJPEG_MARKER)
1018c2ecf20Sopenharmony_ci			switch (data[offset + 1]) {
1028c2ecf20Sopenharmony_ci			case MJPEG_SOI:
1038c2ecf20Sopenharmony_ci				soi = 1;
1048c2ecf20Sopenharmony_ci				*data_offset = offset;
1058c2ecf20Sopenharmony_ci				break;
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci			case MJPEG_SOF_0:
1088c2ecf20Sopenharmony_ci			case MJPEG_SOF_1:
1098c2ecf20Sopenharmony_ci				if (!soi) {
1108c2ecf20Sopenharmony_ci					dev_err(delta->dev,
1118c2ecf20Sopenharmony_ci						"%s   wrong sequence, got SOF while SOI not seen\n",
1128c2ecf20Sopenharmony_ci						pctx->name);
1138c2ecf20Sopenharmony_ci					return -EINVAL;
1148c2ecf20Sopenharmony_ci				}
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci				ret = delta_mjpeg_read_sof(pctx,
1178c2ecf20Sopenharmony_ci							   &data[offset + 2],
1188c2ecf20Sopenharmony_ci							   size - (offset + 2),
1198c2ecf20Sopenharmony_ci							   header);
1208c2ecf20Sopenharmony_ci				if (ret)
1218c2ecf20Sopenharmony_ci					goto err;
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci				goto done;
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci			default:
1268c2ecf20Sopenharmony_ci				break;
1278c2ecf20Sopenharmony_ci			}
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci		offset++;
1308c2ecf20Sopenharmony_ci		if ((offset + 2) >= size)
1318c2ecf20Sopenharmony_ci			goto err_no_more;
1328c2ecf20Sopenharmony_ci	}
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_cidone:
1358c2ecf20Sopenharmony_ci	dev_dbg(delta->dev,
1368c2ecf20Sopenharmony_ci		"%s   found header @ offset %d:\n%s", pctx->name,
1378c2ecf20Sopenharmony_ci		*data_offset,
1388c2ecf20Sopenharmony_ci		header_str(header, str, sizeof(str)));
1398c2ecf20Sopenharmony_ci	return 0;
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_cierr_no_more:
1428c2ecf20Sopenharmony_ci	dev_err(delta->dev,
1438c2ecf20Sopenharmony_ci		"%s   no header found within %d bytes input stream\n",
1448c2ecf20Sopenharmony_ci		pctx->name, size);
1458c2ecf20Sopenharmony_ci	return -ENODATA;
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_cierr:
1488c2ecf20Sopenharmony_ci	return ret;
1498c2ecf20Sopenharmony_ci}
150