brintos

brintos / linux-shallow public Read only

0
0
Text · 14.7 KiB · 3e22df3 Raw
633 lines · c
1// SPDX-License-Identifier: GPL-2.0+2/*3 * IMI RDACM21 GMSL Camera Driver4 *5 * Copyright (C) 2017-2020 Jacopo Mondi6 * Copyright (C) 2017-2019 Kieran Bingham7 * Copyright (C) 2017-2019 Laurent Pinchart8 * Copyright (C) 2017-2019 Niklas Söderlund9 * Copyright (C) 2016 Renesas Electronics Corporation10 * Copyright (C) 2015 Cogent Embedded, Inc.11 */12 13#include <linux/delay.h>14#include <linux/fwnode.h>15#include <linux/init.h>16#include <linux/i2c.h>17#include <linux/module.h>18#include <linux/slab.h>19#include <linux/videodev2.h>20 21#include <media/v4l2-async.h>22#include <media/v4l2-ctrls.h>23#include <media/v4l2-subdev.h>24#include "max9271.h"25 26#define MAX9271_RESET_CYCLES		1027 28#define OV490_I2C_ADDRESS		0x2429 30#define OV490_PAGE_HIGH_REG		0xfffd31#define OV490_PAGE_LOW_REG		0xfffe32 33/*34 * The SCCB slave handling is undocumented; the registers naming scheme is35 * totally arbitrary.36 */37#define OV490_SCCB_SLAVE_WRITE		0x0038#define OV490_SCCB_SLAVE_READ		0x0139#define OV490_SCCB_SLAVE0_DIR		0x8019500040#define OV490_SCCB_SLAVE0_ADDR_HIGH	0x8019500141#define OV490_SCCB_SLAVE0_ADDR_LOW	0x8019500242 43#define OV490_DVP_CTRL3			0x8028600944 45#define OV490_ODS_CTRL_FRAME_OUTPUT_EN	0x0c46#define OV490_ODS_CTRL			0x8029d00047 48#define OV490_HOST_CMD			0x808000c049#define OV490_HOST_CMD_TRIGGER		0xc150 51#define OV490_ID_VAL			0x049052#define OV490_ID(_p, _v)		((((_p) & 0xff) << 8) | ((_v) & 0xff))53#define OV490_PID			0x8080300a54#define OV490_VER			0x8080300b55#define OV490_PID_TIMEOUT		2056#define OV490_OUTPUT_EN_TIMEOUT		30057 58#define OV490_GPIO0			BIT(0)59#define OV490_SPWDN0			BIT(0)60#define OV490_GPIO_SEL0			0x8080005061#define OV490_GPIO_SEL1			0x8080005162#define OV490_GPIO_DIRECTION0		0x8080005463#define OV490_GPIO_DIRECTION1		0x8080005564#define OV490_GPIO_OUTPUT_VALUE0	0x8080005865#define OV490_GPIO_OUTPUT_VALUE1	0x8080005966 67#define OV490_ISP_HSIZE_LOW		0x8082006068#define OV490_ISP_HSIZE_HIGH		0x8082006169#define OV490_ISP_VSIZE_LOW		0x8082006270#define OV490_ISP_VSIZE_HIGH		0x8082006371 72#define OV10640_PID_TIMEOUT		2073#define OV10640_ID_HIGH			0xa674#define OV10640_CHIP_ID			0x300a75#define OV10640_PIXEL_RATE		5500000076 77struct rdacm21_device {78	struct device			*dev;79	struct max9271_device		serializer;80	struct i2c_client		*isp;81	struct v4l2_subdev		sd;82	struct media_pad		pad;83	struct v4l2_mbus_framefmt	fmt;84	struct v4l2_ctrl_handler	ctrls;85	u32				addrs[2];86	u16				last_page;87};88 89static inline struct rdacm21_device *sd_to_rdacm21(struct v4l2_subdev *sd)90{91	return container_of(sd, struct rdacm21_device, sd);92}93 94static const struct ov490_reg {95	u16 reg;96	u8 val;97} ov490_regs_wizard[] = {98	{0xfffd, 0x80},99	{0xfffe, 0x82},100	{0x0071, 0x11},101	{0x0075, 0x11},102	{0xfffe, 0x29},103	{0x6010, 0x01},104	/*105	 * OV490 EMB line disable in YUV and RAW data,106	 * NOTE: EMB line is still used in ISP and sensor107	 */108	{0xe000, 0x14},109	{0xfffe, 0x28},110	{0x6000, 0x04},111	{0x6004, 0x00},112	/*113	 * PCLK polarity - useless due to silicon bug.114	 * Use 0x808000bb register instead.115	 */116	{0x6008, 0x00},117	{0xfffe, 0x80},118	{0x0091, 0x00},119	/* bit[3]=0 - PCLK polarity workaround. */120	{0x00bb, 0x1d},121	/* Ov490 FSIN: app_fsin_from_fsync */122	{0xfffe, 0x85},123	{0x0008, 0x00},124	{0x0009, 0x01},125	/* FSIN0 source. */126	{0x000A, 0x05},127	{0x000B, 0x00},128	/* FSIN0 delay. */129	{0x0030, 0x02},130	{0x0031, 0x00},131	{0x0032, 0x00},132	{0x0033, 0x00},133	/* FSIN1 delay. */134	{0x0038, 0x02},135	{0x0039, 0x00},136	{0x003A, 0x00},137	{0x003B, 0x00},138	/* FSIN0 length. */139	{0x0070, 0x2C},140	{0x0071, 0x01},141	{0x0072, 0x00},142	{0x0073, 0x00},143	/* FSIN1 length. */144	{0x0074, 0x64},145	{0x0075, 0x00},146	{0x0076, 0x00},147	{0x0077, 0x00},148	{0x0000, 0x14},149	{0x0001, 0x00},150	{0x0002, 0x00},151	{0x0003, 0x00},152	/*153	 * Load fsin0,load fsin1,load other,154	 * It will be cleared automatically.155	 */156	{0x0004, 0x32},157	{0x0005, 0x00},158	{0x0006, 0x00},159	{0x0007, 0x00},160	{0xfffe, 0x80},161	/* Sensor FSIN. */162	{0x0081, 0x00},163	/* ov10640 FSIN enable */164	{0xfffe, 0x19},165	{0x5000, 0x00},166	{0x5001, 0x30},167	{0x5002, 0x8c},168	{0x5003, 0xb2},169	{0xfffe, 0x80},170	{0x00c0, 0xc1},171	/* ov10640 HFLIP=1 by default */172	{0xfffe, 0x19},173	{0x5000, 0x01},174	{0x5001, 0x00},175	{0xfffe, 0x80},176	{0x00c0, 0xdc},177};178 179static int ov490_read(struct rdacm21_device *dev, u16 reg, u8 *val)180{181	u8 buf[2] = { reg >> 8, reg };182	int ret;183 184	ret = i2c_master_send(dev->isp, buf, 2);185	if (ret == 2)186		ret = i2c_master_recv(dev->isp, val, 1);187 188	if (ret < 0) {189		dev_dbg(dev->dev, "%s: register 0x%04x read failed (%d)\n",190			__func__, reg, ret);191		return ret;192	}193 194	return 0;195}196 197static int ov490_write(struct rdacm21_device *dev, u16 reg, u8 val)198{199	u8 buf[3] = { reg >> 8, reg, val };200	int ret;201 202	ret = i2c_master_send(dev->isp, buf, 3);203	if (ret < 0) {204		dev_err(dev->dev, "%s: register 0x%04x write failed (%d)\n",205			__func__, reg, ret);206		return ret;207	}208 209	return 0;210}211 212static int ov490_set_page(struct rdacm21_device *dev, u16 page)213{214	u8 page_high = page >> 8;215	u8 page_low = page;216	int ret;217 218	if (page == dev->last_page)219		return 0;220 221	if (page_high != (dev->last_page >> 8)) {222		ret = ov490_write(dev, OV490_PAGE_HIGH_REG, page_high);223		if (ret)224			return ret;225	}226 227	if (page_low != (u8)dev->last_page) {228		ret = ov490_write(dev, OV490_PAGE_LOW_REG, page_low);229		if (ret)230			return ret;231	}232 233	dev->last_page = page;234	usleep_range(100, 150);235 236	return 0;237}238 239static int ov490_read_reg(struct rdacm21_device *dev, u32 reg, u8 *val)240{241	int ret;242 243	ret = ov490_set_page(dev, reg >> 16);244	if (ret)245		return ret;246 247	ret = ov490_read(dev, (u16)reg, val);248	if (ret)249		return ret;250 251	dev_dbg(dev->dev, "%s: 0x%08x = 0x%02x\n", __func__, reg, *val);252 253	return 0;254}255 256static int ov490_write_reg(struct rdacm21_device *dev, u32 reg, u8 val)257{258	int ret;259 260	ret = ov490_set_page(dev, reg >> 16);261	if (ret)262		return ret;263 264	ret = ov490_write(dev, (u16)reg, val);265	if (ret)266		return ret;267 268	dev_dbg(dev->dev, "%s: 0x%08x = 0x%02x\n", __func__, reg, val);269 270	return 0;271}272 273static int rdacm21_s_stream(struct v4l2_subdev *sd, int enable)274{275	struct rdacm21_device *dev = sd_to_rdacm21(sd);276 277	/*278	 * Enable serial link now that the ISP provides a valid pixel clock279	 * to start serializing video data on the GMSL link.280	 */281	return max9271_set_serial_link(&dev->serializer, enable);282}283 284static int rdacm21_enum_mbus_code(struct v4l2_subdev *sd,285				  struct v4l2_subdev_state *sd_state,286				  struct v4l2_subdev_mbus_code_enum *code)287{288	if (code->pad || code->index > 0)289		return -EINVAL;290 291	code->code = MEDIA_BUS_FMT_YUYV8_1X16;292 293	return 0;294}295 296static int rdacm21_get_fmt(struct v4l2_subdev *sd,297			   struct v4l2_subdev_state *sd_state,298			   struct v4l2_subdev_format *format)299{300	struct v4l2_mbus_framefmt *mf = &format->format;301	struct rdacm21_device *dev = sd_to_rdacm21(sd);302 303	if (format->pad)304		return -EINVAL;305 306	mf->width		= dev->fmt.width;307	mf->height		= dev->fmt.height;308	mf->code		= MEDIA_BUS_FMT_YUYV8_1X16;309	mf->colorspace		= V4L2_COLORSPACE_SRGB;310	mf->field		= V4L2_FIELD_NONE;311	mf->ycbcr_enc		= V4L2_YCBCR_ENC_601;312	mf->quantization	= V4L2_QUANTIZATION_FULL_RANGE;313	mf->xfer_func		= V4L2_XFER_FUNC_NONE;314 315	return 0;316}317 318static const struct v4l2_subdev_video_ops rdacm21_video_ops = {319	.s_stream	= rdacm21_s_stream,320};321 322static const struct v4l2_subdev_pad_ops rdacm21_subdev_pad_ops = {323	.enum_mbus_code = rdacm21_enum_mbus_code,324	.get_fmt	= rdacm21_get_fmt,325	.set_fmt	= rdacm21_get_fmt,326};327 328static const struct v4l2_subdev_ops rdacm21_subdev_ops = {329	.video		= &rdacm21_video_ops,330	.pad		= &rdacm21_subdev_pad_ops,331};332 333static void ov10640_power_up(struct rdacm21_device *dev)334{335	/* Enable GPIO0#0 (reset) and GPIO1#0 (pwdn) as output lines. */336	ov490_write_reg(dev, OV490_GPIO_SEL0, OV490_GPIO0);337	ov490_write_reg(dev, OV490_GPIO_SEL1, OV490_SPWDN0);338	ov490_write_reg(dev, OV490_GPIO_DIRECTION0, OV490_GPIO0);339	ov490_write_reg(dev, OV490_GPIO_DIRECTION1, OV490_SPWDN0);340 341	/* Power up OV10640 and then reset it. */342	ov490_write_reg(dev, OV490_GPIO_OUTPUT_VALUE1, OV490_SPWDN0);343	usleep_range(1500, 3000);344 345	ov490_write_reg(dev, OV490_GPIO_OUTPUT_VALUE0, 0x00);346	usleep_range(1500, 3000);347	ov490_write_reg(dev, OV490_GPIO_OUTPUT_VALUE0, OV490_GPIO0);348	usleep_range(3000, 5000);349}350 351static int ov10640_check_id(struct rdacm21_device *dev)352{353	unsigned int i;354	u8 val = 0;355 356	/* Read OV10640 ID to test communications. */357	for (i = 0; i < OV10640_PID_TIMEOUT; ++i) {358		ov490_write_reg(dev, OV490_SCCB_SLAVE0_DIR,359				OV490_SCCB_SLAVE_READ);360		ov490_write_reg(dev, OV490_SCCB_SLAVE0_ADDR_HIGH,361				OV10640_CHIP_ID >> 8);362		ov490_write_reg(dev, OV490_SCCB_SLAVE0_ADDR_LOW,363				OV10640_CHIP_ID & 0xff);364 365		/*366		 * Trigger SCCB slave transaction and give it some time367		 * to complete.368		 */369		ov490_write_reg(dev, OV490_HOST_CMD, OV490_HOST_CMD_TRIGGER);370		usleep_range(1000, 1500);371 372		ov490_read_reg(dev, OV490_SCCB_SLAVE0_DIR, &val);373		if (val == OV10640_ID_HIGH)374			break;375		usleep_range(1000, 1500);376	}377	if (i == OV10640_PID_TIMEOUT) {378		dev_err(dev->dev, "OV10640 ID mismatch: (0x%02x)\n", val);379		return -ENODEV;380	}381 382	dev_dbg(dev->dev, "OV10640 ID = 0x%2x\n", val);383 384	return 0;385}386 387static int ov490_initialize(struct rdacm21_device *dev)388{389	u8 pid, ver, val;390	unsigned int i;391	int ret;392 393	ov10640_power_up(dev);394 395	/*396	 * Read OV490 Id to test communications. Give it up to 40msec to397	 * exit from reset.398	 */399	for (i = 0; i < OV490_PID_TIMEOUT; ++i) {400		ret = ov490_read_reg(dev, OV490_PID, &pid);401		if (ret == 0)402			break;403		usleep_range(1000, 2000);404	}405	if (i == OV490_PID_TIMEOUT) {406		dev_err(dev->dev, "OV490 PID read failed (%d)\n", ret);407		return ret;408	}409 410	ret = ov490_read_reg(dev, OV490_VER, &ver);411	if (ret < 0)412		return ret;413 414	if (OV490_ID(pid, ver) != OV490_ID_VAL) {415		dev_err(dev->dev, "OV490 ID mismatch (0x%04x)\n",416			OV490_ID(pid, ver));417		return -ENODEV;418	}419 420	/* Wait for firmware boot by reading streamon status. */421	for (i = 0; i < OV490_OUTPUT_EN_TIMEOUT; ++i) {422		ov490_read_reg(dev, OV490_ODS_CTRL, &val);423		if (val == OV490_ODS_CTRL_FRAME_OUTPUT_EN)424			break;425		usleep_range(1000, 2000);426	}427	if (i == OV490_OUTPUT_EN_TIMEOUT) {428		dev_err(dev->dev, "Timeout waiting for firmware boot\n");429		return -ENODEV;430	}431 432	ret = ov10640_check_id(dev);433	if (ret)434		return ret;435 436	/* Program OV490 with register-value table. */437	for (i = 0; i < ARRAY_SIZE(ov490_regs_wizard); ++i) {438		ret = ov490_write(dev, ov490_regs_wizard[i].reg,439				  ov490_regs_wizard[i].val);440		if (ret < 0) {441			dev_err(dev->dev,442				"%s: register %u (0x%04x) write failed (%d)\n",443				__func__, i, ov490_regs_wizard[i].reg, ret);444 445			return -EIO;446		}447 448		usleep_range(100, 150);449	}450 451	/*452	 * The ISP is programmed with the content of a serial flash memory.453	 * Read the firmware configuration to reflect it through the V4L2 APIs.454	 */455	ov490_read_reg(dev, OV490_ISP_HSIZE_HIGH, &val);456	dev->fmt.width = (val & 0xf) << 8;457	ov490_read_reg(dev, OV490_ISP_HSIZE_LOW, &val);458	dev->fmt.width |= (val & 0xff);459 460	ov490_read_reg(dev, OV490_ISP_VSIZE_HIGH, &val);461	dev->fmt.height = (val & 0xf) << 8;462	ov490_read_reg(dev, OV490_ISP_VSIZE_LOW, &val);463	dev->fmt.height |= val & 0xff;464 465	/* Set bus width to 12 bits with [0:11] ordering. */466	ov490_write_reg(dev, OV490_DVP_CTRL3, 0x10);467 468	dev_info(dev->dev, "Identified RDACM21 camera module\n");469 470	return 0;471}472 473static int rdacm21_initialize(struct rdacm21_device *dev)474{475	int ret;476 477	max9271_wake_up(&dev->serializer);478 479	/* Enable reverse channel and disable the serial link. */480	ret = max9271_set_serial_link(&dev->serializer, false);481	if (ret)482		return ret;483 484	/* Configure I2C bus at 105Kbps speed and configure GMSL. */485	ret = max9271_configure_i2c(&dev->serializer,486				    MAX9271_I2CSLVSH_469NS_234NS |487				    MAX9271_I2CSLVTO_1024US |488				    MAX9271_I2CMSTBT_105KBPS);489	if (ret)490		return ret;491 492	ret = max9271_verify_id(&dev->serializer);493	if (ret)494		return ret;495 496	/*497	 * Enable GPIO1 and hold OV490 in reset during max9271 configuration.498	 * The reset signal has to be asserted for at least 250 useconds.499	 */500	ret = max9271_enable_gpios(&dev->serializer, MAX9271_GPIO1OUT);501	if (ret)502		return ret;503 504	ret = max9271_clear_gpios(&dev->serializer, MAX9271_GPIO1OUT);505	if (ret)506		return ret;507	usleep_range(250, 500);508 509	ret = max9271_configure_gmsl_link(&dev->serializer);510	if (ret)511		return ret;512 513	ret = max9271_set_address(&dev->serializer, dev->addrs[0]);514	if (ret)515		return ret;516	dev->serializer.client->addr = dev->addrs[0];517 518	ret = max9271_set_translation(&dev->serializer, dev->addrs[1],519				      OV490_I2C_ADDRESS);520	if (ret)521		return ret;522	dev->isp->addr = dev->addrs[1];523 524	/* Release OV490 from reset and initialize it. */525	ret = max9271_set_gpios(&dev->serializer, MAX9271_GPIO1OUT);526	if (ret)527		return ret;528	usleep_range(3000, 5000);529 530	ret = ov490_initialize(dev);531	if (ret)532		return ret;533 534	/*535	 * Set reverse channel high threshold to increase noise immunity.536	 *537	 * This should be compensated by increasing the reverse channel538	 * amplitude on the remote deserializer side.539	 */540	return max9271_set_high_threshold(&dev->serializer, true);541}542 543static int rdacm21_probe(struct i2c_client *client)544{545	struct rdacm21_device *dev;546	int ret;547 548	dev = devm_kzalloc(&client->dev, sizeof(*dev), GFP_KERNEL);549	if (!dev)550		return -ENOMEM;551	dev->dev = &client->dev;552	dev->serializer.client = client;553 554	ret = of_property_read_u32_array(client->dev.of_node, "reg",555					 dev->addrs, 2);556	if (ret < 0) {557		dev_err(dev->dev, "Invalid DT reg property: %d\n", ret);558		return -EINVAL;559	}560 561	/* Create the dummy I2C client for the sensor. */562	dev->isp = i2c_new_dummy_device(client->adapter, OV490_I2C_ADDRESS);563	if (IS_ERR(dev->isp))564		return PTR_ERR(dev->isp);565 566	ret = rdacm21_initialize(dev);567	if (ret < 0)568		goto error;569 570	/* Initialize and register the subdevice. */571	v4l2_i2c_subdev_init(&dev->sd, client, &rdacm21_subdev_ops);572	dev->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;573 574	v4l2_ctrl_handler_init(&dev->ctrls, 1);575	v4l2_ctrl_new_std(&dev->ctrls, NULL, V4L2_CID_PIXEL_RATE,576			  OV10640_PIXEL_RATE, OV10640_PIXEL_RATE, 1,577			  OV10640_PIXEL_RATE);578	dev->sd.ctrl_handler = &dev->ctrls;579 580	ret = dev->ctrls.error;581	if (ret)582		goto error_free_ctrls;583 584	dev->pad.flags = MEDIA_PAD_FL_SOURCE;585	dev->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR;586	ret = media_entity_pads_init(&dev->sd.entity, 1, &dev->pad);587	if (ret < 0)588		goto error_free_ctrls;589 590	ret = v4l2_async_register_subdev(&dev->sd);591	if (ret)592		goto error_free_ctrls;593 594	return 0;595 596error_free_ctrls:597	v4l2_ctrl_handler_free(&dev->ctrls);598error:599	i2c_unregister_device(dev->isp);600 601	return ret;602}603 604static void rdacm21_remove(struct i2c_client *client)605{606	struct rdacm21_device *dev = sd_to_rdacm21(i2c_get_clientdata(client));607 608	v4l2_async_unregister_subdev(&dev->sd);609	v4l2_ctrl_handler_free(&dev->ctrls);610	i2c_unregister_device(dev->isp);611}612 613static const struct of_device_id rdacm21_of_ids[] = {614	{ .compatible = "imi,rdacm21" },615	{ }616};617MODULE_DEVICE_TABLE(of, rdacm21_of_ids);618 619static struct i2c_driver rdacm21_i2c_driver = {620	.driver	= {621		.name	= "rdacm21",622		.of_match_table = rdacm21_of_ids,623	},624	.probe		= rdacm21_probe,625	.remove		= rdacm21_remove,626};627 628module_i2c_driver(rdacm21_i2c_driver);629 630MODULE_DESCRIPTION("GMSL Camera driver for RDACM21");631MODULE_AUTHOR("Jacopo Mondi");632MODULE_LICENSE("GPL v2");633