18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 28c2ecf20Sopenharmony_ci// 38c2ecf20Sopenharmony_ci// em28xx-camera.c - driver for Empia EM25xx/27xx/28xx USB video capture devices 48c2ecf20Sopenharmony_ci// 58c2ecf20Sopenharmony_ci// Copyright (C) 2009 Mauro Carvalho Chehab <mchehab@kernel.org> 68c2ecf20Sopenharmony_ci// Copyright (C) 2013 Frank Schäfer <fschaefer.oss@googlemail.com> 78c2ecf20Sopenharmony_ci// 88c2ecf20Sopenharmony_ci// This program is free software; you can redistribute it and/or modify 98c2ecf20Sopenharmony_ci// it under the terms of the GNU General Public License as published by 108c2ecf20Sopenharmony_ci// the Free Software Foundation; either version 2 of the License, or 118c2ecf20Sopenharmony_ci// (at your option) any later version. 128c2ecf20Sopenharmony_ci// 138c2ecf20Sopenharmony_ci// This program is distributed in the hope that it will be useful, 148c2ecf20Sopenharmony_ci// but WITHOUT ANY WARRANTY; without even the implied warranty of 158c2ecf20Sopenharmony_ci// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 168c2ecf20Sopenharmony_ci// GNU General Public License for more details. 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#include "em28xx.h" 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#include <linux/i2c.h> 218c2ecf20Sopenharmony_ci#include <linux/usb.h> 228c2ecf20Sopenharmony_ci#include <media/i2c/mt9v011.h> 238c2ecf20Sopenharmony_ci#include <media/v4l2-common.h> 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci/* Possible i2c addresses of Micron sensors */ 268c2ecf20Sopenharmony_cistatic unsigned short micron_sensor_addrs[] = { 278c2ecf20Sopenharmony_ci 0xb8 >> 1, /* MT9V111, MT9V403 */ 288c2ecf20Sopenharmony_ci 0xba >> 1, /* MT9M001/011/111/112, MT9V011/012/112, MT9D011 */ 298c2ecf20Sopenharmony_ci 0x90 >> 1, /* MT9V012/112, MT9D011 (alternative address) */ 308c2ecf20Sopenharmony_ci I2C_CLIENT_END 318c2ecf20Sopenharmony_ci}; 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci/* Possible i2c addresses of Omnivision sensors */ 348c2ecf20Sopenharmony_cistatic unsigned short omnivision_sensor_addrs[] = { 358c2ecf20Sopenharmony_ci 0x42 >> 1, /* OV7725, OV7670/60/48 */ 368c2ecf20Sopenharmony_ci 0x60 >> 1, /* OV2640, OV9650/53/55 */ 378c2ecf20Sopenharmony_ci I2C_CLIENT_END 388c2ecf20Sopenharmony_ci}; 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci/* FIXME: Should be replaced by a proper mt9m111 driver */ 418c2ecf20Sopenharmony_cistatic int em28xx_initialize_mt9m111(struct em28xx *dev) 428c2ecf20Sopenharmony_ci{ 438c2ecf20Sopenharmony_ci int i; 448c2ecf20Sopenharmony_ci unsigned char regs[][3] = { 458c2ecf20Sopenharmony_ci { 0x0d, 0x00, 0x01, }, /* reset and use defaults */ 468c2ecf20Sopenharmony_ci { 0x0d, 0x00, 0x00, }, 478c2ecf20Sopenharmony_ci { 0x0a, 0x00, 0x21, }, 488c2ecf20Sopenharmony_ci { 0x21, 0x04, 0x00, }, /* full readout spd, no row/col skip */ 498c2ecf20Sopenharmony_ci }; 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(regs); i++) 528c2ecf20Sopenharmony_ci i2c_master_send(&dev->i2c_client[dev->def_i2c_bus], 538c2ecf20Sopenharmony_ci ®s[i][0], 3); 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci /* FIXME: This won't be creating a sensor at the media graph */ 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci return 0; 588c2ecf20Sopenharmony_ci} 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci/* FIXME: Should be replaced by a proper mt9m001 driver */ 618c2ecf20Sopenharmony_cistatic int em28xx_initialize_mt9m001(struct em28xx *dev) 628c2ecf20Sopenharmony_ci{ 638c2ecf20Sopenharmony_ci int i; 648c2ecf20Sopenharmony_ci unsigned char regs[][3] = { 658c2ecf20Sopenharmony_ci { 0x0d, 0x00, 0x01, }, 668c2ecf20Sopenharmony_ci { 0x0d, 0x00, 0x00, }, 678c2ecf20Sopenharmony_ci { 0x04, 0x05, 0x00, }, /* hres = 1280 */ 688c2ecf20Sopenharmony_ci { 0x03, 0x04, 0x00, }, /* vres = 1024 */ 698c2ecf20Sopenharmony_ci { 0x20, 0x11, 0x00, }, 708c2ecf20Sopenharmony_ci { 0x06, 0x00, 0x10, }, 718c2ecf20Sopenharmony_ci { 0x2b, 0x00, 0x24, }, 728c2ecf20Sopenharmony_ci { 0x2e, 0x00, 0x24, }, 738c2ecf20Sopenharmony_ci { 0x35, 0x00, 0x24, }, 748c2ecf20Sopenharmony_ci { 0x2d, 0x00, 0x20, }, 758c2ecf20Sopenharmony_ci { 0x2c, 0x00, 0x20, }, 768c2ecf20Sopenharmony_ci { 0x09, 0x0a, 0xd4, }, 778c2ecf20Sopenharmony_ci { 0x35, 0x00, 0x57, }, 788c2ecf20Sopenharmony_ci }; 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(regs); i++) 818c2ecf20Sopenharmony_ci i2c_master_send(&dev->i2c_client[dev->def_i2c_bus], 828c2ecf20Sopenharmony_ci ®s[i][0], 3); 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci /* FIXME: This won't be creating a sensor at the media graph */ 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci return 0; 878c2ecf20Sopenharmony_ci} 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci/* 908c2ecf20Sopenharmony_ci * Probes Micron sensors with 8 bit address and 16 bit register width 918c2ecf20Sopenharmony_ci */ 928c2ecf20Sopenharmony_cistatic int em28xx_probe_sensor_micron(struct em28xx *dev) 938c2ecf20Sopenharmony_ci{ 948c2ecf20Sopenharmony_ci int ret, i; 958c2ecf20Sopenharmony_ci char *name; 968c2ecf20Sopenharmony_ci u16 id; 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci struct i2c_client *client = &dev->i2c_client[dev->def_i2c_bus]; 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci dev->em28xx_sensor = EM28XX_NOSENSOR; 1018c2ecf20Sopenharmony_ci for (i = 0; micron_sensor_addrs[i] != I2C_CLIENT_END; i++) { 1028c2ecf20Sopenharmony_ci client->addr = micron_sensor_addrs[i]; 1038c2ecf20Sopenharmony_ci /* Read chip ID from register 0x00 */ 1048c2ecf20Sopenharmony_ci ret = i2c_smbus_read_word_data(client, 0x00); /* assumes LE */ 1058c2ecf20Sopenharmony_ci if (ret < 0) { 1068c2ecf20Sopenharmony_ci if (ret != -ENXIO) 1078c2ecf20Sopenharmony_ci dev_err(&dev->intf->dev, 1088c2ecf20Sopenharmony_ci "couldn't read from i2c device 0x%02x: error %i\n", 1098c2ecf20Sopenharmony_ci client->addr << 1, ret); 1108c2ecf20Sopenharmony_ci continue; 1118c2ecf20Sopenharmony_ci } 1128c2ecf20Sopenharmony_ci id = swab16(ret); /* LE -> BE */ 1138c2ecf20Sopenharmony_ci /* Read chip ID from register 0xff */ 1148c2ecf20Sopenharmony_ci ret = i2c_smbus_read_word_data(client, 0xff); 1158c2ecf20Sopenharmony_ci if (ret < 0) { 1168c2ecf20Sopenharmony_ci dev_err(&dev->intf->dev, 1178c2ecf20Sopenharmony_ci "couldn't read from i2c device 0x%02x: error %i\n", 1188c2ecf20Sopenharmony_ci client->addr << 1, ret); 1198c2ecf20Sopenharmony_ci continue; 1208c2ecf20Sopenharmony_ci } 1218c2ecf20Sopenharmony_ci /* Validate chip ID to be sure we have a Micron device */ 1228c2ecf20Sopenharmony_ci if (id != swab16(ret)) 1238c2ecf20Sopenharmony_ci continue; 1248c2ecf20Sopenharmony_ci /* Check chip ID */ 1258c2ecf20Sopenharmony_ci switch (id) { 1268c2ecf20Sopenharmony_ci case 0x1222: 1278c2ecf20Sopenharmony_ci name = "MT9V012"; /* MI370 */ /* 640x480 */ 1288c2ecf20Sopenharmony_ci break; 1298c2ecf20Sopenharmony_ci case 0x1229: 1308c2ecf20Sopenharmony_ci name = "MT9V112"; /* 640x480 */ 1318c2ecf20Sopenharmony_ci break; 1328c2ecf20Sopenharmony_ci case 0x1433: 1338c2ecf20Sopenharmony_ci name = "MT9M011"; /* 1280x1024 */ 1348c2ecf20Sopenharmony_ci break; 1358c2ecf20Sopenharmony_ci case 0x143a: /* found in the ECS G200 */ 1368c2ecf20Sopenharmony_ci name = "MT9M111"; /* MI1310 */ /* 1280x1024 */ 1378c2ecf20Sopenharmony_ci dev->em28xx_sensor = EM28XX_MT9M111; 1388c2ecf20Sopenharmony_ci break; 1398c2ecf20Sopenharmony_ci case 0x148c: 1408c2ecf20Sopenharmony_ci name = "MT9M112"; /* MI1320 */ /* 1280x1024 */ 1418c2ecf20Sopenharmony_ci break; 1428c2ecf20Sopenharmony_ci case 0x1511: 1438c2ecf20Sopenharmony_ci name = "MT9D011"; /* MI2010 */ /* 1600x1200 */ 1448c2ecf20Sopenharmony_ci break; 1458c2ecf20Sopenharmony_ci case 0x8232: 1468c2ecf20Sopenharmony_ci case 0x8243: /* rev B */ 1478c2ecf20Sopenharmony_ci name = "MT9V011"; /* MI360 */ /* 640x480 */ 1488c2ecf20Sopenharmony_ci dev->em28xx_sensor = EM28XX_MT9V011; 1498c2ecf20Sopenharmony_ci break; 1508c2ecf20Sopenharmony_ci case 0x8431: 1518c2ecf20Sopenharmony_ci name = "MT9M001"; /* 1280x1024 */ 1528c2ecf20Sopenharmony_ci dev->em28xx_sensor = EM28XX_MT9M001; 1538c2ecf20Sopenharmony_ci break; 1548c2ecf20Sopenharmony_ci default: 1558c2ecf20Sopenharmony_ci dev_info(&dev->intf->dev, 1568c2ecf20Sopenharmony_ci "unknown Micron sensor detected: 0x%04x\n", 1578c2ecf20Sopenharmony_ci id); 1588c2ecf20Sopenharmony_ci return 0; 1598c2ecf20Sopenharmony_ci } 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci if (dev->em28xx_sensor == EM28XX_NOSENSOR) 1628c2ecf20Sopenharmony_ci dev_info(&dev->intf->dev, 1638c2ecf20Sopenharmony_ci "unsupported sensor detected: %s\n", name); 1648c2ecf20Sopenharmony_ci else 1658c2ecf20Sopenharmony_ci dev_info(&dev->intf->dev, 1668c2ecf20Sopenharmony_ci "sensor %s detected\n", name); 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci return 0; 1698c2ecf20Sopenharmony_ci } 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci return -ENODEV; 1728c2ecf20Sopenharmony_ci} 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci/* 1758c2ecf20Sopenharmony_ci * Probes Omnivision sensors with 8 bit address and register width 1768c2ecf20Sopenharmony_ci */ 1778c2ecf20Sopenharmony_cistatic int em28xx_probe_sensor_omnivision(struct em28xx *dev) 1788c2ecf20Sopenharmony_ci{ 1798c2ecf20Sopenharmony_ci int ret, i; 1808c2ecf20Sopenharmony_ci char *name; 1818c2ecf20Sopenharmony_ci u8 reg; 1828c2ecf20Sopenharmony_ci u16 id; 1838c2ecf20Sopenharmony_ci struct i2c_client *client = &dev->i2c_client[dev->def_i2c_bus]; 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci dev->em28xx_sensor = EM28XX_NOSENSOR; 1868c2ecf20Sopenharmony_ci /* 1878c2ecf20Sopenharmony_ci * NOTE: these devices have the register auto incrementation disabled 1888c2ecf20Sopenharmony_ci * by default, so we have to use single byte reads ! 1898c2ecf20Sopenharmony_ci */ 1908c2ecf20Sopenharmony_ci for (i = 0; omnivision_sensor_addrs[i] != I2C_CLIENT_END; i++) { 1918c2ecf20Sopenharmony_ci client->addr = omnivision_sensor_addrs[i]; 1928c2ecf20Sopenharmony_ci /* Read manufacturer ID from registers 0x1c-0x1d (BE) */ 1938c2ecf20Sopenharmony_ci reg = 0x1c; 1948c2ecf20Sopenharmony_ci ret = i2c_smbus_read_byte_data(client, reg); 1958c2ecf20Sopenharmony_ci if (ret < 0) { 1968c2ecf20Sopenharmony_ci if (ret != -ENXIO) 1978c2ecf20Sopenharmony_ci dev_err(&dev->intf->dev, 1988c2ecf20Sopenharmony_ci "couldn't read from i2c device 0x%02x: error %i\n", 1998c2ecf20Sopenharmony_ci client->addr << 1, ret); 2008c2ecf20Sopenharmony_ci continue; 2018c2ecf20Sopenharmony_ci } 2028c2ecf20Sopenharmony_ci id = ret << 8; 2038c2ecf20Sopenharmony_ci reg = 0x1d; 2048c2ecf20Sopenharmony_ci ret = i2c_smbus_read_byte_data(client, reg); 2058c2ecf20Sopenharmony_ci if (ret < 0) { 2068c2ecf20Sopenharmony_ci dev_err(&dev->intf->dev, 2078c2ecf20Sopenharmony_ci "couldn't read from i2c device 0x%02x: error %i\n", 2088c2ecf20Sopenharmony_ci client->addr << 1, ret); 2098c2ecf20Sopenharmony_ci continue; 2108c2ecf20Sopenharmony_ci } 2118c2ecf20Sopenharmony_ci id += ret; 2128c2ecf20Sopenharmony_ci /* Check manufacturer ID */ 2138c2ecf20Sopenharmony_ci if (id != 0x7fa2) 2148c2ecf20Sopenharmony_ci continue; 2158c2ecf20Sopenharmony_ci /* Read product ID from registers 0x0a-0x0b (BE) */ 2168c2ecf20Sopenharmony_ci reg = 0x0a; 2178c2ecf20Sopenharmony_ci ret = i2c_smbus_read_byte_data(client, reg); 2188c2ecf20Sopenharmony_ci if (ret < 0) { 2198c2ecf20Sopenharmony_ci dev_err(&dev->intf->dev, 2208c2ecf20Sopenharmony_ci "couldn't read from i2c device 0x%02x: error %i\n", 2218c2ecf20Sopenharmony_ci client->addr << 1, ret); 2228c2ecf20Sopenharmony_ci continue; 2238c2ecf20Sopenharmony_ci } 2248c2ecf20Sopenharmony_ci id = ret << 8; 2258c2ecf20Sopenharmony_ci reg = 0x0b; 2268c2ecf20Sopenharmony_ci ret = i2c_smbus_read_byte_data(client, reg); 2278c2ecf20Sopenharmony_ci if (ret < 0) { 2288c2ecf20Sopenharmony_ci dev_err(&dev->intf->dev, 2298c2ecf20Sopenharmony_ci "couldn't read from i2c device 0x%02x: error %i\n", 2308c2ecf20Sopenharmony_ci client->addr << 1, ret); 2318c2ecf20Sopenharmony_ci continue; 2328c2ecf20Sopenharmony_ci } 2338c2ecf20Sopenharmony_ci id += ret; 2348c2ecf20Sopenharmony_ci /* Check product ID */ 2358c2ecf20Sopenharmony_ci switch (id) { 2368c2ecf20Sopenharmony_ci case 0x2642: 2378c2ecf20Sopenharmony_ci name = "OV2640"; 2388c2ecf20Sopenharmony_ci dev->em28xx_sensor = EM28XX_OV2640; 2398c2ecf20Sopenharmony_ci break; 2408c2ecf20Sopenharmony_ci case 0x7648: 2418c2ecf20Sopenharmony_ci name = "OV7648"; 2428c2ecf20Sopenharmony_ci break; 2438c2ecf20Sopenharmony_ci case 0x7660: 2448c2ecf20Sopenharmony_ci name = "OV7660"; 2458c2ecf20Sopenharmony_ci break; 2468c2ecf20Sopenharmony_ci case 0x7673: 2478c2ecf20Sopenharmony_ci name = "OV7670"; 2488c2ecf20Sopenharmony_ci break; 2498c2ecf20Sopenharmony_ci case 0x7720: 2508c2ecf20Sopenharmony_ci name = "OV7720"; 2518c2ecf20Sopenharmony_ci break; 2528c2ecf20Sopenharmony_ci case 0x7721: 2538c2ecf20Sopenharmony_ci name = "OV7725"; 2548c2ecf20Sopenharmony_ci break; 2558c2ecf20Sopenharmony_ci case 0x9648: /* Rev 2 */ 2568c2ecf20Sopenharmony_ci case 0x9649: /* Rev 3 */ 2578c2ecf20Sopenharmony_ci name = "OV9640"; 2588c2ecf20Sopenharmony_ci break; 2598c2ecf20Sopenharmony_ci case 0x9650: 2608c2ecf20Sopenharmony_ci case 0x9652: /* OV9653 */ 2618c2ecf20Sopenharmony_ci name = "OV9650"; 2628c2ecf20Sopenharmony_ci break; 2638c2ecf20Sopenharmony_ci case 0x9656: /* Rev 4 */ 2648c2ecf20Sopenharmony_ci case 0x9657: /* Rev 5 */ 2658c2ecf20Sopenharmony_ci name = "OV9655"; 2668c2ecf20Sopenharmony_ci break; 2678c2ecf20Sopenharmony_ci default: 2688c2ecf20Sopenharmony_ci dev_info(&dev->intf->dev, 2698c2ecf20Sopenharmony_ci "unknown OmniVision sensor detected: 0x%04x\n", 2708c2ecf20Sopenharmony_ci id); 2718c2ecf20Sopenharmony_ci return 0; 2728c2ecf20Sopenharmony_ci } 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci if (dev->em28xx_sensor == EM28XX_NOSENSOR) 2758c2ecf20Sopenharmony_ci dev_info(&dev->intf->dev, 2768c2ecf20Sopenharmony_ci "unsupported sensor detected: %s\n", name); 2778c2ecf20Sopenharmony_ci else 2788c2ecf20Sopenharmony_ci dev_info(&dev->intf->dev, 2798c2ecf20Sopenharmony_ci "sensor %s detected\n", name); 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci return 0; 2828c2ecf20Sopenharmony_ci } 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci return -ENODEV; 2858c2ecf20Sopenharmony_ci} 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ciint em28xx_detect_sensor(struct em28xx *dev) 2888c2ecf20Sopenharmony_ci{ 2898c2ecf20Sopenharmony_ci int ret; 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci ret = em28xx_probe_sensor_micron(dev); 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci if (dev->em28xx_sensor == EM28XX_NOSENSOR && ret < 0) 2948c2ecf20Sopenharmony_ci ret = em28xx_probe_sensor_omnivision(dev); 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci /* 2978c2ecf20Sopenharmony_ci * NOTE: the Windows driver also probes i2c addresses 2988c2ecf20Sopenharmony_ci * 0x22 (Samsung ?) and 0x66 (Kodak ?) 2998c2ecf20Sopenharmony_ci */ 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci if (dev->em28xx_sensor == EM28XX_NOSENSOR && ret < 0) { 3028c2ecf20Sopenharmony_ci dev_info(&dev->intf->dev, 3038c2ecf20Sopenharmony_ci "No sensor detected\n"); 3048c2ecf20Sopenharmony_ci return -ENODEV; 3058c2ecf20Sopenharmony_ci } 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci return 0; 3088c2ecf20Sopenharmony_ci} 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ciint em28xx_init_camera(struct em28xx *dev) 3118c2ecf20Sopenharmony_ci{ 3128c2ecf20Sopenharmony_ci struct i2c_client *client = &dev->i2c_client[dev->def_i2c_bus]; 3138c2ecf20Sopenharmony_ci struct i2c_adapter *adap = &dev->i2c_adap[dev->def_i2c_bus]; 3148c2ecf20Sopenharmony_ci struct em28xx_v4l2 *v4l2 = dev->v4l2; 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci switch (dev->em28xx_sensor) { 3178c2ecf20Sopenharmony_ci case EM28XX_MT9V011: 3188c2ecf20Sopenharmony_ci { 3198c2ecf20Sopenharmony_ci struct mt9v011_platform_data pdata; 3208c2ecf20Sopenharmony_ci struct i2c_board_info mt9v011_info = { 3218c2ecf20Sopenharmony_ci .type = "mt9v011", 3228c2ecf20Sopenharmony_ci .addr = client->addr, 3238c2ecf20Sopenharmony_ci .platform_data = &pdata, 3248c2ecf20Sopenharmony_ci }; 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci v4l2->sensor_xres = 640; 3278c2ecf20Sopenharmony_ci v4l2->sensor_yres = 480; 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci /* 3308c2ecf20Sopenharmony_ci * FIXME: mt9v011 uses I2S speed as xtal clk - at least with 3318c2ecf20Sopenharmony_ci * the Silvercrest cam I have here for testing - for higher 3328c2ecf20Sopenharmony_ci * resolutions, a high clock cause horizontal artifacts, so we 3338c2ecf20Sopenharmony_ci * need to use a lower xclk frequency. 3348c2ecf20Sopenharmony_ci * Yet, it would be possible to adjust xclk depending on the 3358c2ecf20Sopenharmony_ci * desired resolution, since this affects directly the 3368c2ecf20Sopenharmony_ci * frame rate. 3378c2ecf20Sopenharmony_ci */ 3388c2ecf20Sopenharmony_ci dev->board.xclk = EM28XX_XCLK_FREQUENCY_4_3MHZ; 3398c2ecf20Sopenharmony_ci em28xx_write_reg(dev, EM28XX_R0F_XCLK, dev->board.xclk); 3408c2ecf20Sopenharmony_ci v4l2->sensor_xtal = 4300000; 3418c2ecf20Sopenharmony_ci pdata.xtal = v4l2->sensor_xtal; 3428c2ecf20Sopenharmony_ci if (NULL == 3438c2ecf20Sopenharmony_ci v4l2_i2c_new_subdev_board(&v4l2->v4l2_dev, adap, 3448c2ecf20Sopenharmony_ci &mt9v011_info, NULL)) 3458c2ecf20Sopenharmony_ci return -ENODEV; 3468c2ecf20Sopenharmony_ci v4l2->vinmode = EM28XX_VINMODE_RGB8_GRBG; 3478c2ecf20Sopenharmony_ci v4l2->vinctl = 0x00; 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci break; 3508c2ecf20Sopenharmony_ci } 3518c2ecf20Sopenharmony_ci case EM28XX_MT9M001: 3528c2ecf20Sopenharmony_ci v4l2->sensor_xres = 1280; 3538c2ecf20Sopenharmony_ci v4l2->sensor_yres = 1024; 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci em28xx_initialize_mt9m001(dev); 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci v4l2->vinmode = EM28XX_VINMODE_RGB8_BGGR; 3588c2ecf20Sopenharmony_ci v4l2->vinctl = 0x00; 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci break; 3618c2ecf20Sopenharmony_ci case EM28XX_MT9M111: 3628c2ecf20Sopenharmony_ci v4l2->sensor_xres = 640; 3638c2ecf20Sopenharmony_ci v4l2->sensor_yres = 512; 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci dev->board.xclk = EM28XX_XCLK_FREQUENCY_48MHZ; 3668c2ecf20Sopenharmony_ci em28xx_write_reg(dev, EM28XX_R0F_XCLK, dev->board.xclk); 3678c2ecf20Sopenharmony_ci em28xx_initialize_mt9m111(dev); 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci v4l2->vinmode = EM28XX_VINMODE_YUV422_UYVY; 3708c2ecf20Sopenharmony_ci v4l2->vinctl = 0x00; 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci break; 3738c2ecf20Sopenharmony_ci case EM28XX_OV2640: 3748c2ecf20Sopenharmony_ci { 3758c2ecf20Sopenharmony_ci struct v4l2_subdev *subdev; 3768c2ecf20Sopenharmony_ci struct i2c_board_info ov2640_info = { 3778c2ecf20Sopenharmony_ci .type = "ov2640", 3788c2ecf20Sopenharmony_ci .flags = I2C_CLIENT_SCCB, 3798c2ecf20Sopenharmony_ci .addr = client->addr, 3808c2ecf20Sopenharmony_ci }; 3818c2ecf20Sopenharmony_ci struct v4l2_subdev_format format = { 3828c2ecf20Sopenharmony_ci .which = V4L2_SUBDEV_FORMAT_ACTIVE, 3838c2ecf20Sopenharmony_ci }; 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci /* 3868c2ecf20Sopenharmony_ci * FIXME: sensor supports resolutions up to 1600x1200, but 3878c2ecf20Sopenharmony_ci * resolution setting/switching needs to be modified to 3888c2ecf20Sopenharmony_ci * - switch sensor output resolution (including further 3898c2ecf20Sopenharmony_ci * configuration changes) 3908c2ecf20Sopenharmony_ci * - adjust bridge xclk 3918c2ecf20Sopenharmony_ci * - disable 16 bit (12 bit) output formats on high resolutions 3928c2ecf20Sopenharmony_ci */ 3938c2ecf20Sopenharmony_ci v4l2->sensor_xres = 640; 3948c2ecf20Sopenharmony_ci v4l2->sensor_yres = 480; 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci subdev = 3978c2ecf20Sopenharmony_ci v4l2_i2c_new_subdev_board(&v4l2->v4l2_dev, adap, 3988c2ecf20Sopenharmony_ci &ov2640_info, NULL); 3998c2ecf20Sopenharmony_ci if (!subdev) 4008c2ecf20Sopenharmony_ci return -ENODEV; 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci format.format.code = MEDIA_BUS_FMT_YUYV8_2X8; 4038c2ecf20Sopenharmony_ci format.format.width = 640; 4048c2ecf20Sopenharmony_ci format.format.height = 480; 4058c2ecf20Sopenharmony_ci v4l2_subdev_call(subdev, pad, set_fmt, NULL, &format); 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci /* NOTE: for UXGA=1600x1200 switch to 12MHz */ 4088c2ecf20Sopenharmony_ci dev->board.xclk = EM28XX_XCLK_FREQUENCY_24MHZ; 4098c2ecf20Sopenharmony_ci em28xx_write_reg(dev, EM28XX_R0F_XCLK, dev->board.xclk); 4108c2ecf20Sopenharmony_ci v4l2->vinmode = EM28XX_VINMODE_YUV422_YUYV; 4118c2ecf20Sopenharmony_ci v4l2->vinctl = 0x00; 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci break; 4148c2ecf20Sopenharmony_ci } 4158c2ecf20Sopenharmony_ci case EM28XX_NOSENSOR: 4168c2ecf20Sopenharmony_ci default: 4178c2ecf20Sopenharmony_ci return -EINVAL; 4188c2ecf20Sopenharmony_ci } 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci return 0; 4218c2ecf20Sopenharmony_ci} 4228c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(em28xx_init_camera); 423