vimc-sensor.c 9.7 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
/*
 * vimc-sensor.c Virtual Media Controller Driver
 *
 * Copyright (C) 2015-2017 Helen Koike <helen.fornazier@gmail.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that 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.
 *
 */

18
#include <linux/component.h>
19 20
#include <linux/freezer.h>
#include <linux/kthread.h>
21 22
#include <linux/module.h>
#include <linux/platform_device.h>
23 24 25
#include <linux/v4l2-mediabus.h>
#include <linux/vmalloc.h>
#include <media/v4l2-subdev.h>
26
#include <media/v4l2-tpg.h>
27

28 29 30
#include "vimc-common.h"

#define VIMC_SEN_DRV_NAME "vimc-sensor"
31 32 33 34

struct vimc_sen_device {
	struct vimc_ent_device ved;
	struct v4l2_subdev sd;
35
	struct device *dev;
36
	struct tpg_data tpg;
37 38 39 40 41 42
	struct task_struct *kthread_sen;
	u8 *frame;
	/* The active format */
	struct v4l2_mbus_framefmt mbus_format;
};

43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
static const struct v4l2_mbus_framefmt fmt_default = {
	.width = 640,
	.height = 480,
	.code = MEDIA_BUS_FMT_RGB888_1X24,
	.field = V4L2_FIELD_NONE,
	.colorspace = V4L2_COLORSPACE_DEFAULT,
};

static int vimc_sen_init_cfg(struct v4l2_subdev *sd,
			     struct v4l2_subdev_pad_config *cfg)
{
	unsigned int i;

	for (i = 0; i < sd->entity.num_pads; i++) {
		struct v4l2_mbus_framefmt *mf;

		mf = v4l2_subdev_get_try_format(sd, cfg, i);
		*mf = fmt_default;
	}

	return 0;
}

66 67 68 69
static int vimc_sen_enum_mbus_code(struct v4l2_subdev *sd,
				   struct v4l2_subdev_pad_config *cfg,
				   struct v4l2_subdev_mbus_code_enum *code)
{
70
	const struct vimc_pix_map *vpix = vimc_pix_map_by_index(code->index);
71

72
	if (!vpix)
73 74
		return -EINVAL;

75
	code->code = vpix->code;
76 77 78 79 80 81 82 83

	return 0;
}

static int vimc_sen_enum_frame_size(struct v4l2_subdev *sd,
				    struct v4l2_subdev_pad_config *cfg,
				    struct v4l2_subdev_frame_size_enum *fse)
{
84
	const struct vimc_pix_map *vpix;
85 86 87 88

	if (fse->index)
		return -EINVAL;

89 90 91
	/* Only accept code in the pix map table */
	vpix = vimc_pix_map_by_code(fse->code);
	if (!vpix)
92 93
		return -EINVAL;

94 95 96 97
	fse->min_width = VIMC_FRAME_MIN_WIDTH;
	fse->max_width = VIMC_FRAME_MAX_WIDTH;
	fse->min_height = VIMC_FRAME_MIN_HEIGHT;
	fse->max_height = VIMC_FRAME_MAX_HEIGHT;
98 99 100 101 102 103

	return 0;
}

static int vimc_sen_get_fmt(struct v4l2_subdev *sd,
			    struct v4l2_subdev_pad_config *cfg,
104
			    struct v4l2_subdev_format *fmt)
105 106 107 108
{
	struct vimc_sen_device *vsen =
				container_of(sd, struct vimc_sen_device, sd);

109 110 111
	fmt->format = fmt->which == V4L2_SUBDEV_FORMAT_TRY ?
		      *v4l2_subdev_get_try_format(sd, cfg, fmt->pad) :
		      vsen->mbus_format;
112 113 114 115

	return 0;
}

116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133
static void vimc_sen_tpg_s_format(struct vimc_sen_device *vsen)
{
	const struct vimc_pix_map *vpix =
				vimc_pix_map_by_code(vsen->mbus_format.code);

	tpg_reset_source(&vsen->tpg, vsen->mbus_format.width,
			 vsen->mbus_format.height, vsen->mbus_format.field);
	tpg_s_bytesperline(&vsen->tpg, 0, vsen->mbus_format.width * vpix->bpp);
	tpg_s_buf_height(&vsen->tpg, vsen->mbus_format.height);
	tpg_s_fourcc(&vsen->tpg, vpix->pixelformat);
	/* TODO: add support for V4L2_FIELD_ALTERNATE */
	tpg_s_field(&vsen->tpg, vsen->mbus_format.field, false);
	tpg_s_colorspace(&vsen->tpg, vsen->mbus_format.colorspace);
	tpg_s_ycbcr_enc(&vsen->tpg, vsen->mbus_format.ycbcr_enc);
	tpg_s_quantization(&vsen->tpg, vsen->mbus_format.quantization);
	tpg_s_xfer_func(&vsen->tpg, vsen->mbus_format.xfer_func);
}

