162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
262306a36Sopenharmony_ci//
362306a36Sopenharmony_ci// em28xx-camera.c - driver for Empia EM25xx/27xx/28xx USB video capture devices
462306a36Sopenharmony_ci//
562306a36Sopenharmony_ci// Copyright (C) 2009 Mauro Carvalho Chehab <mchehab@kernel.org>
662306a36Sopenharmony_ci// Copyright (C) 2013 Frank Schäfer <fschaefer.oss@googlemail.com>
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include "em28xx.h"
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <linux/i2c.h>
1162306a36Sopenharmony_ci#include <linux/usb.h>
1262306a36Sopenharmony_ci#include <media/i2c/mt9v011.h>
1362306a36Sopenharmony_ci#include <media/v4l2-common.h>
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci/* Possible i2c addresses of Micron sensors */
1662306a36Sopenharmony_cistatic unsigned short micron_sensor_addrs[] = {
1762306a36Sopenharmony_ci	0xb8 >> 1,   /* MT9V111, MT9V403 */
1862306a36Sopenharmony_ci	0xba >> 1,   /* MT9M001/011/111/112, MT9V011/012/112, MT9D011 */
1962306a36Sopenharmony_ci	0x90 >> 1,   /* MT9V012/112, MT9D011 (alternative address) */
2062306a36Sopenharmony_ci	I2C_CLIENT_END
2162306a36Sopenharmony_ci};
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci/* Possible i2c addresses of Omnivision sensors */
2462306a36Sopenharmony_cistatic unsigned short omnivision_sensor_addrs[] = {
2562306a36Sopenharmony_ci	0x42 >> 1,   /* OV7725, OV7670/60/48 */
2662306a36Sopenharmony_ci	0x60 >> 1,   /* OV2640, OV9650/53/55 */
2762306a36Sopenharmony_ci	I2C_CLIENT_END
2862306a36Sopenharmony_ci};
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci/* FIXME: Should be replaced by a proper mt9m111 driver */
3162306a36Sopenharmony_cistatic int em28xx_initialize_mt9m111(struct em28xx *dev)
3262306a36Sopenharmony_ci{
3362306a36Sopenharmony_ci	int i;
3462306a36Sopenharmony_ci	unsigned char regs[][3] = {
3562306a36Sopenharmony_ci		{ 0x0d, 0x00, 0x01, },  /* reset and use defaults */
3662306a36Sopenharmony_ci		{ 0x0d, 0x00, 0x00, },
3762306a36Sopenharmony_ci		{ 0x0a, 0x00, 0x21, },
3862306a36Sopenharmony_ci		{ 0x21, 0x04, 0x00, },  /* full readout spd, no row/col skip */
3962306a36Sopenharmony_ci	};
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(regs); i++)
4262306a36Sopenharmony_ci		i2c_master_send(&dev->i2c_client[dev->def_i2c_bus],
4362306a36Sopenharmony_ci				&regs[i][0], 3);
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci	/* FIXME: This won't be creating a sensor at the media graph */
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci	return 0;
4862306a36Sopenharmony_ci}
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci/* FIXME: Should be replaced by a proper mt9m001 driver */
5162306a36Sopenharmony_cistatic int em28xx_initialize_mt9m001(struct em28xx *dev)
5262306a36Sopenharmony_ci{
5362306a36Sopenharmony_ci	int i;
5462306a36Sopenharmony_ci	unsigned char regs[][3] = {
5562306a36Sopenharmony_ci		{ 0x0d, 0x00, 0x01, },
5662306a36Sopenharmony_ci		{ 0x0d, 0x00, 0x00, },
5762306a36Sopenharmony_ci		{ 0x04, 0x05, 0x00, },	/* hres = 1280 */
5862306a36Sopenharmony_ci		{ 0x03, 0x04, 0x00, },  /* vres = 1024 */
5962306a36Sopenharmony_ci		{ 0x20, 0x11, 0x00, },
6062306a36Sopenharmony_ci		{ 0x06, 0x00, 0x10, },
6162306a36Sopenharmony_ci		{ 0x2b, 0x00, 0x24, },
6262306a36Sopenharmony_ci		{ 0x2e, 0x00, 0x24, },
6362306a36Sopenharmony_ci		{ 0x35, 0x00, 0x24, },
6462306a36Sopenharmony_ci		{ 0x2d, 0x00, 0x20, },
6562306a36Sopenharmony_ci		{ 0x2c, 0x00, 0x20, },
6662306a36Sopenharmony_ci		{ 0x09, 0x0a, 0xd4, },
6762306a36Sopenharmony_ci		{ 0x35, 0x00, 0x57, },
6862306a36Sopenharmony_ci	};
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(regs); i++)
7162306a36Sopenharmony_ci		i2c_master_send(&dev->i2c_client[dev->def_i2c_bus],
7262306a36Sopenharmony_ci				&regs[i][0], 3);
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci	/* FIXME: This won't be creating a sensor at the media graph */
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci	return 0;
7762306a36Sopenharmony_ci}
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci/*
8062306a36Sopenharmony_ci * Probes Micron sensors with 8 bit address and 16 bit register width
8162306a36Sopenharmony_ci */
8262306a36Sopenharmony_cistatic int em28xx_probe_sensor_micron(struct em28xx *dev)
8362306a36Sopenharmony_ci{
8462306a36Sopenharmony_ci	int ret, i;
8562306a36Sopenharmony_ci	char *name;
8662306a36Sopenharmony_ci	u16 id;
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci	struct i2c_client *client = &dev->i2c_client[dev->def_i2c_bus];
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci	dev->em28xx_sensor = EM28XX_NOSENSOR;
9162306a36Sopenharmony_ci	for (i = 0; micron_sensor_addrs[i] != I2C_CLIENT_END; i++) {
9262306a36Sopenharmony_ci		client->addr = micron_sensor_addrs[i];
9362306a36Sopenharmony_ci		/* Read chip ID from register 0x00 */
9462306a36Sopenharmony_ci		ret = i2c_smbus_read_word_data(client, 0x00); /* assumes LE */
9562306a36Sopenharmony_ci		if (ret < 0) {
9662306a36Sopenharmony_ci			if (ret != -ENXIO)
9762306a36Sopenharmony_ci				dev_err(&dev->intf->dev,
9862306a36Sopenharmony_ci					"couldn't read from i2c device 0x%02x: error %i\n",
9962306a36Sopenharmony_ci				       client->addr << 1, ret);
10062306a36Sopenharmony_ci			continue;
10162306a36Sopenharmony_ci		}
10262306a36Sopenharmony_ci		id = swab16(ret); /* LE -> BE */
10362306a36Sopenharmony_ci		/* Read chip ID from register 0xff */
10462306a36Sopenharmony_ci		ret = i2c_smbus_read_word_data(client, 0xff);
10562306a36Sopenharmony_ci		if (ret < 0) {
10662306a36Sopenharmony_ci			dev_err(&dev->intf->dev,
10762306a36Sopenharmony_ci				"couldn't read from i2c device 0x%02x: error %i\n",
10862306a36Sopenharmony_ci				client->addr << 1, ret);
10962306a36Sopenharmony_ci			continue;
11062306a36Sopenharmony_ci		}
11162306a36Sopenharmony_ci		/* Validate chip ID to be sure we have a Micron device */
11262306a36Sopenharmony_ci		if (id != swab16(ret))
11362306a36Sopenharmony_ci			continue;
11462306a36Sopenharmony_ci		/* Check chip ID */
11562306a36Sopenharmony_ci		switch (id) {
11662306a36Sopenharmony_ci		case 0x1222:
11762306a36Sopenharmony_ci			name = "MT9V012"; /* MI370 */ /* 640x480 */
11862306a36Sopenharmony_ci			break;
11962306a36Sopenharmony_ci		case 0x1229:
12062306a36Sopenharmony_ci			name = "MT9V112"; /* 640x480 */
12162306a36Sopenharmony_ci			break;
12262306a36Sopenharmony_ci		case 0x1433:
12362306a36Sopenharmony_ci			name = "MT9M011"; /* 1280x1024 */
12462306a36Sopenharmony_ci			break;
12562306a36Sopenharmony_ci		case 0x143a:    /* found in the ECS G200 */
12662306a36Sopenharmony_ci			name = "MT9M111"; /* MI1310 */ /* 1280x1024 */
12762306a36Sopenharmony_ci			dev->em28xx_sensor = EM28XX_MT9M111;
12862306a36Sopenharmony_ci			break;
12962306a36Sopenharmony_ci		case 0x148c:
13062306a36Sopenharmony_ci			name = "MT9M112"; /* MI1320 */ /* 1280x1024 */
13162306a36Sopenharmony_ci			break;
13262306a36Sopenharmony_ci		case 0x1511:
13362306a36Sopenharmony_ci			name = "MT9D011"; /* MI2010 */ /* 1600x1200 */
13462306a36Sopenharmony_ci			break;
13562306a36Sopenharmony_ci		case 0x8232:
13662306a36Sopenharmony_ci		case 0x8243:	/* rev B */
13762306a36Sopenharmony_ci			name = "MT9V011"; /* MI360 */ /* 640x480 */
13862306a36Sopenharmony_ci			dev->em28xx_sensor = EM28XX_MT9V011;
13962306a36Sopenharmony_ci			break;
14062306a36Sopenharmony_ci		case 0x8431:
14162306a36Sopenharmony_ci			name = "MT9M001"; /* 1280x1024 */
14262306a36Sopenharmony_ci			dev->em28xx_sensor = EM28XX_MT9M001;
14362306a36Sopenharmony_ci			break;
14462306a36Sopenharmony_ci		default:
14562306a36Sopenharmony_ci			dev_info(&dev->intf->dev,
14662306a36Sopenharmony_ci				 "unknown Micron sensor detected: 0x%04x\n",
14762306a36Sopenharmony_ci				 id);
14862306a36Sopenharmony_ci			return 0;
14962306a36Sopenharmony_ci		}
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci		if (dev->em28xx_sensor == EM28XX_NOSENSOR)
15262306a36Sopenharmony_ci			dev_info(&dev->intf->dev,
15362306a36Sopenharmony_ci				 "unsupported sensor detected: %s\n", name);
15462306a36Sopenharmony_ci		else
15562306a36Sopenharmony_ci			dev_info(&dev->intf->dev,
15662306a36Sopenharmony_ci				 "sensor %s detected\n", name);
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci		return 0;
15962306a36Sopenharmony_ci	}
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci	return -ENODEV;
16262306a36Sopenharmony_ci}
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci/*
16562306a36Sopenharmony_ci * Probes Omnivision sensors with 8 bit address and register width
16662306a36Sopenharmony_ci */
16762306a36Sopenharmony_cistatic int em28xx_probe_sensor_omnivision(struct em28xx *dev)
16862306a36Sopenharmony_ci{
16962306a36Sopenharmony_ci	int ret, i;
17062306a36Sopenharmony_ci	char *name;
17162306a36Sopenharmony_ci	u8 reg;
17262306a36Sopenharmony_ci	u16 id;
17362306a36Sopenharmony_ci	struct i2c_client *client = &dev->i2c_client[dev->def_i2c_bus];
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci	dev->em28xx_sensor = EM28XX_NOSENSOR;
17662306a36Sopenharmony_ci	/*
17762306a36Sopenharmony_ci	 * NOTE: these devices have the register auto incrementation disabled
17862306a36Sopenharmony_ci	 * by default, so we have to use single byte reads !
17962306a36Sopenharmony_ci	 */
18062306a36Sopenharmony_ci	for (i = 0; omnivision_sensor_addrs[i] != I2C_CLIENT_END; i++) {
18162306a36Sopenharmony_ci		client->addr = omnivision_sensor_addrs[i];
18262306a36Sopenharmony_ci		/* Read manufacturer ID from registers 0x1c-0x1d (BE) */
18362306a36Sopenharmony_ci		reg = 0x1c;
18462306a36Sopenharmony_ci		ret = i2c_smbus_read_byte_data(client, reg);
18562306a36Sopenharmony_ci		if (ret < 0) {
18662306a36Sopenharmony_ci			if (ret != -ENXIO)
18762306a36Sopenharmony_ci				dev_err(&dev->intf->dev,
18862306a36Sopenharmony_ci					"couldn't read from i2c device 0x%02x: error %i\n",
18962306a36Sopenharmony_ci					client->addr << 1, ret);
19062306a36Sopenharmony_ci			continue;
19162306a36Sopenharmony_ci		}
19262306a36Sopenharmony_ci		id = ret << 8;
19362306a36Sopenharmony_ci		reg = 0x1d;
19462306a36Sopenharmony_ci		ret = i2c_smbus_read_byte_data(client, reg);
19562306a36Sopenharmony_ci		if (ret < 0) {
19662306a36Sopenharmony_ci			dev_err(&dev->intf->dev,
19762306a36Sopenharmony_ci				"couldn't read from i2c device 0x%02x: error %i\n",
19862306a36Sopenharmony_ci				client->addr << 1, ret);
19962306a36Sopenharmony_ci			continue;
20062306a36Sopenharmony_ci		}
20162306a36Sopenharmony_ci		id += ret;
20262306a36Sopenharmony_ci		/* Check manufacturer ID */
20362306a36Sopenharmony_ci		if (id != 0x7fa2)
20462306a36Sopenharmony_ci			continue;
20562306a36Sopenharmony_ci		/* Read product ID from registers 0x0a-0x0b (BE) */
20662306a36Sopenharmony_ci		reg = 0x0a;
20762306a36Sopenharmony_ci		ret = i2c_smbus_read_byte_data(client, reg);
20862306a36Sopenharmony_ci		if (ret < 0) {
20962306a36Sopenharmony_ci			dev_err(&dev->intf->dev,
21062306a36Sopenharmony_ci				"couldn't read from i2c device 0x%02x: error %i\n",
21162306a36Sopenharmony_ci				client->addr << 1, ret);
21262306a36Sopenharmony_ci			continue;
21362306a36Sopenharmony_ci		}
21462306a36Sopenharmony_ci		id = ret << 8;
21562306a36Sopenharmony_ci		reg = 0x0b;
21662306a36Sopenharmony_ci		ret = i2c_smbus_read_byte_data(client, reg);
21762306a36Sopenharmony_ci		if (ret < 0) {
21862306a36Sopenharmony_ci			dev_err(&dev->intf->dev,
21962306a36Sopenharmony_ci				"couldn't read from i2c device 0x%02x: error %i\n",
22062306a36Sopenharmony_ci				client->addr << 1, ret);
22162306a36Sopenharmony_ci			continue;
22262306a36Sopenharmony_ci		}
22362306a36Sopenharmony_ci		id += ret;
22462306a36Sopenharmony_ci		/* Check product ID */
22562306a36Sopenharmony_ci		switch (id) {
22662306a36Sopenharmony_ci		case 0x2642:
22762306a36Sopenharmony_ci			name = "OV2640";
22862306a36Sopenharmony_ci			dev->em28xx_sensor = EM28XX_OV2640;
22962306a36Sopenharmony_ci			break;
23062306a36Sopenharmony_ci		case 0x7648:
23162306a36Sopenharmony_ci			name = "OV7648";
23262306a36Sopenharmony_ci			break;
23362306a36Sopenharmony_ci		case 0x7660:
23462306a36Sopenharmony_ci			name = "OV7660";
23562306a36Sopenharmony_ci			break;
23662306a36Sopenharmony_ci		case 0x7673:
23762306a36Sopenharmony_ci			name = "OV7670";
23862306a36Sopenharmony_ci			break;
23962306a36Sopenharmony_ci		case 0x7720:
24062306a36Sopenharmony_ci			name = "OV7720";
24162306a36Sopenharmony_ci			break;
24262306a36Sopenharmony_ci		case 0x7721:
24362306a36Sopenharmony_ci			name = "OV7725";
24462306a36Sopenharmony_ci			break;
24562306a36Sopenharmony_ci		case 0x9648: /* Rev 2 */
24662306a36Sopenharmony_ci		case 0x9649: /* Rev 3 */
24762306a36Sopenharmony_ci			name = "OV9640";
24862306a36Sopenharmony_ci			break;
24962306a36Sopenharmony_ci		case 0x9650:
25062306a36Sopenharmony_ci		case 0x9652: /* OV9653 */
25162306a36Sopenharmony_ci			name = "OV9650";
25262306a36Sopenharmony_ci			break;
25362306a36Sopenharmony_ci		case 0x9656: /* Rev 4 */
25462306a36Sopenharmony_ci		case 0x9657: /* Rev 5 */
25562306a36Sopenharmony_ci			name = "OV9655";
25662306a36Sopenharmony_ci			break;
25762306a36Sopenharmony_ci		default:
25862306a36Sopenharmony_ci			dev_info(&dev->intf->dev,
25962306a36Sopenharmony_ci				 "unknown OmniVision sensor detected: 0x%04x\n",
26062306a36Sopenharmony_ci				id);
26162306a36Sopenharmony_ci			return 0;
26262306a36Sopenharmony_ci		}
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ci		if (dev->em28xx_sensor == EM28XX_NOSENSOR)
26562306a36Sopenharmony_ci			dev_info(&dev->intf->dev,
26662306a36Sopenharmony_ci				 "unsupported sensor detected: %s\n", name);
26762306a36Sopenharmony_ci		else
26862306a36Sopenharmony_ci			dev_info(&dev->intf->dev,
26962306a36Sopenharmony_ci				 "sensor %s detected\n", name);
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_ci		return 0;
27262306a36Sopenharmony_ci	}
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_ci	return -ENODEV;
27562306a36Sopenharmony_ci}
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_ciint em28xx_detect_sensor(struct em28xx *dev)
27862306a36Sopenharmony_ci{
27962306a36Sopenharmony_ci	int ret;
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci	ret = em28xx_probe_sensor_micron(dev);
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ci	if (dev->em28xx_sensor == EM28XX_NOSENSOR && ret < 0)
28462306a36Sopenharmony_ci		ret = em28xx_probe_sensor_omnivision(dev);
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_ci	/*
28762306a36Sopenharmony_ci	 * NOTE: the Windows driver also probes i2c addresses
28862306a36Sopenharmony_ci	 *       0x22 (Samsung ?) and 0x66 (Kodak ?)
28962306a36Sopenharmony_ci	 */
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_ci	if (dev->em28xx_sensor == EM28XX_NOSENSOR && ret < 0) {
29262306a36Sopenharmony_ci		dev_info(&dev->intf->dev,
29362306a36Sopenharmony_ci			 "No sensor detected\n");
29462306a36Sopenharmony_ci		return -ENODEV;
29562306a36Sopenharmony_ci	}
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_ci	return 0;
29862306a36Sopenharmony_ci}
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ciint em28xx_init_camera(struct em28xx *dev)
30162306a36Sopenharmony_ci{
30262306a36Sopenharmony_ci	struct i2c_client *client = &dev->i2c_client[dev->def_i2c_bus];
30362306a36Sopenharmony_ci	struct i2c_adapter *adap = &dev->i2c_adap[dev->def_i2c_bus];
30462306a36Sopenharmony_ci	struct em28xx_v4l2 *v4l2 = dev->v4l2;
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_ci	switch (dev->em28xx_sensor) {
30762306a36Sopenharmony_ci	case EM28XX_MT9V011:
30862306a36Sopenharmony_ci	{
30962306a36Sopenharmony_ci		struct mt9v011_platform_data pdata;
31062306a36Sopenharmony_ci		struct i2c_board_info mt9v011_info = {
31162306a36Sopenharmony_ci			.type = "mt9v011",
31262306a36Sopenharmony_ci			.addr = client->addr,
31362306a36Sopenharmony_ci			.platform_data = &pdata,
31462306a36Sopenharmony_ci		};
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci		v4l2->sensor_xres = 640;
31762306a36Sopenharmony_ci		v4l2->sensor_yres = 480;
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_ci		/*
32062306a36Sopenharmony_ci		 * FIXME: mt9v011 uses I2S speed as xtal clk - at least with
32162306a36Sopenharmony_ci		 * the Silvercrest cam I have here for testing - for higher
32262306a36Sopenharmony_ci		 * resolutions, a high clock cause horizontal artifacts, so we
32362306a36Sopenharmony_ci		 * need to use a lower xclk frequency.
32462306a36Sopenharmony_ci		 * Yet, it would be possible to adjust xclk depending on the
32562306a36Sopenharmony_ci		 * desired resolution, since this affects directly the
32662306a36Sopenharmony_ci		 * frame rate.
32762306a36Sopenharmony_ci		 */
32862306a36Sopenharmony_ci		dev->board.xclk = EM28XX_XCLK_FREQUENCY_4_3MHZ;
32962306a36Sopenharmony_ci		em28xx_write_reg(dev, EM28XX_R0F_XCLK, dev->board.xclk);
33062306a36Sopenharmony_ci		v4l2->sensor_xtal = 4300000;
33162306a36Sopenharmony_ci		pdata.xtal = v4l2->sensor_xtal;
33262306a36Sopenharmony_ci		if (NULL ==
33362306a36Sopenharmony_ci		    v4l2_i2c_new_subdev_board(&v4l2->v4l2_dev, adap,
33462306a36Sopenharmony_ci					      &mt9v011_info, NULL))
33562306a36Sopenharmony_ci			return -ENODEV;
33662306a36Sopenharmony_ci		v4l2->vinmode = EM28XX_VINMODE_RGB8_GRBG;
33762306a36Sopenharmony_ci		v4l2->vinctl = 0x00;
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_ci		break;
34062306a36Sopenharmony_ci	}
34162306a36Sopenharmony_ci	case EM28XX_MT9M001:
34262306a36Sopenharmony_ci		v4l2->sensor_xres = 1280;
34362306a36Sopenharmony_ci		v4l2->sensor_yres = 1024;
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_ci		em28xx_initialize_mt9m001(dev);
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_ci		v4l2->vinmode = EM28XX_VINMODE_RGB8_BGGR;
34862306a36Sopenharmony_ci		v4l2->vinctl = 0x00;
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_ci		break;
35162306a36Sopenharmony_ci	case EM28XX_MT9M111:
35262306a36Sopenharmony_ci		v4l2->sensor_xres = 640;
35362306a36Sopenharmony_ci		v4l2->sensor_yres = 512;
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_ci		dev->board.xclk = EM28XX_XCLK_FREQUENCY_48MHZ;
35662306a36Sopenharmony_ci		em28xx_write_reg(dev, EM28XX_R0F_XCLK, dev->board.xclk);
35762306a36Sopenharmony_ci		em28xx_initialize_mt9m111(dev);
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci		v4l2->vinmode = EM28XX_VINMODE_YUV422_UYVY;
36062306a36Sopenharmony_ci		v4l2->vinctl = 0x00;
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_ci		break;
36362306a36Sopenharmony_ci	case EM28XX_OV2640:
36462306a36Sopenharmony_ci	{
36562306a36Sopenharmony_ci		struct v4l2_subdev *subdev;
36662306a36Sopenharmony_ci		struct i2c_board_info ov2640_info = {
36762306a36Sopenharmony_ci			.type = "ov2640",
36862306a36Sopenharmony_ci			.flags = I2C_CLIENT_SCCB,
36962306a36Sopenharmony_ci			.addr = client->addr,
37062306a36Sopenharmony_ci		};
37162306a36Sopenharmony_ci		struct v4l2_subdev_format format = {
37262306a36Sopenharmony_ci			.which = V4L2_SUBDEV_FORMAT_ACTIVE,
37362306a36Sopenharmony_ci		};
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_ci		/*
37662306a36Sopenharmony_ci		 * FIXME: sensor supports resolutions up to 1600x1200, but
37762306a36Sopenharmony_ci		 * resolution setting/switching needs to be modified to
37862306a36Sopenharmony_ci		 * - switch sensor output resolution (including further
37962306a36Sopenharmony_ci		 *   configuration changes)
38062306a36Sopenharmony_ci		 * - adjust bridge xclk
38162306a36Sopenharmony_ci		 * - disable 16 bit (12 bit) output formats on high resolutions
38262306a36Sopenharmony_ci		 */
38362306a36Sopenharmony_ci		v4l2->sensor_xres = 640;
38462306a36Sopenharmony_ci		v4l2->sensor_yres = 480;
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci		subdev =
38762306a36Sopenharmony_ci		     v4l2_i2c_new_subdev_board(&v4l2->v4l2_dev, adap,
38862306a36Sopenharmony_ci					       &ov2640_info, NULL);
38962306a36Sopenharmony_ci		if (!subdev)
39062306a36Sopenharmony_ci			return -ENODEV;
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_ci		format.format.code = MEDIA_BUS_FMT_YUYV8_2X8;
39362306a36Sopenharmony_ci		format.format.width = 640;
39462306a36Sopenharmony_ci		format.format.height = 480;
39562306a36Sopenharmony_ci		v4l2_subdev_call(subdev, pad, set_fmt, NULL, &format);
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_ci		/* NOTE: for UXGA=1600x1200 switch to 12MHz */
39862306a36Sopenharmony_ci		dev->board.xclk = EM28XX_XCLK_FREQUENCY_24MHZ;
39962306a36Sopenharmony_ci		em28xx_write_reg(dev, EM28XX_R0F_XCLK, dev->board.xclk);
40062306a36Sopenharmony_ci		v4l2->vinmode = EM28XX_VINMODE_YUV422_YUYV;
40162306a36Sopenharmony_ci		v4l2->vinctl = 0x00;
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_ci		break;
40462306a36Sopenharmony_ci	}
40562306a36Sopenharmony_ci	case EM28XX_NOSENSOR:
40662306a36Sopenharmony_ci	default:
40762306a36Sopenharmony_ci		return -EINVAL;
40862306a36Sopenharmony_ci	}
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_ci	return 0;
41162306a36Sopenharmony_ci}
41262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(em28xx_init_camera);
413