162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Driver for the VIA Chrome integrated camera controller.
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright 2009,2010 Jonathan Corbet <corbet@lwn.net>
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * This work was supported by the One Laptop Per Child project
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci#include <linux/kernel.h>
1062306a36Sopenharmony_ci#include <linux/module.h>
1162306a36Sopenharmony_ci#include <linux/device.h>
1262306a36Sopenharmony_ci#include <linux/list.h>
1362306a36Sopenharmony_ci#include <linux/pci.h>
1462306a36Sopenharmony_ci#include <linux/gpio/consumer.h>
1562306a36Sopenharmony_ci#include <linux/interrupt.h>
1662306a36Sopenharmony_ci#include <linux/platform_device.h>
1762306a36Sopenharmony_ci#include <linux/videodev2.h>
1862306a36Sopenharmony_ci#include <media/v4l2-device.h>
1962306a36Sopenharmony_ci#include <media/v4l2-ioctl.h>
2062306a36Sopenharmony_ci#include <media/v4l2-ctrls.h>
2162306a36Sopenharmony_ci#include <media/v4l2-event.h>
2262306a36Sopenharmony_ci#include <media/v4l2-image-sizes.h>
2362306a36Sopenharmony_ci#include <media/i2c/ov7670.h>
2462306a36Sopenharmony_ci#include <media/videobuf2-dma-sg.h>
2562306a36Sopenharmony_ci#include <linux/delay.h>
2662306a36Sopenharmony_ci#include <linux/dma-mapping.h>
2762306a36Sopenharmony_ci#include <linux/pm_qos.h>
2862306a36Sopenharmony_ci#include <linux/via-core.h>
2962306a36Sopenharmony_ci#include <linux/via_i2c.h>
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci#ifdef CONFIG_X86
3262306a36Sopenharmony_ci#include <asm/olpc.h>
3362306a36Sopenharmony_ci#else
3462306a36Sopenharmony_ci#define machine_is_olpc(x) 0
3562306a36Sopenharmony_ci#endif
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci#include "via-camera.h"
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ciMODULE_ALIAS("platform:viafb-camera");
4062306a36Sopenharmony_ciMODULE_AUTHOR("Jonathan Corbet <corbet@lwn.net>");
4162306a36Sopenharmony_ciMODULE_DESCRIPTION("VIA framebuffer-based camera controller driver");
4262306a36Sopenharmony_ciMODULE_LICENSE("GPL");
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_cistatic bool flip_image;
4562306a36Sopenharmony_cimodule_param(flip_image, bool, 0444);
4662306a36Sopenharmony_ciMODULE_PARM_DESC(flip_image,
4762306a36Sopenharmony_ci		"If set, the sensor will be instructed to flip the image vertically.");
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_cistatic bool override_serial;
5062306a36Sopenharmony_cimodule_param(override_serial, bool, 0444);
5162306a36Sopenharmony_ciMODULE_PARM_DESC(override_serial,
5262306a36Sopenharmony_ci		"The camera driver will normally refuse to load if the XO 1.5 serial port is enabled.  Set this option to force-enable the camera.");
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci/*
5562306a36Sopenharmony_ci * The structure describing our camera.
5662306a36Sopenharmony_ci */
5762306a36Sopenharmony_cienum viacam_opstate { S_IDLE = 0, S_RUNNING = 1 };
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_cistruct via_camera {
6062306a36Sopenharmony_ci	struct v4l2_device v4l2_dev;
6162306a36Sopenharmony_ci	struct v4l2_ctrl_handler ctrl_handler;
6262306a36Sopenharmony_ci	struct video_device vdev;
6362306a36Sopenharmony_ci	struct v4l2_subdev *sensor;
6462306a36Sopenharmony_ci	struct platform_device *platdev;
6562306a36Sopenharmony_ci	struct viafb_dev *viadev;
6662306a36Sopenharmony_ci	struct mutex lock;
6762306a36Sopenharmony_ci	enum viacam_opstate opstate;
6862306a36Sopenharmony_ci	unsigned long flags;
6962306a36Sopenharmony_ci	struct pm_qos_request qos_request;
7062306a36Sopenharmony_ci	/*
7162306a36Sopenharmony_ci	 * GPIO info for power/reset management
7262306a36Sopenharmony_ci	 */
7362306a36Sopenharmony_ci	struct gpio_desc *power_gpio;
7462306a36Sopenharmony_ci	struct gpio_desc *reset_gpio;
7562306a36Sopenharmony_ci	/*
7662306a36Sopenharmony_ci	 * I/O memory stuff.
7762306a36Sopenharmony_ci	 */
7862306a36Sopenharmony_ci	void __iomem *mmio;	/* Where the registers live */
7962306a36Sopenharmony_ci	void __iomem *fbmem;	/* Frame buffer memory */
8062306a36Sopenharmony_ci	u32 fb_offset;		/* Reserved memory offset (FB) */
8162306a36Sopenharmony_ci	/*
8262306a36Sopenharmony_ci	 * Capture buffers and related.	 The controller supports
8362306a36Sopenharmony_ci	 * up to three, so that's what we have here.  These buffers
8462306a36Sopenharmony_ci	 * live in frame buffer memory, so we don't call them "DMA".
8562306a36Sopenharmony_ci	 */
8662306a36Sopenharmony_ci	unsigned int cb_offsets[3];	/* offsets into fb mem */
8762306a36Sopenharmony_ci	u8 __iomem *cb_addrs[3];	/* Kernel-space addresses */
8862306a36Sopenharmony_ci	int n_cap_bufs;			/* How many are we using? */
8962306a36Sopenharmony_ci	struct vb2_queue vq;
9062306a36Sopenharmony_ci	struct list_head buffer_queue;
9162306a36Sopenharmony_ci	u32 sequence;
9262306a36Sopenharmony_ci	/*
9362306a36Sopenharmony_ci	 * Video format information.  sensor_format is kept in a form
9462306a36Sopenharmony_ci	 * that we can use to pass to the sensor.  We always run the
9562306a36Sopenharmony_ci	 * sensor in VGA resolution, though, and let the controller
9662306a36Sopenharmony_ci	 * downscale things if need be.	 So we keep the "real*
9762306a36Sopenharmony_ci	 * dimensions separately.
9862306a36Sopenharmony_ci	 */
9962306a36Sopenharmony_ci	struct v4l2_pix_format sensor_format;
10062306a36Sopenharmony_ci	struct v4l2_pix_format user_format;
10162306a36Sopenharmony_ci	u32 mbus_code;
10262306a36Sopenharmony_ci};
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci/* buffer for one video frame */
10562306a36Sopenharmony_cistruct via_buffer {
10662306a36Sopenharmony_ci	/* common v4l buffer stuff -- must be first */
10762306a36Sopenharmony_ci	struct vb2_v4l2_buffer		vbuf;
10862306a36Sopenharmony_ci	struct list_head		queue;
10962306a36Sopenharmony_ci};
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci/*
11262306a36Sopenharmony_ci * Yes, this is a hack, but there's only going to be one of these
11362306a36Sopenharmony_ci * on any system we know of.
11462306a36Sopenharmony_ci */
11562306a36Sopenharmony_cistatic struct via_camera *via_cam_info;
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci/*
11862306a36Sopenharmony_ci * Flag values, manipulated with bitops
11962306a36Sopenharmony_ci */
12062306a36Sopenharmony_ci#define CF_DMA_ACTIVE	 0	/* A frame is incoming */
12162306a36Sopenharmony_ci#define CF_CONFIG_NEEDED 1	/* Must configure hardware */
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci/*
12562306a36Sopenharmony_ci * Nasty ugly v4l2 boilerplate.
12662306a36Sopenharmony_ci */
12762306a36Sopenharmony_ci#define sensor_call(cam, optype, func, args...) \
12862306a36Sopenharmony_ci	v4l2_subdev_call(cam->sensor, optype, func, ##args)
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci/*
13162306a36Sopenharmony_ci * Debugging and related.
13262306a36Sopenharmony_ci */
13362306a36Sopenharmony_ci#define cam_err(cam, fmt, arg...) \
13462306a36Sopenharmony_ci	dev_err(&(cam)->platdev->dev, fmt, ##arg)
13562306a36Sopenharmony_ci#define cam_warn(cam, fmt, arg...) \
13662306a36Sopenharmony_ci	dev_warn(&(cam)->platdev->dev, fmt, ##arg)
13762306a36Sopenharmony_ci#define cam_dbg(cam, fmt, arg...) \
13862306a36Sopenharmony_ci	dev_dbg(&(cam)->platdev->dev, fmt, ##arg)
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci/*
14162306a36Sopenharmony_ci * Format handling.  This is ripped almost directly from Hans's changes
14262306a36Sopenharmony_ci * to cafe_ccic.c.  It's a little unfortunate; until this change, we
14362306a36Sopenharmony_ci * didn't need to know anything about the format except its byte depth;
14462306a36Sopenharmony_ci * now this information must be managed at this level too.
14562306a36Sopenharmony_ci */
14662306a36Sopenharmony_cistatic struct via_format {
14762306a36Sopenharmony_ci	__u32 pixelformat;
14862306a36Sopenharmony_ci	int bpp;   /* Bytes per pixel */
14962306a36Sopenharmony_ci	u32 mbus_code;
15062306a36Sopenharmony_ci} via_formats[] = {
15162306a36Sopenharmony_ci	{
15262306a36Sopenharmony_ci		.pixelformat	= V4L2_PIX_FMT_YUYV,
15362306a36Sopenharmony_ci		.mbus_code	= MEDIA_BUS_FMT_YUYV8_2X8,
15462306a36Sopenharmony_ci		.bpp		= 2,
15562306a36Sopenharmony_ci	},
15662306a36Sopenharmony_ci	/* RGB444 and Bayer should be doable, but have never been
15762306a36Sopenharmony_ci	   tested with this driver. RGB565 seems to work at the default
15862306a36Sopenharmony_ci	   resolution, but results in color corruption when being scaled by
15962306a36Sopenharmony_ci	   viacam_set_scaled(), and is disabled as a result. */
16062306a36Sopenharmony_ci};
16162306a36Sopenharmony_ci#define N_VIA_FMTS ARRAY_SIZE(via_formats)
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_cistatic struct via_format *via_find_format(u32 pixelformat)
16462306a36Sopenharmony_ci{
16562306a36Sopenharmony_ci	unsigned i;
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci	for (i = 0; i < N_VIA_FMTS; i++)
16862306a36Sopenharmony_ci		if (via_formats[i].pixelformat == pixelformat)
16962306a36Sopenharmony_ci			return via_formats + i;
17062306a36Sopenharmony_ci	/* Not found? Then return the first format. */
17162306a36Sopenharmony_ci	return via_formats;
17262306a36Sopenharmony_ci}
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci/*--------------------------------------------------------------------------*/
17662306a36Sopenharmony_ci/*
17762306a36Sopenharmony_ci * Sensor power/reset management.  This piece is OLPC-specific for
17862306a36Sopenharmony_ci * sure; other configurations will have things connected differently.
17962306a36Sopenharmony_ci */
18062306a36Sopenharmony_cistatic int via_sensor_power_setup(struct via_camera *cam)
18162306a36Sopenharmony_ci{
18262306a36Sopenharmony_ci	struct device *dev = &cam->platdev->dev;
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci	cam->power_gpio = devm_gpiod_get(dev, "VGPIO3", GPIOD_OUT_LOW);
18562306a36Sopenharmony_ci	if (IS_ERR(cam->power_gpio))
18662306a36Sopenharmony_ci		return dev_err_probe(dev, PTR_ERR(cam->power_gpio),
18762306a36Sopenharmony_ci				     "failed to get power GPIO");
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci	/* Request the reset line asserted */
19062306a36Sopenharmony_ci	cam->reset_gpio = devm_gpiod_get(dev, "VGPIO2", GPIOD_OUT_HIGH);
19162306a36Sopenharmony_ci	if (IS_ERR(cam->reset_gpio))
19262306a36Sopenharmony_ci		return dev_err_probe(dev, PTR_ERR(cam->reset_gpio),
19362306a36Sopenharmony_ci				     "failed to get reset GPIO");
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci	return 0;
19662306a36Sopenharmony_ci}
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci/*
19962306a36Sopenharmony_ci * Power up the sensor and perform the reset dance.
20062306a36Sopenharmony_ci */
20162306a36Sopenharmony_cistatic void via_sensor_power_up(struct via_camera *cam)
20262306a36Sopenharmony_ci{
20362306a36Sopenharmony_ci	gpiod_set_value(cam->power_gpio, 1);
20462306a36Sopenharmony_ci	gpiod_set_value(cam->reset_gpio, 1);
20562306a36Sopenharmony_ci	msleep(20);  /* Probably excessive */
20662306a36Sopenharmony_ci	gpiod_set_value(cam->reset_gpio, 0);
20762306a36Sopenharmony_ci	msleep(20);
20862306a36Sopenharmony_ci}
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_cistatic void via_sensor_power_down(struct via_camera *cam)
21162306a36Sopenharmony_ci{
21262306a36Sopenharmony_ci	gpiod_set_value(cam->power_gpio, 0);
21362306a36Sopenharmony_ci	gpiod_set_value(cam->reset_gpio, 1);
21462306a36Sopenharmony_ci}
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_cistatic void via_sensor_power_release(struct via_camera *cam)
21862306a36Sopenharmony_ci{
21962306a36Sopenharmony_ci	via_sensor_power_down(cam);
22062306a36Sopenharmony_ci}
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci/* --------------------------------------------------------------------------*/
22362306a36Sopenharmony_ci/* Sensor ops */
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci/*
22662306a36Sopenharmony_ci * Manage the ov7670 "flip" bit, which needs special help.
22762306a36Sopenharmony_ci */
22862306a36Sopenharmony_cistatic int viacam_set_flip(struct via_camera *cam)
22962306a36Sopenharmony_ci{
23062306a36Sopenharmony_ci	struct v4l2_control ctrl;
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci	memset(&ctrl, 0, sizeof(ctrl));
23362306a36Sopenharmony_ci	ctrl.id = V4L2_CID_VFLIP;
23462306a36Sopenharmony_ci	ctrl.value = flip_image;
23562306a36Sopenharmony_ci	return v4l2_s_ctrl(NULL, cam->sensor->ctrl_handler, &ctrl);
23662306a36Sopenharmony_ci}
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci/*
23962306a36Sopenharmony_ci * Configure the sensor.  It's up to the caller to ensure
24062306a36Sopenharmony_ci * that the camera is in the correct operating state.
24162306a36Sopenharmony_ci */
24262306a36Sopenharmony_cistatic int viacam_configure_sensor(struct via_camera *cam)
24362306a36Sopenharmony_ci{
24462306a36Sopenharmony_ci	struct v4l2_subdev_format format = {
24562306a36Sopenharmony_ci		.which = V4L2_SUBDEV_FORMAT_ACTIVE,
24662306a36Sopenharmony_ci	};
24762306a36Sopenharmony_ci	int ret;
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci	v4l2_fill_mbus_format(&format.format, &cam->sensor_format, cam->mbus_code);
25062306a36Sopenharmony_ci	ret = sensor_call(cam, core, init, 0);
25162306a36Sopenharmony_ci	if (ret == 0)
25262306a36Sopenharmony_ci		ret = sensor_call(cam, pad, set_fmt, NULL, &format);
25362306a36Sopenharmony_ci	/*
25462306a36Sopenharmony_ci	 * OV7670 does weird things if flip is set *before* format...
25562306a36Sopenharmony_ci	 */
25662306a36Sopenharmony_ci	if (ret == 0)
25762306a36Sopenharmony_ci		ret = viacam_set_flip(cam);
25862306a36Sopenharmony_ci	return ret;
25962306a36Sopenharmony_ci}
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_ci/* --------------------------------------------------------------------------*/
26462306a36Sopenharmony_ci/*
26562306a36Sopenharmony_ci * Some simple register accessors; they assume that the lock is held.
26662306a36Sopenharmony_ci *
26762306a36Sopenharmony_ci * Should we want to support the second capture engine, we could
26862306a36Sopenharmony_ci * hide the register difference by adding 0x1000 to registers in the
26962306a36Sopenharmony_ci * 0x300-350 range.
27062306a36Sopenharmony_ci */
27162306a36Sopenharmony_cistatic inline void viacam_write_reg(struct via_camera *cam,
27262306a36Sopenharmony_ci		int reg, int value)
27362306a36Sopenharmony_ci{
27462306a36Sopenharmony_ci	iowrite32(value, cam->mmio + reg);
27562306a36Sopenharmony_ci}
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_cistatic inline int viacam_read_reg(struct via_camera *cam, int reg)
27862306a36Sopenharmony_ci{
27962306a36Sopenharmony_ci	return ioread32(cam->mmio + reg);
28062306a36Sopenharmony_ci}
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_cistatic inline void viacam_write_reg_mask(struct via_camera *cam,
28362306a36Sopenharmony_ci		int reg, int value, int mask)
28462306a36Sopenharmony_ci{
28562306a36Sopenharmony_ci	int tmp = viacam_read_reg(cam, reg);
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_ci	tmp = (tmp & ~mask) | (value & mask);
28862306a36Sopenharmony_ci	viacam_write_reg(cam, reg, tmp);
28962306a36Sopenharmony_ci}
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci/* --------------------------------------------------------------------------*/
29362306a36Sopenharmony_ci/* Interrupt management and handling */
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_cistatic irqreturn_t viacam_quick_irq(int irq, void *data)
29662306a36Sopenharmony_ci{
29762306a36Sopenharmony_ci	struct via_camera *cam = data;
29862306a36Sopenharmony_ci	irqreturn_t ret = IRQ_NONE;
29962306a36Sopenharmony_ci	int icv;
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_ci	/*
30262306a36Sopenharmony_ci	 * All we do here is to clear the interrupts and tell
30362306a36Sopenharmony_ci	 * the handler thread to wake up.
30462306a36Sopenharmony_ci	 */
30562306a36Sopenharmony_ci	spin_lock(&cam->viadev->reg_lock);
30662306a36Sopenharmony_ci	icv = viacam_read_reg(cam, VCR_INTCTRL);
30762306a36Sopenharmony_ci	if (icv & VCR_IC_EAV) {
30862306a36Sopenharmony_ci		icv |= VCR_IC_EAV|VCR_IC_EVBI|VCR_IC_FFULL;
30962306a36Sopenharmony_ci		viacam_write_reg(cam, VCR_INTCTRL, icv);
31062306a36Sopenharmony_ci		ret = IRQ_WAKE_THREAD;
31162306a36Sopenharmony_ci	}
31262306a36Sopenharmony_ci	spin_unlock(&cam->viadev->reg_lock);
31362306a36Sopenharmony_ci	return ret;
31462306a36Sopenharmony_ci}
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci/*
31762306a36Sopenharmony_ci * Find the next buffer which has somebody waiting on it.
31862306a36Sopenharmony_ci */
31962306a36Sopenharmony_cistatic struct via_buffer *viacam_next_buffer(struct via_camera *cam)
32062306a36Sopenharmony_ci{
32162306a36Sopenharmony_ci	if (cam->opstate != S_RUNNING)
32262306a36Sopenharmony_ci		return NULL;
32362306a36Sopenharmony_ci	if (list_empty(&cam->buffer_queue))
32462306a36Sopenharmony_ci		return NULL;
32562306a36Sopenharmony_ci	return list_entry(cam->buffer_queue.next, struct via_buffer, queue);
32662306a36Sopenharmony_ci}
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_ci/*
32962306a36Sopenharmony_ci * The threaded IRQ handler.
33062306a36Sopenharmony_ci */
33162306a36Sopenharmony_cistatic irqreturn_t viacam_irq(int irq, void *data)
33262306a36Sopenharmony_ci{
33362306a36Sopenharmony_ci	struct via_camera *cam = data;
33462306a36Sopenharmony_ci	struct via_buffer *vb;
33562306a36Sopenharmony_ci	int bufn;
33662306a36Sopenharmony_ci	struct sg_table *sgt;
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci	mutex_lock(&cam->lock);
33962306a36Sopenharmony_ci	/*
34062306a36Sopenharmony_ci	 * If there is no place to put the data frame, don't bother
34162306a36Sopenharmony_ci	 * with anything else.
34262306a36Sopenharmony_ci	 */
34362306a36Sopenharmony_ci	vb = viacam_next_buffer(cam);
34462306a36Sopenharmony_ci	if (vb == NULL)
34562306a36Sopenharmony_ci		goto done;
34662306a36Sopenharmony_ci	/*
34762306a36Sopenharmony_ci	 * Figure out which buffer we just completed.
34862306a36Sopenharmony_ci	 */
34962306a36Sopenharmony_ci	bufn = (viacam_read_reg(cam, VCR_INTCTRL) & VCR_IC_ACTBUF) >> 3;
35062306a36Sopenharmony_ci	bufn -= 1;
35162306a36Sopenharmony_ci	if (bufn < 0)
35262306a36Sopenharmony_ci		bufn = cam->n_cap_bufs - 1;
35362306a36Sopenharmony_ci	/*
35462306a36Sopenharmony_ci	 * Copy over the data and let any waiters know.
35562306a36Sopenharmony_ci	 */
35662306a36Sopenharmony_ci	sgt = vb2_dma_sg_plane_desc(&vb->vbuf.vb2_buf, 0);
35762306a36Sopenharmony_ci	vb->vbuf.vb2_buf.timestamp = ktime_get_ns();
35862306a36Sopenharmony_ci	viafb_dma_copy_out_sg(cam->cb_offsets[bufn], sgt->sgl, sgt->nents);
35962306a36Sopenharmony_ci	vb->vbuf.sequence = cam->sequence++;
36062306a36Sopenharmony_ci	vb->vbuf.field = V4L2_FIELD_NONE;
36162306a36Sopenharmony_ci	list_del(&vb->queue);
36262306a36Sopenharmony_ci	vb2_buffer_done(&vb->vbuf.vb2_buf, VB2_BUF_STATE_DONE);
36362306a36Sopenharmony_cidone:
36462306a36Sopenharmony_ci	mutex_unlock(&cam->lock);
36562306a36Sopenharmony_ci	return IRQ_HANDLED;
36662306a36Sopenharmony_ci}
36762306a36Sopenharmony_ci
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_ci/*
37062306a36Sopenharmony_ci * These functions must mess around with the general interrupt
37162306a36Sopenharmony_ci * control register, which is relevant to much more than just the
37262306a36Sopenharmony_ci * camera.  Nothing else uses interrupts, though, as of this writing.
37362306a36Sopenharmony_ci * Should that situation change, we'll have to improve support at
37462306a36Sopenharmony_ci * the via-core level.
37562306a36Sopenharmony_ci */
37662306a36Sopenharmony_cistatic void viacam_int_enable(struct via_camera *cam)
37762306a36Sopenharmony_ci{
37862306a36Sopenharmony_ci	viacam_write_reg(cam, VCR_INTCTRL,
37962306a36Sopenharmony_ci			VCR_IC_INTEN|VCR_IC_EAV|VCR_IC_EVBI|VCR_IC_FFULL);
38062306a36Sopenharmony_ci	viafb_irq_enable(VDE_I_C0AVEN);
38162306a36Sopenharmony_ci}
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_cistatic void viacam_int_disable(struct via_camera *cam)
38462306a36Sopenharmony_ci{
38562306a36Sopenharmony_ci	viafb_irq_disable(VDE_I_C0AVEN);
38662306a36Sopenharmony_ci	viacam_write_reg(cam, VCR_INTCTRL, 0);
38762306a36Sopenharmony_ci}
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_ci
39162306a36Sopenharmony_ci/* --------------------------------------------------------------------------*/
39262306a36Sopenharmony_ci/* Controller operations */
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_ci/*
39562306a36Sopenharmony_ci * Set up our capture buffers in framebuffer memory.
39662306a36Sopenharmony_ci */
39762306a36Sopenharmony_cistatic int viacam_ctlr_cbufs(struct via_camera *cam)
39862306a36Sopenharmony_ci{
39962306a36Sopenharmony_ci	int nbuf = cam->viadev->camera_fbmem_size/cam->sensor_format.sizeimage;
40062306a36Sopenharmony_ci	int i;
40162306a36Sopenharmony_ci	unsigned int offset;
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_ci	/*
40462306a36Sopenharmony_ci	 * See how many buffers we can work with.
40562306a36Sopenharmony_ci	 */
40662306a36Sopenharmony_ci	if (nbuf >= 3) {
40762306a36Sopenharmony_ci		cam->n_cap_bufs = 3;
40862306a36Sopenharmony_ci		viacam_write_reg_mask(cam, VCR_CAPINTC, VCR_CI_3BUFS,
40962306a36Sopenharmony_ci				VCR_CI_3BUFS);
41062306a36Sopenharmony_ci	} else if (nbuf == 2) {
41162306a36Sopenharmony_ci		cam->n_cap_bufs = 2;
41262306a36Sopenharmony_ci		viacam_write_reg_mask(cam, VCR_CAPINTC, 0, VCR_CI_3BUFS);
41362306a36Sopenharmony_ci	} else {
41462306a36Sopenharmony_ci		cam_warn(cam, "Insufficient frame buffer memory\n");
41562306a36Sopenharmony_ci		return -ENOMEM;
41662306a36Sopenharmony_ci	}
41762306a36Sopenharmony_ci	/*
41862306a36Sopenharmony_ci	 * Set them up.
41962306a36Sopenharmony_ci	 */
42062306a36Sopenharmony_ci	offset = cam->fb_offset;
42162306a36Sopenharmony_ci	for (i = 0; i < cam->n_cap_bufs; i++) {
42262306a36Sopenharmony_ci		cam->cb_offsets[i] = offset;
42362306a36Sopenharmony_ci		cam->cb_addrs[i] = cam->fbmem + offset;
42462306a36Sopenharmony_ci		viacam_write_reg(cam, VCR_VBUF1 + i*4, offset & VCR_VBUF_MASK);
42562306a36Sopenharmony_ci		offset += cam->sensor_format.sizeimage;
42662306a36Sopenharmony_ci	}
42762306a36Sopenharmony_ci	return 0;
42862306a36Sopenharmony_ci}
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_ci/*
43162306a36Sopenharmony_ci * Set the scaling register for downscaling the image.
43262306a36Sopenharmony_ci *
43362306a36Sopenharmony_ci * This register works like this...  Vertical scaling is enabled
43462306a36Sopenharmony_ci * by bit 26; if that bit is set, downscaling is controlled by the
43562306a36Sopenharmony_ci * value in bits 16:25.	 Those bits are divided by 1024 to get
43662306a36Sopenharmony_ci * the scaling factor; setting just bit 25 thus cuts the height
43762306a36Sopenharmony_ci * in half.
43862306a36Sopenharmony_ci *
43962306a36Sopenharmony_ci * Horizontal scaling works about the same, but it's enabled by
44062306a36Sopenharmony_ci * bit 11, with bits 0:10 giving the numerator of a fraction
44162306a36Sopenharmony_ci * (over 2048) for the scaling value.
44262306a36Sopenharmony_ci *
44362306a36Sopenharmony_ci * This function is naive in that, if the user departs from
44462306a36Sopenharmony_ci * the 3x4 VGA scaling factor, the image will distort.	We
44562306a36Sopenharmony_ci * could work around that if it really seemed important.
44662306a36Sopenharmony_ci */
44762306a36Sopenharmony_cistatic void viacam_set_scale(struct via_camera *cam)
44862306a36Sopenharmony_ci{
44962306a36Sopenharmony_ci	unsigned int avscale;
45062306a36Sopenharmony_ci	int sf;
45162306a36Sopenharmony_ci
45262306a36Sopenharmony_ci	if (cam->user_format.width == VGA_WIDTH)
45362306a36Sopenharmony_ci		avscale = 0;
45462306a36Sopenharmony_ci	else {
45562306a36Sopenharmony_ci		sf = (cam->user_format.width*2048)/VGA_WIDTH;
45662306a36Sopenharmony_ci		avscale = VCR_AVS_HEN | sf;
45762306a36Sopenharmony_ci	}
45862306a36Sopenharmony_ci	if (cam->user_format.height < VGA_HEIGHT) {
45962306a36Sopenharmony_ci		sf = (1024*cam->user_format.height)/VGA_HEIGHT;
46062306a36Sopenharmony_ci		avscale |= VCR_AVS_VEN | (sf << 16);
46162306a36Sopenharmony_ci	}
46262306a36Sopenharmony_ci	viacam_write_reg(cam, VCR_AVSCALE, avscale);
46362306a36Sopenharmony_ci}
46462306a36Sopenharmony_ci
46562306a36Sopenharmony_ci
46662306a36Sopenharmony_ci/*
46762306a36Sopenharmony_ci * Configure image-related information into the capture engine.
46862306a36Sopenharmony_ci */
46962306a36Sopenharmony_cistatic void viacam_ctlr_image(struct via_camera *cam)
47062306a36Sopenharmony_ci{
47162306a36Sopenharmony_ci	int cicreg;
47262306a36Sopenharmony_ci
47362306a36Sopenharmony_ci	/*
47462306a36Sopenharmony_ci	 * Disable clock before messing with stuff - from the via
47562306a36Sopenharmony_ci	 * sample driver.
47662306a36Sopenharmony_ci	 */
47762306a36Sopenharmony_ci	viacam_write_reg(cam, VCR_CAPINTC, ~(VCR_CI_ENABLE|VCR_CI_CLKEN));
47862306a36Sopenharmony_ci	/*
47962306a36Sopenharmony_ci	 * Set up the controller for VGA resolution, modulo magic
48062306a36Sopenharmony_ci	 * offsets from the via sample driver.
48162306a36Sopenharmony_ci	 */
48262306a36Sopenharmony_ci	viacam_write_reg(cam, VCR_HORRANGE, 0x06200120);
48362306a36Sopenharmony_ci	viacam_write_reg(cam, VCR_VERTRANGE, 0x01de0000);
48462306a36Sopenharmony_ci	viacam_set_scale(cam);
48562306a36Sopenharmony_ci	/*
48662306a36Sopenharmony_ci	 * Image size info.
48762306a36Sopenharmony_ci	 */
48862306a36Sopenharmony_ci	viacam_write_reg(cam, VCR_MAXDATA,
48962306a36Sopenharmony_ci			(cam->sensor_format.height << 16) |
49062306a36Sopenharmony_ci			(cam->sensor_format.bytesperline >> 3));
49162306a36Sopenharmony_ci	viacam_write_reg(cam, VCR_MAXVBI, 0);
49262306a36Sopenharmony_ci	viacam_write_reg(cam, VCR_VSTRIDE,
49362306a36Sopenharmony_ci			cam->user_format.bytesperline & VCR_VS_STRIDE);
49462306a36Sopenharmony_ci	/*
49562306a36Sopenharmony_ci	 * Set up the capture interface control register,
49662306a36Sopenharmony_ci	 * everything but the "go" bit.
49762306a36Sopenharmony_ci	 *
49862306a36Sopenharmony_ci	 * The FIFO threshold is a bit of a magic number; 8 is what
49962306a36Sopenharmony_ci	 * VIA's sample code uses.
50062306a36Sopenharmony_ci	 */
50162306a36Sopenharmony_ci	cicreg = VCR_CI_CLKEN |
50262306a36Sopenharmony_ci		0x08000000 |		/* FIFO threshold */
50362306a36Sopenharmony_ci		VCR_CI_FLDINV |		/* OLPC-specific? */
50462306a36Sopenharmony_ci		VCR_CI_VREFINV |	/* OLPC-specific? */
50562306a36Sopenharmony_ci		VCR_CI_DIBOTH |		/* Capture both fields */
50662306a36Sopenharmony_ci		VCR_CI_CCIR601_8;
50762306a36Sopenharmony_ci	if (cam->n_cap_bufs == 3)
50862306a36Sopenharmony_ci		cicreg |= VCR_CI_3BUFS;
50962306a36Sopenharmony_ci	/*
51062306a36Sopenharmony_ci	 * YUV formats need different byte swapping than RGB.
51162306a36Sopenharmony_ci	 */
51262306a36Sopenharmony_ci	if (cam->user_format.pixelformat == V4L2_PIX_FMT_YUYV)
51362306a36Sopenharmony_ci		cicreg |= VCR_CI_YUYV;
51462306a36Sopenharmony_ci	else
51562306a36Sopenharmony_ci		cicreg |= VCR_CI_UYVY;
51662306a36Sopenharmony_ci	viacam_write_reg(cam, VCR_CAPINTC, cicreg);
51762306a36Sopenharmony_ci}
51862306a36Sopenharmony_ci
51962306a36Sopenharmony_ci
52062306a36Sopenharmony_cistatic int viacam_config_controller(struct via_camera *cam)
52162306a36Sopenharmony_ci{
52262306a36Sopenharmony_ci	int ret;
52362306a36Sopenharmony_ci	unsigned long flags;
52462306a36Sopenharmony_ci
52562306a36Sopenharmony_ci	spin_lock_irqsave(&cam->viadev->reg_lock, flags);
52662306a36Sopenharmony_ci	ret = viacam_ctlr_cbufs(cam);
52762306a36Sopenharmony_ci	if (!ret)
52862306a36Sopenharmony_ci		viacam_ctlr_image(cam);
52962306a36Sopenharmony_ci	spin_unlock_irqrestore(&cam->viadev->reg_lock, flags);
53062306a36Sopenharmony_ci	clear_bit(CF_CONFIG_NEEDED, &cam->flags);
53162306a36Sopenharmony_ci	return ret;
53262306a36Sopenharmony_ci}
53362306a36Sopenharmony_ci
53462306a36Sopenharmony_ci/*
53562306a36Sopenharmony_ci * Make it start grabbing data.
53662306a36Sopenharmony_ci */
53762306a36Sopenharmony_cistatic void viacam_start_engine(struct via_camera *cam)
53862306a36Sopenharmony_ci{
53962306a36Sopenharmony_ci	spin_lock_irq(&cam->viadev->reg_lock);
54062306a36Sopenharmony_ci	viacam_write_reg_mask(cam, VCR_CAPINTC, VCR_CI_ENABLE, VCR_CI_ENABLE);
54162306a36Sopenharmony_ci	viacam_int_enable(cam);
54262306a36Sopenharmony_ci	(void) viacam_read_reg(cam, VCR_CAPINTC); /* Force post */
54362306a36Sopenharmony_ci	cam->opstate = S_RUNNING;
54462306a36Sopenharmony_ci	spin_unlock_irq(&cam->viadev->reg_lock);
54562306a36Sopenharmony_ci}
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_ci
54862306a36Sopenharmony_cistatic void viacam_stop_engine(struct via_camera *cam)
54962306a36Sopenharmony_ci{
55062306a36Sopenharmony_ci	spin_lock_irq(&cam->viadev->reg_lock);
55162306a36Sopenharmony_ci	viacam_int_disable(cam);
55262306a36Sopenharmony_ci	viacam_write_reg_mask(cam, VCR_CAPINTC, 0, VCR_CI_ENABLE);
55362306a36Sopenharmony_ci	(void) viacam_read_reg(cam, VCR_CAPINTC); /* Force post */
55462306a36Sopenharmony_ci	cam->opstate = S_IDLE;
55562306a36Sopenharmony_ci	spin_unlock_irq(&cam->viadev->reg_lock);
55662306a36Sopenharmony_ci}
55762306a36Sopenharmony_ci
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_ci/* --------------------------------------------------------------------------*/
56062306a36Sopenharmony_ci/* vb2 callback ops */
56162306a36Sopenharmony_ci
56262306a36Sopenharmony_cistatic struct via_buffer *vb2_to_via_buffer(struct vb2_buffer *vb)
56362306a36Sopenharmony_ci{
56462306a36Sopenharmony_ci	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
56562306a36Sopenharmony_ci
56662306a36Sopenharmony_ci	return container_of(vbuf, struct via_buffer, vbuf);
56762306a36Sopenharmony_ci}
56862306a36Sopenharmony_ci
56962306a36Sopenharmony_cistatic void viacam_vb2_queue(struct vb2_buffer *vb)
57062306a36Sopenharmony_ci{
57162306a36Sopenharmony_ci	struct via_camera *cam = vb2_get_drv_priv(vb->vb2_queue);
57262306a36Sopenharmony_ci	struct via_buffer *via = vb2_to_via_buffer(vb);
57362306a36Sopenharmony_ci
57462306a36Sopenharmony_ci	list_add_tail(&via->queue, &cam->buffer_queue);
57562306a36Sopenharmony_ci}
57662306a36Sopenharmony_ci
57762306a36Sopenharmony_cistatic int viacam_vb2_prepare(struct vb2_buffer *vb)
57862306a36Sopenharmony_ci{
57962306a36Sopenharmony_ci	struct via_camera *cam = vb2_get_drv_priv(vb->vb2_queue);
58062306a36Sopenharmony_ci
58162306a36Sopenharmony_ci	if (vb2_plane_size(vb, 0) < cam->user_format.sizeimage) {
58262306a36Sopenharmony_ci		cam_dbg(cam,
58362306a36Sopenharmony_ci			"Plane size too small (%lu < %u)\n",
58462306a36Sopenharmony_ci			vb2_plane_size(vb, 0),
58562306a36Sopenharmony_ci			cam->user_format.sizeimage);
58662306a36Sopenharmony_ci		return -EINVAL;
58762306a36Sopenharmony_ci	}
58862306a36Sopenharmony_ci
58962306a36Sopenharmony_ci	vb2_set_plane_payload(vb, 0, cam->user_format.sizeimage);
59062306a36Sopenharmony_ci
59162306a36Sopenharmony_ci	return 0;
59262306a36Sopenharmony_ci}
59362306a36Sopenharmony_ci
59462306a36Sopenharmony_cistatic int viacam_vb2_queue_setup(struct vb2_queue *vq,
59562306a36Sopenharmony_ci				  unsigned int *nbufs,
59662306a36Sopenharmony_ci				  unsigned int *num_planes, unsigned int sizes[],
59762306a36Sopenharmony_ci				  struct device *alloc_devs[])
59862306a36Sopenharmony_ci{
59962306a36Sopenharmony_ci	struct via_camera *cam = vb2_get_drv_priv(vq);
60062306a36Sopenharmony_ci	int size = cam->user_format.sizeimage;
60162306a36Sopenharmony_ci
60262306a36Sopenharmony_ci	if (*num_planes)
60362306a36Sopenharmony_ci		return sizes[0] < size ? -EINVAL : 0;
60462306a36Sopenharmony_ci
60562306a36Sopenharmony_ci	*num_planes = 1;
60662306a36Sopenharmony_ci	sizes[0] = size;
60762306a36Sopenharmony_ci	return 0;
60862306a36Sopenharmony_ci}
60962306a36Sopenharmony_ci
61062306a36Sopenharmony_cistatic int viacam_vb2_start_streaming(struct vb2_queue *vq, unsigned int count)
61162306a36Sopenharmony_ci{
61262306a36Sopenharmony_ci	struct via_camera *cam = vb2_get_drv_priv(vq);
61362306a36Sopenharmony_ci	struct via_buffer *buf, *tmp;
61462306a36Sopenharmony_ci	int ret = 0;
61562306a36Sopenharmony_ci
61662306a36Sopenharmony_ci	if (cam->opstate != S_IDLE) {
61762306a36Sopenharmony_ci		ret = -EBUSY;
61862306a36Sopenharmony_ci		goto out;
61962306a36Sopenharmony_ci	}
62062306a36Sopenharmony_ci	/*
62162306a36Sopenharmony_ci	 * Configure things if need be.
62262306a36Sopenharmony_ci	 */
62362306a36Sopenharmony_ci	if (test_bit(CF_CONFIG_NEEDED, &cam->flags)) {
62462306a36Sopenharmony_ci		ret = viacam_configure_sensor(cam);
62562306a36Sopenharmony_ci		if (ret)
62662306a36Sopenharmony_ci			goto out;
62762306a36Sopenharmony_ci		ret = viacam_config_controller(cam);
62862306a36Sopenharmony_ci		if (ret)
62962306a36Sopenharmony_ci			goto out;
63062306a36Sopenharmony_ci	}
63162306a36Sopenharmony_ci	cam->sequence = 0;
63262306a36Sopenharmony_ci	/*
63362306a36Sopenharmony_ci	 * If the CPU goes into C3, the DMA transfer gets corrupted and
63462306a36Sopenharmony_ci	 * users start filing unsightly bug reports.  Put in a "latency"
63562306a36Sopenharmony_ci	 * requirement which will keep the CPU out of the deeper sleep
63662306a36Sopenharmony_ci	 * states.
63762306a36Sopenharmony_ci	 */
63862306a36Sopenharmony_ci	cpu_latency_qos_add_request(&cam->qos_request, 50);
63962306a36Sopenharmony_ci	viacam_start_engine(cam);
64062306a36Sopenharmony_ci	return 0;
64162306a36Sopenharmony_ciout:
64262306a36Sopenharmony_ci	list_for_each_entry_safe(buf, tmp, &cam->buffer_queue, queue) {
64362306a36Sopenharmony_ci		list_del(&buf->queue);
64462306a36Sopenharmony_ci		vb2_buffer_done(&buf->vbuf.vb2_buf, VB2_BUF_STATE_QUEUED);
64562306a36Sopenharmony_ci	}
64662306a36Sopenharmony_ci	return ret;
64762306a36Sopenharmony_ci}
64862306a36Sopenharmony_ci
64962306a36Sopenharmony_cistatic void viacam_vb2_stop_streaming(struct vb2_queue *vq)
65062306a36Sopenharmony_ci{
65162306a36Sopenharmony_ci	struct via_camera *cam = vb2_get_drv_priv(vq);
65262306a36Sopenharmony_ci	struct via_buffer *buf, *tmp;
65362306a36Sopenharmony_ci
65462306a36Sopenharmony_ci	cpu_latency_qos_remove_request(&cam->qos_request);
65562306a36Sopenharmony_ci	viacam_stop_engine(cam);
65662306a36Sopenharmony_ci
65762306a36Sopenharmony_ci	list_for_each_entry_safe(buf, tmp, &cam->buffer_queue, queue) {
65862306a36Sopenharmony_ci		list_del(&buf->queue);
65962306a36Sopenharmony_ci		vb2_buffer_done(&buf->vbuf.vb2_buf, VB2_BUF_STATE_ERROR);
66062306a36Sopenharmony_ci	}
66162306a36Sopenharmony_ci}
66262306a36Sopenharmony_ci
66362306a36Sopenharmony_cistatic const struct vb2_ops viacam_vb2_ops = {
66462306a36Sopenharmony_ci	.queue_setup		= viacam_vb2_queue_setup,
66562306a36Sopenharmony_ci	.buf_queue		= viacam_vb2_queue,
66662306a36Sopenharmony_ci	.buf_prepare		= viacam_vb2_prepare,
66762306a36Sopenharmony_ci	.start_streaming	= viacam_vb2_start_streaming,
66862306a36Sopenharmony_ci	.stop_streaming		= viacam_vb2_stop_streaming,
66962306a36Sopenharmony_ci	.wait_prepare		= vb2_ops_wait_prepare,
67062306a36Sopenharmony_ci	.wait_finish		= vb2_ops_wait_finish,
67162306a36Sopenharmony_ci};
67262306a36Sopenharmony_ci
67362306a36Sopenharmony_ci/* --------------------------------------------------------------------------*/
67462306a36Sopenharmony_ci/* File operations */
67562306a36Sopenharmony_ci
67662306a36Sopenharmony_cistatic int viacam_open(struct file *filp)
67762306a36Sopenharmony_ci{
67862306a36Sopenharmony_ci	struct via_camera *cam = video_drvdata(filp);
67962306a36Sopenharmony_ci	int ret;
68062306a36Sopenharmony_ci
68162306a36Sopenharmony_ci	/*
68262306a36Sopenharmony_ci	 * Note the new user.  If this is the first one, we'll also
68362306a36Sopenharmony_ci	 * need to power up the sensor.
68462306a36Sopenharmony_ci	 */
68562306a36Sopenharmony_ci	mutex_lock(&cam->lock);
68662306a36Sopenharmony_ci	ret = v4l2_fh_open(filp);
68762306a36Sopenharmony_ci	if (ret)
68862306a36Sopenharmony_ci		goto out;
68962306a36Sopenharmony_ci	if (v4l2_fh_is_singular_file(filp)) {
69062306a36Sopenharmony_ci		ret = viafb_request_dma();
69162306a36Sopenharmony_ci
69262306a36Sopenharmony_ci		if (ret) {
69362306a36Sopenharmony_ci			v4l2_fh_release(filp);
69462306a36Sopenharmony_ci			goto out;
69562306a36Sopenharmony_ci		}
69662306a36Sopenharmony_ci		via_sensor_power_up(cam);
69762306a36Sopenharmony_ci		set_bit(CF_CONFIG_NEEDED, &cam->flags);
69862306a36Sopenharmony_ci	}
69962306a36Sopenharmony_ciout:
70062306a36Sopenharmony_ci	mutex_unlock(&cam->lock);
70162306a36Sopenharmony_ci	return ret;
70262306a36Sopenharmony_ci}
70362306a36Sopenharmony_ci
70462306a36Sopenharmony_cistatic int viacam_release(struct file *filp)
70562306a36Sopenharmony_ci{
70662306a36Sopenharmony_ci	struct via_camera *cam = video_drvdata(filp);
70762306a36Sopenharmony_ci	bool last_open;
70862306a36Sopenharmony_ci
70962306a36Sopenharmony_ci	mutex_lock(&cam->lock);
71062306a36Sopenharmony_ci	last_open = v4l2_fh_is_singular_file(filp);
71162306a36Sopenharmony_ci	_vb2_fop_release(filp, NULL);
71262306a36Sopenharmony_ci	/*
71362306a36Sopenharmony_ci	 * Last one out needs to turn out the lights.
71462306a36Sopenharmony_ci	 */
71562306a36Sopenharmony_ci	if (last_open) {
71662306a36Sopenharmony_ci		via_sensor_power_down(cam);
71762306a36Sopenharmony_ci		viafb_release_dma();
71862306a36Sopenharmony_ci	}
71962306a36Sopenharmony_ci	mutex_unlock(&cam->lock);
72062306a36Sopenharmony_ci	return 0;
72162306a36Sopenharmony_ci}
72262306a36Sopenharmony_ci
72362306a36Sopenharmony_cistatic const struct v4l2_file_operations viacam_fops = {
72462306a36Sopenharmony_ci	.owner		= THIS_MODULE,
72562306a36Sopenharmony_ci	.open		= viacam_open,
72662306a36Sopenharmony_ci	.release	= viacam_release,
72762306a36Sopenharmony_ci	.read		= vb2_fop_read,
72862306a36Sopenharmony_ci	.poll		= vb2_fop_poll,
72962306a36Sopenharmony_ci	.mmap		= vb2_fop_mmap,
73062306a36Sopenharmony_ci	.unlocked_ioctl = video_ioctl2,
73162306a36Sopenharmony_ci};
73262306a36Sopenharmony_ci
73362306a36Sopenharmony_ci/*----------------------------------------------------------------------------*/
73462306a36Sopenharmony_ci/*
73562306a36Sopenharmony_ci * The long list of v4l2 ioctl ops
73662306a36Sopenharmony_ci */
73762306a36Sopenharmony_ci
73862306a36Sopenharmony_ci/*
73962306a36Sopenharmony_ci * Only one input.
74062306a36Sopenharmony_ci */
74162306a36Sopenharmony_cistatic int viacam_enum_input(struct file *filp, void *priv,
74262306a36Sopenharmony_ci		struct v4l2_input *input)
74362306a36Sopenharmony_ci{
74462306a36Sopenharmony_ci	if (input->index != 0)
74562306a36Sopenharmony_ci		return -EINVAL;
74662306a36Sopenharmony_ci
74762306a36Sopenharmony_ci	input->type = V4L2_INPUT_TYPE_CAMERA;
74862306a36Sopenharmony_ci	strscpy(input->name, "Camera", sizeof(input->name));
74962306a36Sopenharmony_ci	return 0;
75062306a36Sopenharmony_ci}
75162306a36Sopenharmony_ci
75262306a36Sopenharmony_cistatic int viacam_g_input(struct file *filp, void *priv, unsigned int *i)
75362306a36Sopenharmony_ci{
75462306a36Sopenharmony_ci	*i = 0;
75562306a36Sopenharmony_ci	return 0;
75662306a36Sopenharmony_ci}
75762306a36Sopenharmony_ci
75862306a36Sopenharmony_cistatic int viacam_s_input(struct file *filp, void *priv, unsigned int i)
75962306a36Sopenharmony_ci{
76062306a36Sopenharmony_ci	if (i != 0)
76162306a36Sopenharmony_ci		return -EINVAL;
76262306a36Sopenharmony_ci	return 0;
76362306a36Sopenharmony_ci}
76462306a36Sopenharmony_ci
76562306a36Sopenharmony_ci/*
76662306a36Sopenharmony_ci * Video format stuff.	Here is our default format until
76762306a36Sopenharmony_ci * user space messes with things.
76862306a36Sopenharmony_ci */
76962306a36Sopenharmony_cistatic const struct v4l2_pix_format viacam_def_pix_format = {
77062306a36Sopenharmony_ci	.width		= VGA_WIDTH,
77162306a36Sopenharmony_ci	.height		= VGA_HEIGHT,
77262306a36Sopenharmony_ci	.pixelformat	= V4L2_PIX_FMT_YUYV,
77362306a36Sopenharmony_ci	.field		= V4L2_FIELD_NONE,
77462306a36Sopenharmony_ci	.bytesperline	= VGA_WIDTH * 2,
77562306a36Sopenharmony_ci	.sizeimage	= VGA_WIDTH * VGA_HEIGHT * 2,
77662306a36Sopenharmony_ci	.colorspace	= V4L2_COLORSPACE_SRGB,
77762306a36Sopenharmony_ci};
77862306a36Sopenharmony_ci
77962306a36Sopenharmony_cistatic const u32 via_def_mbus_code = MEDIA_BUS_FMT_YUYV8_2X8;
78062306a36Sopenharmony_ci
78162306a36Sopenharmony_cistatic int viacam_enum_fmt_vid_cap(struct file *filp, void *priv,
78262306a36Sopenharmony_ci		struct v4l2_fmtdesc *fmt)
78362306a36Sopenharmony_ci{
78462306a36Sopenharmony_ci	if (fmt->index >= N_VIA_FMTS)
78562306a36Sopenharmony_ci		return -EINVAL;
78662306a36Sopenharmony_ci	fmt->pixelformat = via_formats[fmt->index].pixelformat;
78762306a36Sopenharmony_ci	return 0;
78862306a36Sopenharmony_ci}
78962306a36Sopenharmony_ci
79062306a36Sopenharmony_ci/*
79162306a36Sopenharmony_ci * Figure out proper image dimensions, but always force the
79262306a36Sopenharmony_ci * sensor to VGA.
79362306a36Sopenharmony_ci */
79462306a36Sopenharmony_cistatic void viacam_fmt_pre(struct v4l2_pix_format *userfmt,
79562306a36Sopenharmony_ci		struct v4l2_pix_format *sensorfmt)
79662306a36Sopenharmony_ci{
79762306a36Sopenharmony_ci	*sensorfmt = *userfmt;
79862306a36Sopenharmony_ci	if (userfmt->width < QCIF_WIDTH || userfmt->height < QCIF_HEIGHT) {
79962306a36Sopenharmony_ci		userfmt->width = QCIF_WIDTH;
80062306a36Sopenharmony_ci		userfmt->height = QCIF_HEIGHT;
80162306a36Sopenharmony_ci	}
80262306a36Sopenharmony_ci	if (userfmt->width > VGA_WIDTH || userfmt->height > VGA_HEIGHT) {
80362306a36Sopenharmony_ci		userfmt->width = VGA_WIDTH;
80462306a36Sopenharmony_ci		userfmt->height = VGA_HEIGHT;
80562306a36Sopenharmony_ci	}
80662306a36Sopenharmony_ci	sensorfmt->width = VGA_WIDTH;
80762306a36Sopenharmony_ci	sensorfmt->height = VGA_HEIGHT;
80862306a36Sopenharmony_ci}
80962306a36Sopenharmony_ci
81062306a36Sopenharmony_cistatic void viacam_fmt_post(struct v4l2_pix_format *userfmt,
81162306a36Sopenharmony_ci		struct v4l2_pix_format *sensorfmt)
81262306a36Sopenharmony_ci{
81362306a36Sopenharmony_ci	struct via_format *f = via_find_format(userfmt->pixelformat);
81462306a36Sopenharmony_ci
81562306a36Sopenharmony_ci	sensorfmt->bytesperline = sensorfmt->width * f->bpp;
81662306a36Sopenharmony_ci	sensorfmt->sizeimage = sensorfmt->height * sensorfmt->bytesperline;
81762306a36Sopenharmony_ci	userfmt->pixelformat = sensorfmt->pixelformat;
81862306a36Sopenharmony_ci	userfmt->field = sensorfmt->field;
81962306a36Sopenharmony_ci	userfmt->bytesperline = 2 * userfmt->width;
82062306a36Sopenharmony_ci	userfmt->sizeimage = userfmt->bytesperline * userfmt->height;
82162306a36Sopenharmony_ci	userfmt->colorspace = sensorfmt->colorspace;
82262306a36Sopenharmony_ci	userfmt->ycbcr_enc = sensorfmt->ycbcr_enc;
82362306a36Sopenharmony_ci	userfmt->quantization = sensorfmt->quantization;
82462306a36Sopenharmony_ci	userfmt->xfer_func = sensorfmt->xfer_func;
82562306a36Sopenharmony_ci}
82662306a36Sopenharmony_ci
82762306a36Sopenharmony_ci
82862306a36Sopenharmony_ci/*
82962306a36Sopenharmony_ci * The real work of figuring out a workable format.
83062306a36Sopenharmony_ci */
83162306a36Sopenharmony_cistatic int viacam_do_try_fmt(struct via_camera *cam,
83262306a36Sopenharmony_ci		struct v4l2_pix_format *upix, struct v4l2_pix_format *spix)
83362306a36Sopenharmony_ci{
83462306a36Sopenharmony_ci	int ret;
83562306a36Sopenharmony_ci	struct v4l2_subdev_pad_config pad_cfg;
83662306a36Sopenharmony_ci	struct v4l2_subdev_state pad_state = {
83762306a36Sopenharmony_ci		.pads = &pad_cfg,
83862306a36Sopenharmony_ci	};
83962306a36Sopenharmony_ci	struct v4l2_subdev_format format = {
84062306a36Sopenharmony_ci		.which = V4L2_SUBDEV_FORMAT_TRY,
84162306a36Sopenharmony_ci	};
84262306a36Sopenharmony_ci	struct via_format *f = via_find_format(upix->pixelformat);
84362306a36Sopenharmony_ci
84462306a36Sopenharmony_ci	upix->pixelformat = f->pixelformat;
84562306a36Sopenharmony_ci	viacam_fmt_pre(upix, spix);
84662306a36Sopenharmony_ci	v4l2_fill_mbus_format(&format.format, spix, f->mbus_code);
84762306a36Sopenharmony_ci	ret = sensor_call(cam, pad, set_fmt, &pad_state, &format);
84862306a36Sopenharmony_ci	v4l2_fill_pix_format(spix, &format.format);
84962306a36Sopenharmony_ci	viacam_fmt_post(upix, spix);
85062306a36Sopenharmony_ci	return ret;
85162306a36Sopenharmony_ci}
85262306a36Sopenharmony_ci
85362306a36Sopenharmony_ci
85462306a36Sopenharmony_ci
85562306a36Sopenharmony_cistatic int viacam_try_fmt_vid_cap(struct file *filp, void *priv,
85662306a36Sopenharmony_ci		struct v4l2_format *fmt)
85762306a36Sopenharmony_ci{
85862306a36Sopenharmony_ci	struct via_camera *cam = video_drvdata(filp);
85962306a36Sopenharmony_ci	struct v4l2_format sfmt;
86062306a36Sopenharmony_ci
86162306a36Sopenharmony_ci	return viacam_do_try_fmt(cam, &fmt->fmt.pix, &sfmt.fmt.pix);
86262306a36Sopenharmony_ci}
86362306a36Sopenharmony_ci
86462306a36Sopenharmony_ci
86562306a36Sopenharmony_cistatic int viacam_g_fmt_vid_cap(struct file *filp, void *priv,
86662306a36Sopenharmony_ci		struct v4l2_format *fmt)
86762306a36Sopenharmony_ci{
86862306a36Sopenharmony_ci	struct via_camera *cam = video_drvdata(filp);
86962306a36Sopenharmony_ci
87062306a36Sopenharmony_ci	fmt->fmt.pix = cam->user_format;
87162306a36Sopenharmony_ci	return 0;
87262306a36Sopenharmony_ci}
87362306a36Sopenharmony_ci
87462306a36Sopenharmony_cistatic int viacam_s_fmt_vid_cap(struct file *filp, void *priv,
87562306a36Sopenharmony_ci		struct v4l2_format *fmt)
87662306a36Sopenharmony_ci{
87762306a36Sopenharmony_ci	struct via_camera *cam = video_drvdata(filp);
87862306a36Sopenharmony_ci	int ret;
87962306a36Sopenharmony_ci	struct v4l2_format sfmt;
88062306a36Sopenharmony_ci	struct via_format *f = via_find_format(fmt->fmt.pix.pixelformat);
88162306a36Sopenharmony_ci
88262306a36Sopenharmony_ci	/*
88362306a36Sopenharmony_ci	 * Camera must be idle or we can't mess with the
88462306a36Sopenharmony_ci	 * video setup.
88562306a36Sopenharmony_ci	 */
88662306a36Sopenharmony_ci	if (cam->opstate != S_IDLE)
88762306a36Sopenharmony_ci		return -EBUSY;
88862306a36Sopenharmony_ci	/*
88962306a36Sopenharmony_ci	 * Let the sensor code look over and tweak the
89062306a36Sopenharmony_ci	 * requested formatting.
89162306a36Sopenharmony_ci	 */
89262306a36Sopenharmony_ci	ret = viacam_do_try_fmt(cam, &fmt->fmt.pix, &sfmt.fmt.pix);
89362306a36Sopenharmony_ci	if (ret)
89462306a36Sopenharmony_ci		return ret;
89562306a36Sopenharmony_ci	/*
89662306a36Sopenharmony_ci	 * OK, let's commit to the new format.
89762306a36Sopenharmony_ci	 */
89862306a36Sopenharmony_ci	cam->user_format = fmt->fmt.pix;
89962306a36Sopenharmony_ci	cam->sensor_format = sfmt.fmt.pix;
90062306a36Sopenharmony_ci	cam->mbus_code = f->mbus_code;
90162306a36Sopenharmony_ci	ret = viacam_configure_sensor(cam);
90262306a36Sopenharmony_ci	if (!ret)
90362306a36Sopenharmony_ci		ret = viacam_config_controller(cam);
90462306a36Sopenharmony_ci	return ret;
90562306a36Sopenharmony_ci}
90662306a36Sopenharmony_ci
90762306a36Sopenharmony_cistatic int viacam_querycap(struct file *filp, void *priv,
90862306a36Sopenharmony_ci		struct v4l2_capability *cap)
90962306a36Sopenharmony_ci{
91062306a36Sopenharmony_ci	strscpy(cap->driver, "via-camera", sizeof(cap->driver));
91162306a36Sopenharmony_ci	strscpy(cap->card, "via-camera", sizeof(cap->card));
91262306a36Sopenharmony_ci	strscpy(cap->bus_info, "platform:via-camera", sizeof(cap->bus_info));
91362306a36Sopenharmony_ci	return 0;
91462306a36Sopenharmony_ci}
91562306a36Sopenharmony_ci
91662306a36Sopenharmony_ci/* G/S_PARM */
91762306a36Sopenharmony_ci
91862306a36Sopenharmony_cistatic int viacam_g_parm(struct file *filp, void *priv,
91962306a36Sopenharmony_ci		struct v4l2_streamparm *parm)
92062306a36Sopenharmony_ci{
92162306a36Sopenharmony_ci	struct via_camera *cam = video_drvdata(filp);
92262306a36Sopenharmony_ci
92362306a36Sopenharmony_ci	return v4l2_g_parm_cap(video_devdata(filp), cam->sensor, parm);
92462306a36Sopenharmony_ci}
92562306a36Sopenharmony_ci
92662306a36Sopenharmony_cistatic int viacam_s_parm(struct file *filp, void *priv,
92762306a36Sopenharmony_ci		struct v4l2_streamparm *parm)
92862306a36Sopenharmony_ci{
92962306a36Sopenharmony_ci	struct via_camera *cam = video_drvdata(filp);
93062306a36Sopenharmony_ci
93162306a36Sopenharmony_ci	return v4l2_s_parm_cap(video_devdata(filp), cam->sensor, parm);
93262306a36Sopenharmony_ci}
93362306a36Sopenharmony_ci
93462306a36Sopenharmony_cistatic int viacam_enum_framesizes(struct file *filp, void *priv,
93562306a36Sopenharmony_ci		struct v4l2_frmsizeenum *sizes)
93662306a36Sopenharmony_ci{
93762306a36Sopenharmony_ci	unsigned int i;
93862306a36Sopenharmony_ci
93962306a36Sopenharmony_ci	if (sizes->index != 0)
94062306a36Sopenharmony_ci		return -EINVAL;
94162306a36Sopenharmony_ci	for (i = 0; i < N_VIA_FMTS; i++)
94262306a36Sopenharmony_ci		if (sizes->pixel_format == via_formats[i].pixelformat)
94362306a36Sopenharmony_ci			break;
94462306a36Sopenharmony_ci	if (i >= N_VIA_FMTS)
94562306a36Sopenharmony_ci		return -EINVAL;
94662306a36Sopenharmony_ci	sizes->type = V4L2_FRMSIZE_TYPE_CONTINUOUS;
94762306a36Sopenharmony_ci	sizes->stepwise.min_width = QCIF_WIDTH;
94862306a36Sopenharmony_ci	sizes->stepwise.min_height = QCIF_HEIGHT;
94962306a36Sopenharmony_ci	sizes->stepwise.max_width = VGA_WIDTH;
95062306a36Sopenharmony_ci	sizes->stepwise.max_height = VGA_HEIGHT;
95162306a36Sopenharmony_ci	sizes->stepwise.step_width = sizes->stepwise.step_height = 1;
95262306a36Sopenharmony_ci	return 0;
95362306a36Sopenharmony_ci}
95462306a36Sopenharmony_ci
95562306a36Sopenharmony_cistatic int viacam_enum_frameintervals(struct file *filp, void *priv,
95662306a36Sopenharmony_ci		struct v4l2_frmivalenum *interval)
95762306a36Sopenharmony_ci{
95862306a36Sopenharmony_ci	struct via_camera *cam = video_drvdata(filp);
95962306a36Sopenharmony_ci	struct v4l2_subdev_frame_interval_enum fie = {
96062306a36Sopenharmony_ci		.index = interval->index,
96162306a36Sopenharmony_ci		.code = cam->mbus_code,
96262306a36Sopenharmony_ci		.width = cam->sensor_format.width,
96362306a36Sopenharmony_ci		.height = cam->sensor_format.height,
96462306a36Sopenharmony_ci		.which = V4L2_SUBDEV_FORMAT_ACTIVE,
96562306a36Sopenharmony_ci	};
96662306a36Sopenharmony_ci	unsigned int i;
96762306a36Sopenharmony_ci	int ret;
96862306a36Sopenharmony_ci
96962306a36Sopenharmony_ci	for (i = 0; i < N_VIA_FMTS; i++)
97062306a36Sopenharmony_ci		if (interval->pixel_format == via_formats[i].pixelformat)
97162306a36Sopenharmony_ci			break;
97262306a36Sopenharmony_ci	if (i >= N_VIA_FMTS)
97362306a36Sopenharmony_ci		return -EINVAL;
97462306a36Sopenharmony_ci	if (interval->width < QCIF_WIDTH || interval->width > VGA_WIDTH ||
97562306a36Sopenharmony_ci	    interval->height < QCIF_HEIGHT || interval->height > VGA_HEIGHT)
97662306a36Sopenharmony_ci		return -EINVAL;
97762306a36Sopenharmony_ci	ret = sensor_call(cam, pad, enum_frame_interval, NULL, &fie);
97862306a36Sopenharmony_ci	if (ret)
97962306a36Sopenharmony_ci		return ret;
98062306a36Sopenharmony_ci	interval->type = V4L2_FRMIVAL_TYPE_DISCRETE;
98162306a36Sopenharmony_ci	interval->discrete = fie.interval;
98262306a36Sopenharmony_ci	return 0;
98362306a36Sopenharmony_ci}
98462306a36Sopenharmony_ci
98562306a36Sopenharmony_cistatic const struct v4l2_ioctl_ops viacam_ioctl_ops = {
98662306a36Sopenharmony_ci	.vidioc_enum_input	= viacam_enum_input,
98762306a36Sopenharmony_ci	.vidioc_g_input		= viacam_g_input,
98862306a36Sopenharmony_ci	.vidioc_s_input		= viacam_s_input,
98962306a36Sopenharmony_ci	.vidioc_enum_fmt_vid_cap = viacam_enum_fmt_vid_cap,
99062306a36Sopenharmony_ci	.vidioc_try_fmt_vid_cap = viacam_try_fmt_vid_cap,
99162306a36Sopenharmony_ci	.vidioc_g_fmt_vid_cap	= viacam_g_fmt_vid_cap,
99262306a36Sopenharmony_ci	.vidioc_s_fmt_vid_cap	= viacam_s_fmt_vid_cap,
99362306a36Sopenharmony_ci	.vidioc_querycap	= viacam_querycap,
99462306a36Sopenharmony_ci	.vidioc_reqbufs		= vb2_ioctl_reqbufs,
99562306a36Sopenharmony_ci	.vidioc_create_bufs	= vb2_ioctl_create_bufs,
99662306a36Sopenharmony_ci	.vidioc_querybuf	= vb2_ioctl_querybuf,
99762306a36Sopenharmony_ci	.vidioc_prepare_buf	= vb2_ioctl_prepare_buf,
99862306a36Sopenharmony_ci	.vidioc_qbuf		= vb2_ioctl_qbuf,
99962306a36Sopenharmony_ci	.vidioc_dqbuf		= vb2_ioctl_dqbuf,
100062306a36Sopenharmony_ci	.vidioc_expbuf		= vb2_ioctl_expbuf,
100162306a36Sopenharmony_ci	.vidioc_streamon	= vb2_ioctl_streamon,
100262306a36Sopenharmony_ci	.vidioc_streamoff	= vb2_ioctl_streamoff,
100362306a36Sopenharmony_ci	.vidioc_g_parm		= viacam_g_parm,
100462306a36Sopenharmony_ci	.vidioc_s_parm		= viacam_s_parm,
100562306a36Sopenharmony_ci	.vidioc_enum_framesizes = viacam_enum_framesizes,
100662306a36Sopenharmony_ci	.vidioc_enum_frameintervals = viacam_enum_frameintervals,
100762306a36Sopenharmony_ci	.vidioc_subscribe_event		= v4l2_ctrl_subscribe_event,
100862306a36Sopenharmony_ci	.vidioc_unsubscribe_event	= v4l2_event_unsubscribe,
100962306a36Sopenharmony_ci};
101062306a36Sopenharmony_ci
101162306a36Sopenharmony_ci/*----------------------------------------------------------------------------*/
101262306a36Sopenharmony_ci
101362306a36Sopenharmony_ci/*
101462306a36Sopenharmony_ci * Power management.
101562306a36Sopenharmony_ci */
101662306a36Sopenharmony_ci#ifdef CONFIG_PM
101762306a36Sopenharmony_ci
101862306a36Sopenharmony_cistatic int viacam_suspend(void *priv)
101962306a36Sopenharmony_ci{
102062306a36Sopenharmony_ci	struct via_camera *cam = priv;
102162306a36Sopenharmony_ci	enum viacam_opstate state = cam->opstate;
102262306a36Sopenharmony_ci
102362306a36Sopenharmony_ci	if (cam->opstate != S_IDLE) {
102462306a36Sopenharmony_ci		viacam_stop_engine(cam);
102562306a36Sopenharmony_ci		cam->opstate = state; /* So resume restarts */
102662306a36Sopenharmony_ci	}
102762306a36Sopenharmony_ci
102862306a36Sopenharmony_ci	return 0;
102962306a36Sopenharmony_ci}
103062306a36Sopenharmony_ci
103162306a36Sopenharmony_cistatic int viacam_resume(void *priv)
103262306a36Sopenharmony_ci{
103362306a36Sopenharmony_ci	struct via_camera *cam = priv;
103462306a36Sopenharmony_ci	int ret = 0;
103562306a36Sopenharmony_ci
103662306a36Sopenharmony_ci	/*
103762306a36Sopenharmony_ci	 * Get back to a reasonable operating state.
103862306a36Sopenharmony_ci	 */
103962306a36Sopenharmony_ci	via_write_reg_mask(VIASR, 0x78, 0, 0x80);
104062306a36Sopenharmony_ci	via_write_reg_mask(VIASR, 0x1e, 0xc0, 0xc0);
104162306a36Sopenharmony_ci	viacam_int_disable(cam);
104262306a36Sopenharmony_ci	set_bit(CF_CONFIG_NEEDED, &cam->flags);
104362306a36Sopenharmony_ci	/*
104462306a36Sopenharmony_ci	 * Make sure the sensor's power state is correct
104562306a36Sopenharmony_ci	 */
104662306a36Sopenharmony_ci	if (!list_empty(&cam->vdev.fh_list))
104762306a36Sopenharmony_ci		via_sensor_power_up(cam);
104862306a36Sopenharmony_ci	else
104962306a36Sopenharmony_ci		via_sensor_power_down(cam);
105062306a36Sopenharmony_ci	/*
105162306a36Sopenharmony_ci	 * If it was operating, try to restart it.
105262306a36Sopenharmony_ci	 */
105362306a36Sopenharmony_ci	if (cam->opstate != S_IDLE) {
105462306a36Sopenharmony_ci		mutex_lock(&cam->lock);
105562306a36Sopenharmony_ci		ret = viacam_configure_sensor(cam);
105662306a36Sopenharmony_ci		if (!ret)
105762306a36Sopenharmony_ci			ret = viacam_config_controller(cam);
105862306a36Sopenharmony_ci		mutex_unlock(&cam->lock);
105962306a36Sopenharmony_ci		if (!ret)
106062306a36Sopenharmony_ci			viacam_start_engine(cam);
106162306a36Sopenharmony_ci	}
106262306a36Sopenharmony_ci
106362306a36Sopenharmony_ci	return ret;
106462306a36Sopenharmony_ci}
106562306a36Sopenharmony_ci
106662306a36Sopenharmony_cistatic struct viafb_pm_hooks viacam_pm_hooks = {
106762306a36Sopenharmony_ci	.suspend = viacam_suspend,
106862306a36Sopenharmony_ci	.resume = viacam_resume
106962306a36Sopenharmony_ci};
107062306a36Sopenharmony_ci
107162306a36Sopenharmony_ci#endif /* CONFIG_PM */
107262306a36Sopenharmony_ci
107362306a36Sopenharmony_ci/*
107462306a36Sopenharmony_ci * Setup stuff.
107562306a36Sopenharmony_ci */
107662306a36Sopenharmony_ci
107762306a36Sopenharmony_cistatic const struct video_device viacam_v4l_template = {
107862306a36Sopenharmony_ci	.name		= "via-camera",
107962306a36Sopenharmony_ci	.minor		= -1,
108062306a36Sopenharmony_ci	.fops		= &viacam_fops,
108162306a36Sopenharmony_ci	.ioctl_ops	= &viacam_ioctl_ops,
108262306a36Sopenharmony_ci	.release	= video_device_release_empty, /* Check this */
108362306a36Sopenharmony_ci	.device_caps	= V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE |
108462306a36Sopenharmony_ci			  V4L2_CAP_STREAMING,
108562306a36Sopenharmony_ci};
108662306a36Sopenharmony_ci
108762306a36Sopenharmony_ci/*
108862306a36Sopenharmony_ci * The OLPC folks put the serial port on the same pin as
108962306a36Sopenharmony_ci * the camera.	They also get grumpy if we break the
109062306a36Sopenharmony_ci * serial port and keep them from using it.  So we have
109162306a36Sopenharmony_ci * to check the serial enable bit and not step on it.
109262306a36Sopenharmony_ci */
109362306a36Sopenharmony_ci#define VIACAM_SERIAL_DEVFN 0x88
109462306a36Sopenharmony_ci#define VIACAM_SERIAL_CREG 0x46
109562306a36Sopenharmony_ci#define VIACAM_SERIAL_BIT 0x40
109662306a36Sopenharmony_ci
109762306a36Sopenharmony_cistatic bool viacam_serial_is_enabled(void)
109862306a36Sopenharmony_ci{
109962306a36Sopenharmony_ci	struct pci_bus *pbus = pci_find_bus(0, 0);
110062306a36Sopenharmony_ci	u8 cbyte;
110162306a36Sopenharmony_ci
110262306a36Sopenharmony_ci	if (!pbus)
110362306a36Sopenharmony_ci		return false;
110462306a36Sopenharmony_ci	pci_bus_read_config_byte(pbus, VIACAM_SERIAL_DEVFN,
110562306a36Sopenharmony_ci			VIACAM_SERIAL_CREG, &cbyte);
110662306a36Sopenharmony_ci	if ((cbyte & VIACAM_SERIAL_BIT) == 0)
110762306a36Sopenharmony_ci		return false; /* Not enabled */
110862306a36Sopenharmony_ci	if (!override_serial) {
110962306a36Sopenharmony_ci		printk(KERN_NOTICE "Via camera: serial port is enabled, " \
111062306a36Sopenharmony_ci				"refusing to load.\n");
111162306a36Sopenharmony_ci		printk(KERN_NOTICE "Specify override_serial=1 to force " \
111262306a36Sopenharmony_ci				"module loading.\n");
111362306a36Sopenharmony_ci		return true;
111462306a36Sopenharmony_ci	}
111562306a36Sopenharmony_ci	printk(KERN_NOTICE "Via camera: overriding serial port\n");
111662306a36Sopenharmony_ci	pci_bus_write_config_byte(pbus, VIACAM_SERIAL_DEVFN,
111762306a36Sopenharmony_ci			VIACAM_SERIAL_CREG, cbyte & ~VIACAM_SERIAL_BIT);
111862306a36Sopenharmony_ci	return false;
111962306a36Sopenharmony_ci}
112062306a36Sopenharmony_ci
112162306a36Sopenharmony_cistatic struct ov7670_config sensor_cfg = {
112262306a36Sopenharmony_ci	/* The XO-1.5 (only known user) clocks the camera at 90MHz. */
112362306a36Sopenharmony_ci	.clock_speed = 90,
112462306a36Sopenharmony_ci};
112562306a36Sopenharmony_ci
112662306a36Sopenharmony_cistatic int viacam_probe(struct platform_device *pdev)
112762306a36Sopenharmony_ci{
112862306a36Sopenharmony_ci	int ret;
112962306a36Sopenharmony_ci	struct i2c_adapter *sensor_adapter;
113062306a36Sopenharmony_ci	struct viafb_dev *viadev = pdev->dev.platform_data;
113162306a36Sopenharmony_ci	struct vb2_queue *vq;
113262306a36Sopenharmony_ci	struct i2c_board_info ov7670_info = {
113362306a36Sopenharmony_ci		.type = "ov7670",
113462306a36Sopenharmony_ci		.addr = 0x42 >> 1,
113562306a36Sopenharmony_ci		.platform_data = &sensor_cfg,
113662306a36Sopenharmony_ci	};
113762306a36Sopenharmony_ci
113862306a36Sopenharmony_ci	/*
113962306a36Sopenharmony_ci	 * Note that there are actually two capture channels on
114062306a36Sopenharmony_ci	 * the device.	We only deal with one for now.	That
114162306a36Sopenharmony_ci	 * is encoded here; nothing else assumes it's dealing with
114262306a36Sopenharmony_ci	 * a unique capture device.
114362306a36Sopenharmony_ci	 */
114462306a36Sopenharmony_ci	struct via_camera *cam;
114562306a36Sopenharmony_ci
114662306a36Sopenharmony_ci	/*
114762306a36Sopenharmony_ci	 * Ensure that frame buffer memory has been set aside for
114862306a36Sopenharmony_ci	 * this purpose.  As an arbitrary limit, refuse to work
114962306a36Sopenharmony_ci	 * with less than two frames of VGA 16-bit data.
115062306a36Sopenharmony_ci	 *
115162306a36Sopenharmony_ci	 * If we ever support the second port, we'll need to set
115262306a36Sopenharmony_ci	 * aside more memory.
115362306a36Sopenharmony_ci	 */
115462306a36Sopenharmony_ci	if (viadev->camera_fbmem_size < (VGA_HEIGHT*VGA_WIDTH*4)) {
115562306a36Sopenharmony_ci		printk(KERN_ERR "viacam: insufficient FB memory reserved\n");
115662306a36Sopenharmony_ci		return -ENOMEM;
115762306a36Sopenharmony_ci	}
115862306a36Sopenharmony_ci	if (viadev->engine_mmio == NULL) {
115962306a36Sopenharmony_ci		printk(KERN_ERR "viacam: No I/O memory, so no pictures\n");
116062306a36Sopenharmony_ci		return -ENOMEM;
116162306a36Sopenharmony_ci	}
116262306a36Sopenharmony_ci
116362306a36Sopenharmony_ci	if (machine_is_olpc() && viacam_serial_is_enabled())
116462306a36Sopenharmony_ci		return -EBUSY;
116562306a36Sopenharmony_ci
116662306a36Sopenharmony_ci	/*
116762306a36Sopenharmony_ci	 * Basic structure initialization.
116862306a36Sopenharmony_ci	 */
116962306a36Sopenharmony_ci	cam = kzalloc (sizeof(struct via_camera), GFP_KERNEL);
117062306a36Sopenharmony_ci	if (cam == NULL)
117162306a36Sopenharmony_ci		return -ENOMEM;
117262306a36Sopenharmony_ci	via_cam_info = cam;
117362306a36Sopenharmony_ci	cam->platdev = pdev;
117462306a36Sopenharmony_ci	cam->viadev = viadev;
117562306a36Sopenharmony_ci	cam->opstate = S_IDLE;
117662306a36Sopenharmony_ci	cam->user_format = cam->sensor_format = viacam_def_pix_format;
117762306a36Sopenharmony_ci	mutex_init(&cam->lock);
117862306a36Sopenharmony_ci	INIT_LIST_HEAD(&cam->buffer_queue);
117962306a36Sopenharmony_ci	cam->mmio = viadev->engine_mmio;
118062306a36Sopenharmony_ci	cam->fbmem = viadev->fbmem;
118162306a36Sopenharmony_ci	cam->fb_offset = viadev->camera_fbmem_offset;
118262306a36Sopenharmony_ci	cam->flags = 1 << CF_CONFIG_NEEDED;
118362306a36Sopenharmony_ci	cam->mbus_code = via_def_mbus_code;
118462306a36Sopenharmony_ci	/*
118562306a36Sopenharmony_ci	 * Tell V4L that we exist.
118662306a36Sopenharmony_ci	 */
118762306a36Sopenharmony_ci	ret = v4l2_device_register(&pdev->dev, &cam->v4l2_dev);
118862306a36Sopenharmony_ci	if (ret) {
118962306a36Sopenharmony_ci		dev_err(&pdev->dev, "Unable to register v4l2 device\n");
119062306a36Sopenharmony_ci		goto out_free;
119162306a36Sopenharmony_ci	}
119262306a36Sopenharmony_ci	ret = v4l2_ctrl_handler_init(&cam->ctrl_handler, 10);
119362306a36Sopenharmony_ci	if (ret)
119462306a36Sopenharmony_ci		goto out_unregister;
119562306a36Sopenharmony_ci	cam->v4l2_dev.ctrl_handler = &cam->ctrl_handler;
119662306a36Sopenharmony_ci	/*
119762306a36Sopenharmony_ci	 * Convince the system that we can do DMA.
119862306a36Sopenharmony_ci	 */
119962306a36Sopenharmony_ci	pdev->dev.dma_mask = &viadev->pdev->dma_mask;
120062306a36Sopenharmony_ci	ret = dma_set_mask(&pdev->dev, 0xffffffff);
120162306a36Sopenharmony_ci	if (ret)
120262306a36Sopenharmony_ci		goto out_ctrl_hdl_free;
120362306a36Sopenharmony_ci	/*
120462306a36Sopenharmony_ci	 * Fire up the capture port.  The write to 0x78 looks purely
120562306a36Sopenharmony_ci	 * OLPCish; any system will need to tweak 0x1e.
120662306a36Sopenharmony_ci	 */
120762306a36Sopenharmony_ci	via_write_reg_mask(VIASR, 0x78, 0, 0x80);
120862306a36Sopenharmony_ci	via_write_reg_mask(VIASR, 0x1e, 0xc0, 0xc0);
120962306a36Sopenharmony_ci	/*
121062306a36Sopenharmony_ci	 * Get the sensor powered up.
121162306a36Sopenharmony_ci	 */
121262306a36Sopenharmony_ci	ret = via_sensor_power_setup(cam);
121362306a36Sopenharmony_ci	if (ret)
121462306a36Sopenharmony_ci		goto out_ctrl_hdl_free;
121562306a36Sopenharmony_ci	via_sensor_power_up(cam);
121662306a36Sopenharmony_ci
121762306a36Sopenharmony_ci	/*
121862306a36Sopenharmony_ci	 * See if we can't find it on the bus.	The VIA_PORT_31 assumption
121962306a36Sopenharmony_ci	 * is OLPC-specific.  0x42 assumption is ov7670-specific.
122062306a36Sopenharmony_ci	 */
122162306a36Sopenharmony_ci	sensor_adapter = viafb_find_i2c_adapter(VIA_PORT_31);
122262306a36Sopenharmony_ci	cam->sensor = v4l2_i2c_new_subdev_board(&cam->v4l2_dev, sensor_adapter,
122362306a36Sopenharmony_ci			&ov7670_info, NULL);
122462306a36Sopenharmony_ci	if (cam->sensor == NULL) {
122562306a36Sopenharmony_ci		dev_err(&pdev->dev, "Unable to find the sensor!\n");
122662306a36Sopenharmony_ci		ret = -ENODEV;
122762306a36Sopenharmony_ci		goto out_power_down;
122862306a36Sopenharmony_ci	}
122962306a36Sopenharmony_ci	/*
123062306a36Sopenharmony_ci	 * Get the IRQ.
123162306a36Sopenharmony_ci	 */
123262306a36Sopenharmony_ci	viacam_int_disable(cam);
123362306a36Sopenharmony_ci	ret = request_threaded_irq(viadev->pdev->irq, viacam_quick_irq,
123462306a36Sopenharmony_ci			viacam_irq, IRQF_SHARED, "via-camera", cam);
123562306a36Sopenharmony_ci	if (ret)
123662306a36Sopenharmony_ci		goto out_power_down;
123762306a36Sopenharmony_ci
123862306a36Sopenharmony_ci	vq = &cam->vq;
123962306a36Sopenharmony_ci	vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
124062306a36Sopenharmony_ci	vq->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF | VB2_READ;
124162306a36Sopenharmony_ci	vq->drv_priv = cam;
124262306a36Sopenharmony_ci	vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
124362306a36Sopenharmony_ci	vq->buf_struct_size = sizeof(struct via_buffer);
124462306a36Sopenharmony_ci	vq->dev = cam->v4l2_dev.dev;
124562306a36Sopenharmony_ci
124662306a36Sopenharmony_ci	vq->ops = &viacam_vb2_ops;
124762306a36Sopenharmony_ci	vq->mem_ops = &vb2_dma_sg_memops;
124862306a36Sopenharmony_ci	vq->lock = &cam->lock;
124962306a36Sopenharmony_ci
125062306a36Sopenharmony_ci	ret = vb2_queue_init(vq);
125162306a36Sopenharmony_ci	/*
125262306a36Sopenharmony_ci	 * Tell V4l2 that we exist.
125362306a36Sopenharmony_ci	 */
125462306a36Sopenharmony_ci	cam->vdev = viacam_v4l_template;
125562306a36Sopenharmony_ci	cam->vdev.v4l2_dev = &cam->v4l2_dev;
125662306a36Sopenharmony_ci	cam->vdev.lock = &cam->lock;
125762306a36Sopenharmony_ci	cam->vdev.queue = vq;
125862306a36Sopenharmony_ci	video_set_drvdata(&cam->vdev, cam);
125962306a36Sopenharmony_ci	ret = video_register_device(&cam->vdev, VFL_TYPE_VIDEO, -1);
126062306a36Sopenharmony_ci	if (ret)
126162306a36Sopenharmony_ci		goto out_irq;
126262306a36Sopenharmony_ci
126362306a36Sopenharmony_ci#ifdef CONFIG_PM
126462306a36Sopenharmony_ci	/*
126562306a36Sopenharmony_ci	 * Hook into PM events
126662306a36Sopenharmony_ci	 */
126762306a36Sopenharmony_ci	viacam_pm_hooks.private = cam;
126862306a36Sopenharmony_ci	viafb_pm_register(&viacam_pm_hooks);
126962306a36Sopenharmony_ci#endif
127062306a36Sopenharmony_ci
127162306a36Sopenharmony_ci	/* Power the sensor down until somebody opens the device */
127262306a36Sopenharmony_ci	via_sensor_power_down(cam);
127362306a36Sopenharmony_ci	return 0;
127462306a36Sopenharmony_ci
127562306a36Sopenharmony_ciout_irq:
127662306a36Sopenharmony_ci	free_irq(viadev->pdev->irq, cam);
127762306a36Sopenharmony_ciout_power_down:
127862306a36Sopenharmony_ci	via_sensor_power_release(cam);
127962306a36Sopenharmony_ciout_ctrl_hdl_free:
128062306a36Sopenharmony_ci	v4l2_ctrl_handler_free(&cam->ctrl_handler);
128162306a36Sopenharmony_ciout_unregister:
128262306a36Sopenharmony_ci	v4l2_device_unregister(&cam->v4l2_dev);
128362306a36Sopenharmony_ciout_free:
128462306a36Sopenharmony_ci	kfree(cam);
128562306a36Sopenharmony_ci	return ret;
128662306a36Sopenharmony_ci}
128762306a36Sopenharmony_ci
128862306a36Sopenharmony_cistatic void viacam_remove(struct platform_device *pdev)
128962306a36Sopenharmony_ci{
129062306a36Sopenharmony_ci	struct via_camera *cam = via_cam_info;
129162306a36Sopenharmony_ci	struct viafb_dev *viadev = pdev->dev.platform_data;
129262306a36Sopenharmony_ci
129362306a36Sopenharmony_ci	video_unregister_device(&cam->vdev);
129462306a36Sopenharmony_ci	v4l2_device_unregister(&cam->v4l2_dev);
129562306a36Sopenharmony_ci#ifdef CONFIG_PM
129662306a36Sopenharmony_ci	viafb_pm_unregister(&viacam_pm_hooks);
129762306a36Sopenharmony_ci#endif
129862306a36Sopenharmony_ci	free_irq(viadev->pdev->irq, cam);
129962306a36Sopenharmony_ci	via_sensor_power_release(cam);
130062306a36Sopenharmony_ci	v4l2_ctrl_handler_free(&cam->ctrl_handler);
130162306a36Sopenharmony_ci	kfree(cam);
130262306a36Sopenharmony_ci	via_cam_info = NULL;
130362306a36Sopenharmony_ci}
130462306a36Sopenharmony_ci
130562306a36Sopenharmony_cistatic struct platform_driver viacam_driver = {
130662306a36Sopenharmony_ci	.driver = {
130762306a36Sopenharmony_ci		.name = "viafb-camera",
130862306a36Sopenharmony_ci	},
130962306a36Sopenharmony_ci	.probe = viacam_probe,
131062306a36Sopenharmony_ci	.remove_new = viacam_remove,
131162306a36Sopenharmony_ci};
131262306a36Sopenharmony_ci
131362306a36Sopenharmony_cimodule_platform_driver(viacam_driver);
1314