134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174
static void vimc_sen_adjust_fmt(struct v4l2_mbus_framefmt *fmt)
{
	const struct vimc_pix_map *vpix;

	/* Only accept code in the pix map table */
	vpix = vimc_pix_map_by_code(fmt->code);
	if (!vpix)
		fmt->code = fmt_default.code;

	fmt->width = clamp_t(u32, fmt->width, VIMC_FRAME_MIN_WIDTH,
			     VIMC_FRAME_MAX_WIDTH) & ~1;
	fmt->height = clamp_t(u32, fmt->height, VIMC_FRAME_MIN_HEIGHT,
			      VIMC_FRAME_MAX_HEIGHT) & ~1;

	/* TODO: add support for V4L2_FIELD_ALTERNATE */
	if (fmt->field == V4L2_FIELD_ANY || fmt->field == V4L2_FIELD_ALTERNATE)
		fmt->field = fmt_default.field;

	vimc_colorimetry_clamp(fmt);
}

static int vimc_sen_set_fmt(struct v4l2_subdev *sd,
			    struct v4l2_subdev_pad_config *cfg,
			    struct v4l2_subdev_format *fmt)
{
	struct vimc_sen_device *vsen = v4l2_get_subdevdata(sd);
	struct v4l2_mbus_framefmt *mf;

	if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) {
		/* Do not change the format while stream is on */
		if (vsen->frame)
			return -EBUSY;

		mf = &vsen->mbus_format;
	} else {
		mf = v4l2_subdev_get_try_format(sd, cfg, fmt->pad);
	}

	/* Set the new format */
	vimc_sen_adjust_fmt(&fmt->format);

175
	dev_dbg(vsen->dev, "%s: format update: "
176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191
		"old:%dx%d (0x%x, %d, %d, %d, %d) "
		"new:%dx%d (0x%x, %d, %d, %d, %d)\n", vsen->sd.name,
		/* old */
		mf->width, mf->height, mf->code,
		mf->colorspace,	mf->quantization,
		mf->xfer_func, mf->ycbcr_enc,
		/* new */
		fmt->format.width, fmt->format.height, fmt->format.code,
		fmt->format.colorspace, fmt->format.quantization,
		fmt->format.xfer_func, fmt->format.ycbcr_enc);

	*mf = fmt->format;

	return 0;
}

192
static const struct v4l2_subdev_pad_ops vimc_sen_pad_ops = {
193
	.init_cfg		= vimc_sen_init_cfg,
194 195 196
	.enum_mbus_code		= vimc_sen_enum_mbus_code,
	.enum_frame_size	= vimc_sen_enum_frame_size,
	.get_fmt		= vimc_sen_get_fmt,
197
	.set_fmt		= vimc_sen_set_fmt,
198 199
};

200
static int vimc_sen_tpg_thread(void *data)
201 202 203 204 205 206 207 208 209 210 211 212
{
	struct vimc_sen_device *vsen = data;
	unsigned int i;

	set_freezable();
	set_current_state(TASK_UNINTERRUPTIBLE);

	for (;;) {
		try_to_freeze();
		if (kthread_should_stop())
			break;

213
		tpg_fill_plane_buffer(&vsen->tpg, 0, 0, vsen->frame);
214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234

		/* Send the frame to all source pads */
		for (i = 0; i < vsen->sd.entity.num_pads; i++)
			vimc_propagate_frame(&vsen->sd.entity.pads[i],
					     vsen->frame);

		/* 60 frames per second */
		schedule_timeout(HZ/60);
	}

	return 0;
}

static int vimc_sen_s_stream(struct v4l2_subdev *sd, int enable)
{
	struct vimc_sen_device *vsen =
				container_of(sd, struct vimc_sen_device, sd);
	int ret;

	if (enable) {
		const struct vimc_pix_map *vpix;
235
		unsigned int frame_size;
236 237

		if (vsen->kthread_sen)
238 239
			/* tpg is already executing */
			return 0;
240 241 242

		/* Calculate the frame size */
		vpix = vimc_pix_map_by_code(vsen->mbus_format.code);
243 244
		frame_size = vsen->mbus_format.width * vpix->bpp *
			     vsen->mbus_format.height;
245 246 247 248 249

		/*
		 * Allocate the frame buffer. Use vmalloc to be able to
		 * allocate a large amount of memory
		 */
250
		vsen->frame = vmalloc(frame_size);
251 252 253
		if (!vsen->frame)
			return -ENOMEM;

254 255 256
		/* configure the test pattern generator */
		vimc_sen_tpg_s_format(vsen);

257
		/* Initialize the image generator thread */
258 259
		vsen->kthread_sen = kthread_run(vimc_sen_tpg_thread, vsen,
					"%s-sen", vsen->sd.v4l2_dev->name);
260
		if (IS_ERR(vsen->kthread_sen)) {
261 262
			dev_err(vsen->dev, "%s: kernel_thread() failed\n",
				vsen->sd.name);
263 264 265 266 267 268
			vfree(vsen->frame);
			vsen->frame = NULL;
			return PTR_ERR(vsen->kthread_sen);
		}
	} else {
		if (!vsen->kthread_sen)
269
			return 0;
270 271 272

		/* Stop image generator */
		ret = kthread_stop(vsen->kthread_sen);
273 274
		if (ret)
			return ret;
275

276
		vsen->kthread_sen = NULL;
277 278
		vfree(vsen->frame);
		vsen->frame = NULL;
279
		return 0;
280 281 282 283 284 285 286 287 288 289 290 291 292 293
	}

	return 0;
}

struct v4l2_subdev_video_ops vimc_sen_video_ops = {
	.s_stream = vimc_sen_s_stream,
};

static const struct v4l2_subdev_ops vimc_sen_ops = {
	.pad = &vimc_sen_pad_ops,
	.video = &vimc_sen_video_ops,
};

294 295
static void vimc_sen_comp_unbind(struct device *comp, struct device *master,
				 void *master_data)
296
{
297
	struct vimc_ent_device *ved = dev_get_drvdata(comp);
298 299 300
	struct vimc_sen_device *vsen =
				container_of(ved, struct vimc_sen_device, ved);

301
	vimc_ent_sd_unregister(ved, &vsen->sd);
302
	tpg_free(&vsen->tpg);
303 304 305
	kfree(vsen);
}

306 307
static int vimc_sen_comp_bind(struct device *comp, struct device *master,
			      void *master_data)
308
{
309 310
	struct v4l2_device *v4l2_dev = master_data;
	struct vimc_platform_data *pdata = comp->platform_data;
311 312 313 314 315 316
	struct vimc_sen_device *vsen;
	int ret;

	/* Allocate the vsen struct */
	vsen = kzalloc(sizeof(*vsen), GFP_KERNEL);
	if (!vsen)
317
		return -ENOMEM;
318

319
	/* Initialize ved and sd */
320 321 322 323 324
	ret = vimc_ent_sd_register(&vsen->ved, &vsen->sd, v4l2_dev,
				   pdata->entity_name,
				   MEDIA_ENT_F_ATV_DECODER, 1,
				   (const unsigned long[1]) {MEDIA_PAD_FL_SOURCE},
				   &vimc_sen_ops);
325
	if (ret)
326
		goto err_free_vsen;
327

328 329 330
	dev_set_drvdata(comp, &vsen->ved);
	vsen->dev = comp;

331 332
	/* Initialize the frame format */
	vsen->mbus_format = fmt_default;
333

334 335 336
	/* Initialize the test pattern generator */
	tpg_init(&vsen->tpg, vsen->mbus_format.width,
		 vsen->mbus_format.height);
337
	ret = tpg_alloc(&vsen->tpg, VIMC_FRAME_MAX_WIDTH);
338
	if (ret)
339
		goto err_unregister_ent_sd;
340

341
	return 0;
342

343 344
err_unregister_ent_sd:
	vimc_ent_sd_unregister(&vsen->ved,  &vsen->sd);
345 346 347
err_free_vsen:
	kfree(vsen);

348
	return ret;
349
}
350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389

static const struct component_ops vimc_sen_comp_ops = {
	.bind = vimc_sen_comp_bind,
	.unbind = vimc_sen_comp_unbind,
};

static int vimc_sen_probe(struct platform_device *pdev)
{
	return component_add(&pdev->dev, &vimc_sen_comp_ops);
}

static int vimc_sen_remove(struct platform_device *pdev)
{
	component_del(&pdev->dev, &vimc_sen_comp_ops);

	return 0;
}

static struct platform_driver vimc_sen_pdrv = {
	.probe		= vimc_sen_probe,
	.remove		= vimc_sen_remove,
	.driver		= {
		.name	= VIMC_SEN_DRV_NAME,
	},
};

static const struct platform_device_id vimc_sen_driver_ids[] = {
	{
		.name           = VIMC_SEN_DRV_NAME,
	},
	{ }
};

module_platform_driver(vimc_sen_pdrv);

MODULE_DEVICE_TABLE(platform, vimc_sen_driver_ids);

MODULE_DESCRIPTION("Virtual Media Controller Driver (VIMC) Sensor");
MODULE_AUTHOR("Helen Mae Koike Fornazier <helen.fornazier@gmail.com>");
MODULE_LICENSE("GPL");