18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2005-2006 Micronas USA Inc. 48c2ecf20Sopenharmony_ci */ 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci#include <linux/module.h> 78c2ecf20Sopenharmony_ci#include <linux/delay.h> 88c2ecf20Sopenharmony_ci#include <linux/sched.h> 98c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 108c2ecf20Sopenharmony_ci#include <linux/unistd.h> 118c2ecf20Sopenharmony_ci#include <linux/time.h> 128c2ecf20Sopenharmony_ci#include <linux/mm.h> 138c2ecf20Sopenharmony_ci#include <linux/vmalloc.h> 148c2ecf20Sopenharmony_ci#include <linux/device.h> 158c2ecf20Sopenharmony_ci#include <linux/i2c.h> 168c2ecf20Sopenharmony_ci#include <linux/firmware.h> 178c2ecf20Sopenharmony_ci#include <linux/mutex.h> 188c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 198c2ecf20Sopenharmony_ci#include <linux/slab.h> 208c2ecf20Sopenharmony_ci#include <linux/videodev2.h> 218c2ecf20Sopenharmony_ci#include <media/tuner.h> 228c2ecf20Sopenharmony_ci#include <media/v4l2-common.h> 238c2ecf20Sopenharmony_ci#include <media/v4l2-event.h> 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#include "go7007-priv.h" 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci/* 288c2ecf20Sopenharmony_ci * Wait for an interrupt to be delivered from the GO7007SB and return 298c2ecf20Sopenharmony_ci * the associated value and data. 308c2ecf20Sopenharmony_ci * 318c2ecf20Sopenharmony_ci * Must be called with the hw_lock held. 328c2ecf20Sopenharmony_ci */ 338c2ecf20Sopenharmony_ciint go7007_read_interrupt(struct go7007 *go, u16 *value, u16 *data) 348c2ecf20Sopenharmony_ci{ 358c2ecf20Sopenharmony_ci go->interrupt_available = 0; 368c2ecf20Sopenharmony_ci go->hpi_ops->read_interrupt(go); 378c2ecf20Sopenharmony_ci if (wait_event_timeout(go->interrupt_waitq, 388c2ecf20Sopenharmony_ci go->interrupt_available, 5*HZ) < 0) { 398c2ecf20Sopenharmony_ci v4l2_err(&go->v4l2_dev, "timeout waiting for read interrupt\n"); 408c2ecf20Sopenharmony_ci return -1; 418c2ecf20Sopenharmony_ci } 428c2ecf20Sopenharmony_ci if (!go->interrupt_available) 438c2ecf20Sopenharmony_ci return -1; 448c2ecf20Sopenharmony_ci go->interrupt_available = 0; 458c2ecf20Sopenharmony_ci *value = go->interrupt_value & 0xfffe; 468c2ecf20Sopenharmony_ci *data = go->interrupt_data; 478c2ecf20Sopenharmony_ci return 0; 488c2ecf20Sopenharmony_ci} 498c2ecf20Sopenharmony_ciEXPORT_SYMBOL(go7007_read_interrupt); 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci/* 528c2ecf20Sopenharmony_ci * Read a register/address on the GO7007SB. 538c2ecf20Sopenharmony_ci * 548c2ecf20Sopenharmony_ci * Must be called with the hw_lock held. 558c2ecf20Sopenharmony_ci */ 568c2ecf20Sopenharmony_ciint go7007_read_addr(struct go7007 *go, u16 addr, u16 *data) 578c2ecf20Sopenharmony_ci{ 588c2ecf20Sopenharmony_ci int count = 100; 598c2ecf20Sopenharmony_ci u16 value; 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci if (go7007_write_interrupt(go, 0x0010, addr) < 0) 628c2ecf20Sopenharmony_ci return -EIO; 638c2ecf20Sopenharmony_ci while (count-- > 0) { 648c2ecf20Sopenharmony_ci if (go7007_read_interrupt(go, &value, data) == 0 && 658c2ecf20Sopenharmony_ci value == 0xa000) 668c2ecf20Sopenharmony_ci return 0; 678c2ecf20Sopenharmony_ci } 688c2ecf20Sopenharmony_ci return -EIO; 698c2ecf20Sopenharmony_ci} 708c2ecf20Sopenharmony_ciEXPORT_SYMBOL(go7007_read_addr); 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci/* 738c2ecf20Sopenharmony_ci * Send the boot firmware to the encoder, which just wakes it up and lets 748c2ecf20Sopenharmony_ci * us talk to the GPIO pins and on-board I2C adapter. 758c2ecf20Sopenharmony_ci * 768c2ecf20Sopenharmony_ci * Must be called with the hw_lock held. 778c2ecf20Sopenharmony_ci */ 788c2ecf20Sopenharmony_cistatic int go7007_load_encoder(struct go7007 *go) 798c2ecf20Sopenharmony_ci{ 808c2ecf20Sopenharmony_ci const struct firmware *fw_entry; 818c2ecf20Sopenharmony_ci char fw_name[] = "go7007/go7007fw.bin"; 828c2ecf20Sopenharmony_ci void *bounce; 838c2ecf20Sopenharmony_ci int fw_len, rv = 0; 848c2ecf20Sopenharmony_ci u16 intr_val, intr_data; 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci if (go->boot_fw == NULL) { 878c2ecf20Sopenharmony_ci if (request_firmware(&fw_entry, fw_name, go->dev)) { 888c2ecf20Sopenharmony_ci v4l2_err(go, "unable to load firmware from file \"%s\"\n", fw_name); 898c2ecf20Sopenharmony_ci return -1; 908c2ecf20Sopenharmony_ci } 918c2ecf20Sopenharmony_ci if (fw_entry->size < 16 || memcmp(fw_entry->data, "WISGO7007FW", 11)) { 928c2ecf20Sopenharmony_ci v4l2_err(go, "file \"%s\" does not appear to be go7007 firmware\n", fw_name); 938c2ecf20Sopenharmony_ci release_firmware(fw_entry); 948c2ecf20Sopenharmony_ci return -1; 958c2ecf20Sopenharmony_ci } 968c2ecf20Sopenharmony_ci fw_len = fw_entry->size - 16; 978c2ecf20Sopenharmony_ci bounce = kmemdup(fw_entry->data + 16, fw_len, GFP_KERNEL); 988c2ecf20Sopenharmony_ci if (bounce == NULL) { 998c2ecf20Sopenharmony_ci v4l2_err(go, "unable to allocate %d bytes for firmware transfer\n", fw_len); 1008c2ecf20Sopenharmony_ci release_firmware(fw_entry); 1018c2ecf20Sopenharmony_ci return -1; 1028c2ecf20Sopenharmony_ci } 1038c2ecf20Sopenharmony_ci release_firmware(fw_entry); 1048c2ecf20Sopenharmony_ci go->boot_fw_len = fw_len; 1058c2ecf20Sopenharmony_ci go->boot_fw = bounce; 1068c2ecf20Sopenharmony_ci } 1078c2ecf20Sopenharmony_ci if (go7007_interface_reset(go) < 0 || 1088c2ecf20Sopenharmony_ci go7007_send_firmware(go, go->boot_fw, go->boot_fw_len) < 0 || 1098c2ecf20Sopenharmony_ci go7007_read_interrupt(go, &intr_val, &intr_data) < 0 || 1108c2ecf20Sopenharmony_ci (intr_val & ~0x1) != 0x5a5a) { 1118c2ecf20Sopenharmony_ci v4l2_err(go, "error transferring firmware\n"); 1128c2ecf20Sopenharmony_ci rv = -1; 1138c2ecf20Sopenharmony_ci } 1148c2ecf20Sopenharmony_ci return rv; 1158c2ecf20Sopenharmony_ci} 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ciMODULE_FIRMWARE("go7007/go7007fw.bin"); 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci/* 1208c2ecf20Sopenharmony_ci * Boot the encoder and register the I2C adapter if requested. Do the 1218c2ecf20Sopenharmony_ci * minimum initialization necessary, since the board-specific code may 1228c2ecf20Sopenharmony_ci * still need to probe the board ID. 1238c2ecf20Sopenharmony_ci * 1248c2ecf20Sopenharmony_ci * Must NOT be called with the hw_lock held. 1258c2ecf20Sopenharmony_ci */ 1268c2ecf20Sopenharmony_ciint go7007_boot_encoder(struct go7007 *go, int init_i2c) 1278c2ecf20Sopenharmony_ci{ 1288c2ecf20Sopenharmony_ci int ret; 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci mutex_lock(&go->hw_lock); 1318c2ecf20Sopenharmony_ci ret = go7007_load_encoder(go); 1328c2ecf20Sopenharmony_ci mutex_unlock(&go->hw_lock); 1338c2ecf20Sopenharmony_ci if (ret < 0) 1348c2ecf20Sopenharmony_ci return -1; 1358c2ecf20Sopenharmony_ci if (!init_i2c) 1368c2ecf20Sopenharmony_ci return 0; 1378c2ecf20Sopenharmony_ci if (go7007_i2c_init(go) < 0) 1388c2ecf20Sopenharmony_ci return -1; 1398c2ecf20Sopenharmony_ci go->i2c_adapter_online = 1; 1408c2ecf20Sopenharmony_ci return 0; 1418c2ecf20Sopenharmony_ci} 1428c2ecf20Sopenharmony_ciEXPORT_SYMBOL(go7007_boot_encoder); 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci/* 1458c2ecf20Sopenharmony_ci * Configure any hardware-related registers in the GO7007, such as GPIO 1468c2ecf20Sopenharmony_ci * pins and bus parameters, which are board-specific. This assumes 1478c2ecf20Sopenharmony_ci * the boot firmware has already been downloaded. 1488c2ecf20Sopenharmony_ci * 1498c2ecf20Sopenharmony_ci * Must be called with the hw_lock held. 1508c2ecf20Sopenharmony_ci */ 1518c2ecf20Sopenharmony_cistatic int go7007_init_encoder(struct go7007 *go) 1528c2ecf20Sopenharmony_ci{ 1538c2ecf20Sopenharmony_ci if (go->board_info->audio_flags & GO7007_AUDIO_I2S_MASTER) { 1548c2ecf20Sopenharmony_ci go7007_write_addr(go, 0x1000, 0x0811); 1558c2ecf20Sopenharmony_ci go7007_write_addr(go, 0x1000, 0x0c11); 1568c2ecf20Sopenharmony_ci } 1578c2ecf20Sopenharmony_ci switch (go->board_id) { 1588c2ecf20Sopenharmony_ci case GO7007_BOARDID_MATRIX_REV: 1598c2ecf20Sopenharmony_ci /* Set GPIO pin 0 to be an output (audio clock control) */ 1608c2ecf20Sopenharmony_ci go7007_write_addr(go, 0x3c82, 0x0001); 1618c2ecf20Sopenharmony_ci go7007_write_addr(go, 0x3c80, 0x00fe); 1628c2ecf20Sopenharmony_ci break; 1638c2ecf20Sopenharmony_ci case GO7007_BOARDID_ADLINK_MPG24: 1648c2ecf20Sopenharmony_ci /* set GPIO5 to be an output, currently low */ 1658c2ecf20Sopenharmony_ci go7007_write_addr(go, 0x3c82, 0x0000); 1668c2ecf20Sopenharmony_ci go7007_write_addr(go, 0x3c80, 0x00df); 1678c2ecf20Sopenharmony_ci break; 1688c2ecf20Sopenharmony_ci case GO7007_BOARDID_ADS_USBAV_709: 1698c2ecf20Sopenharmony_ci /* GPIO pin 0: audio clock control */ 1708c2ecf20Sopenharmony_ci /* pin 2: TW9906 reset */ 1718c2ecf20Sopenharmony_ci /* pin 3: capture LED */ 1728c2ecf20Sopenharmony_ci go7007_write_addr(go, 0x3c82, 0x000d); 1738c2ecf20Sopenharmony_ci go7007_write_addr(go, 0x3c80, 0x00f2); 1748c2ecf20Sopenharmony_ci break; 1758c2ecf20Sopenharmony_ci } 1768c2ecf20Sopenharmony_ci return 0; 1778c2ecf20Sopenharmony_ci} 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci/* 1808c2ecf20Sopenharmony_ci * Send the boot firmware to the GO7007 and configure the registers. This 1818c2ecf20Sopenharmony_ci * is the only way to stop the encoder once it has started streaming video. 1828c2ecf20Sopenharmony_ci * 1838c2ecf20Sopenharmony_ci * Must be called with the hw_lock held. 1848c2ecf20Sopenharmony_ci */ 1858c2ecf20Sopenharmony_ciint go7007_reset_encoder(struct go7007 *go) 1868c2ecf20Sopenharmony_ci{ 1878c2ecf20Sopenharmony_ci if (go7007_load_encoder(go) < 0) 1888c2ecf20Sopenharmony_ci return -1; 1898c2ecf20Sopenharmony_ci return go7007_init_encoder(go); 1908c2ecf20Sopenharmony_ci} 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci/* 1938c2ecf20Sopenharmony_ci * Attempt to instantiate an I2C client by ID, probably loading a module. 1948c2ecf20Sopenharmony_ci */ 1958c2ecf20Sopenharmony_cistatic int init_i2c_module(struct i2c_adapter *adapter, const struct go_i2c *const i2c) 1968c2ecf20Sopenharmony_ci{ 1978c2ecf20Sopenharmony_ci struct go7007 *go = i2c_get_adapdata(adapter); 1988c2ecf20Sopenharmony_ci struct v4l2_device *v4l2_dev = &go->v4l2_dev; 1998c2ecf20Sopenharmony_ci struct v4l2_subdev *sd; 2008c2ecf20Sopenharmony_ci struct i2c_board_info info; 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci memset(&info, 0, sizeof(info)); 2038c2ecf20Sopenharmony_ci strscpy(info.type, i2c->type, sizeof(info.type)); 2048c2ecf20Sopenharmony_ci info.addr = i2c->addr; 2058c2ecf20Sopenharmony_ci info.flags = i2c->flags; 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci sd = v4l2_i2c_new_subdev_board(v4l2_dev, adapter, &info, NULL); 2088c2ecf20Sopenharmony_ci if (sd) { 2098c2ecf20Sopenharmony_ci if (i2c->is_video) 2108c2ecf20Sopenharmony_ci go->sd_video = sd; 2118c2ecf20Sopenharmony_ci if (i2c->is_audio) 2128c2ecf20Sopenharmony_ci go->sd_audio = sd; 2138c2ecf20Sopenharmony_ci return 0; 2148c2ecf20Sopenharmony_ci } 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci pr_info("go7007: probing for module i2c:%s failed\n", i2c->type); 2178c2ecf20Sopenharmony_ci return -EINVAL; 2188c2ecf20Sopenharmony_ci} 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci/* 2218c2ecf20Sopenharmony_ci * Detach and unregister the encoder. The go7007 struct won't be freed 2228c2ecf20Sopenharmony_ci * until v4l2 finishes releasing its resources and all associated fds are 2238c2ecf20Sopenharmony_ci * closed by applications. 2248c2ecf20Sopenharmony_ci */ 2258c2ecf20Sopenharmony_cistatic void go7007_remove(struct v4l2_device *v4l2_dev) 2268c2ecf20Sopenharmony_ci{ 2278c2ecf20Sopenharmony_ci struct go7007 *go = container_of(v4l2_dev, struct go7007, v4l2_dev); 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci v4l2_device_unregister(v4l2_dev); 2308c2ecf20Sopenharmony_ci if (go->hpi_ops->release) 2318c2ecf20Sopenharmony_ci go->hpi_ops->release(go); 2328c2ecf20Sopenharmony_ci if (go->i2c_adapter_online) { 2338c2ecf20Sopenharmony_ci i2c_del_adapter(&go->i2c_adapter); 2348c2ecf20Sopenharmony_ci go->i2c_adapter_online = 0; 2358c2ecf20Sopenharmony_ci } 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci kfree(go->boot_fw); 2388c2ecf20Sopenharmony_ci go7007_v4l2_remove(go); 2398c2ecf20Sopenharmony_ci kfree(go); 2408c2ecf20Sopenharmony_ci} 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci/* 2438c2ecf20Sopenharmony_ci * Finalize the GO7007 hardware setup, register the on-board I2C adapter 2448c2ecf20Sopenharmony_ci * (if used on this board), load the I2C client driver for the sensor 2458c2ecf20Sopenharmony_ci * (SAA7115 or whatever) and other devices, and register the ALSA and V4L2 2468c2ecf20Sopenharmony_ci * interfaces. 2478c2ecf20Sopenharmony_ci * 2488c2ecf20Sopenharmony_ci * Must NOT be called with the hw_lock held. 2498c2ecf20Sopenharmony_ci */ 2508c2ecf20Sopenharmony_ciint go7007_register_encoder(struct go7007 *go, unsigned num_i2c_devs) 2518c2ecf20Sopenharmony_ci{ 2528c2ecf20Sopenharmony_ci int i, ret; 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci dev_info(go->dev, "go7007: registering new %s\n", go->name); 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci go->v4l2_dev.release = go7007_remove; 2578c2ecf20Sopenharmony_ci ret = v4l2_device_register(go->dev, &go->v4l2_dev); 2588c2ecf20Sopenharmony_ci if (ret < 0) 2598c2ecf20Sopenharmony_ci return ret; 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci mutex_lock(&go->hw_lock); 2628c2ecf20Sopenharmony_ci ret = go7007_init_encoder(go); 2638c2ecf20Sopenharmony_ci mutex_unlock(&go->hw_lock); 2648c2ecf20Sopenharmony_ci if (ret < 0) 2658c2ecf20Sopenharmony_ci return ret; 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci ret = go7007_v4l2_ctrl_init(go); 2688c2ecf20Sopenharmony_ci if (ret < 0) 2698c2ecf20Sopenharmony_ci return ret; 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci if (!go->i2c_adapter_online && 2728c2ecf20Sopenharmony_ci go->board_info->flags & GO7007_BOARD_USE_ONBOARD_I2C) { 2738c2ecf20Sopenharmony_ci ret = go7007_i2c_init(go); 2748c2ecf20Sopenharmony_ci if (ret < 0) 2758c2ecf20Sopenharmony_ci return ret; 2768c2ecf20Sopenharmony_ci go->i2c_adapter_online = 1; 2778c2ecf20Sopenharmony_ci } 2788c2ecf20Sopenharmony_ci if (go->i2c_adapter_online) { 2798c2ecf20Sopenharmony_ci if (go->board_id == GO7007_BOARDID_ADS_USBAV_709) { 2808c2ecf20Sopenharmony_ci /* Reset the TW9906 */ 2818c2ecf20Sopenharmony_ci go7007_write_addr(go, 0x3c82, 0x0009); 2828c2ecf20Sopenharmony_ci msleep(50); 2838c2ecf20Sopenharmony_ci go7007_write_addr(go, 0x3c82, 0x000d); 2848c2ecf20Sopenharmony_ci } 2858c2ecf20Sopenharmony_ci for (i = 0; i < num_i2c_devs; ++i) 2868c2ecf20Sopenharmony_ci init_i2c_module(&go->i2c_adapter, &go->board_info->i2c_devs[i]); 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci if (go->tuner_type >= 0) { 2898c2ecf20Sopenharmony_ci struct tuner_setup setup = { 2908c2ecf20Sopenharmony_ci .addr = ADDR_UNSET, 2918c2ecf20Sopenharmony_ci .type = go->tuner_type, 2928c2ecf20Sopenharmony_ci .mode_mask = T_ANALOG_TV, 2938c2ecf20Sopenharmony_ci }; 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci v4l2_device_call_all(&go->v4l2_dev, 0, tuner, 2968c2ecf20Sopenharmony_ci s_type_addr, &setup); 2978c2ecf20Sopenharmony_ci } 2988c2ecf20Sopenharmony_ci if (go->board_id == GO7007_BOARDID_ADLINK_MPG24) 2998c2ecf20Sopenharmony_ci v4l2_subdev_call(go->sd_video, video, s_routing, 3008c2ecf20Sopenharmony_ci 0, 0, go->channel_number + 1); 3018c2ecf20Sopenharmony_ci } 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci ret = go7007_v4l2_init(go); 3048c2ecf20Sopenharmony_ci if (ret < 0) 3058c2ecf20Sopenharmony_ci return ret; 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci if (go->board_info->flags & GO7007_BOARD_HAS_AUDIO) { 3088c2ecf20Sopenharmony_ci go->audio_enabled = 1; 3098c2ecf20Sopenharmony_ci go7007_snd_init(go); 3108c2ecf20Sopenharmony_ci } 3118c2ecf20Sopenharmony_ci return 0; 3128c2ecf20Sopenharmony_ci} 3138c2ecf20Sopenharmony_ciEXPORT_SYMBOL(go7007_register_encoder); 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci/* 3168c2ecf20Sopenharmony_ci * Send the encode firmware to the encoder, which will cause it 3178c2ecf20Sopenharmony_ci * to immediately start delivering the video and audio streams. 3188c2ecf20Sopenharmony_ci * 3198c2ecf20Sopenharmony_ci * Must be called with the hw_lock held. 3208c2ecf20Sopenharmony_ci */ 3218c2ecf20Sopenharmony_ciint go7007_start_encoder(struct go7007 *go) 3228c2ecf20Sopenharmony_ci{ 3238c2ecf20Sopenharmony_ci u8 *fw; 3248c2ecf20Sopenharmony_ci int fw_len, rv = 0, i, x, y; 3258c2ecf20Sopenharmony_ci u16 intr_val, intr_data; 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci go->modet_enable = 0; 3288c2ecf20Sopenharmony_ci for (i = 0; i < 4; i++) 3298c2ecf20Sopenharmony_ci go->modet[i].enable = 0; 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci switch (v4l2_ctrl_g_ctrl(go->modet_mode)) { 3328c2ecf20Sopenharmony_ci case V4L2_DETECT_MD_MODE_GLOBAL: 3338c2ecf20Sopenharmony_ci memset(go->modet_map, 0, sizeof(go->modet_map)); 3348c2ecf20Sopenharmony_ci go->modet[0].enable = 1; 3358c2ecf20Sopenharmony_ci go->modet_enable = 1; 3368c2ecf20Sopenharmony_ci break; 3378c2ecf20Sopenharmony_ci case V4L2_DETECT_MD_MODE_REGION_GRID: 3388c2ecf20Sopenharmony_ci for (y = 0; y < go->height / 16; y++) { 3398c2ecf20Sopenharmony_ci for (x = 0; x < go->width / 16; x++) { 3408c2ecf20Sopenharmony_ci int idx = y * go->width / 16 + x; 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci go->modet[go->modet_map[idx]].enable = 1; 3438c2ecf20Sopenharmony_ci } 3448c2ecf20Sopenharmony_ci } 3458c2ecf20Sopenharmony_ci go->modet_enable = 1; 3468c2ecf20Sopenharmony_ci break; 3478c2ecf20Sopenharmony_ci } 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci if (go->dvd_mode) 3508c2ecf20Sopenharmony_ci go->modet_enable = 0; 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci if (go7007_construct_fw_image(go, &fw, &fw_len) < 0) 3538c2ecf20Sopenharmony_ci return -1; 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci if (go7007_send_firmware(go, fw, fw_len) < 0 || 3568c2ecf20Sopenharmony_ci go7007_read_interrupt(go, &intr_val, &intr_data) < 0) { 3578c2ecf20Sopenharmony_ci v4l2_err(&go->v4l2_dev, "error transferring firmware\n"); 3588c2ecf20Sopenharmony_ci rv = -1; 3598c2ecf20Sopenharmony_ci goto start_error; 3608c2ecf20Sopenharmony_ci } 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci go->state = STATE_DATA; 3638c2ecf20Sopenharmony_ci go->parse_length = 0; 3648c2ecf20Sopenharmony_ci go->seen_frame = 0; 3658c2ecf20Sopenharmony_ci if (go7007_stream_start(go) < 0) { 3668c2ecf20Sopenharmony_ci v4l2_err(&go->v4l2_dev, "error starting stream transfer\n"); 3678c2ecf20Sopenharmony_ci rv = -1; 3688c2ecf20Sopenharmony_ci goto start_error; 3698c2ecf20Sopenharmony_ci } 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_cistart_error: 3728c2ecf20Sopenharmony_ci kfree(fw); 3738c2ecf20Sopenharmony_ci return rv; 3748c2ecf20Sopenharmony_ci} 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci/* 3778c2ecf20Sopenharmony_ci * Store a byte in the current video buffer, if there is one. 3788c2ecf20Sopenharmony_ci */ 3798c2ecf20Sopenharmony_cistatic inline void store_byte(struct go7007_buffer *vb, u8 byte) 3808c2ecf20Sopenharmony_ci{ 3818c2ecf20Sopenharmony_ci if (vb && vb->vb.vb2_buf.planes[0].bytesused < GO7007_BUF_SIZE) { 3828c2ecf20Sopenharmony_ci u8 *ptr = vb2_plane_vaddr(&vb->vb.vb2_buf, 0); 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ci ptr[vb->vb.vb2_buf.planes[0].bytesused++] = byte; 3858c2ecf20Sopenharmony_ci } 3868c2ecf20Sopenharmony_ci} 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_cistatic void go7007_set_motion_regions(struct go7007 *go, struct go7007_buffer *vb, 3898c2ecf20Sopenharmony_ci u32 motion_regions) 3908c2ecf20Sopenharmony_ci{ 3918c2ecf20Sopenharmony_ci if (motion_regions != go->modet_event_status) { 3928c2ecf20Sopenharmony_ci struct v4l2_event ev = { 3938c2ecf20Sopenharmony_ci .type = V4L2_EVENT_MOTION_DET, 3948c2ecf20Sopenharmony_ci .u.motion_det = { 3958c2ecf20Sopenharmony_ci .flags = V4L2_EVENT_MD_FL_HAVE_FRAME_SEQ, 3968c2ecf20Sopenharmony_ci .frame_sequence = vb->vb.sequence, 3978c2ecf20Sopenharmony_ci .region_mask = motion_regions, 3988c2ecf20Sopenharmony_ci }, 3998c2ecf20Sopenharmony_ci }; 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci v4l2_event_queue(&go->vdev, &ev); 4028c2ecf20Sopenharmony_ci go->modet_event_status = motion_regions; 4038c2ecf20Sopenharmony_ci } 4048c2ecf20Sopenharmony_ci} 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci/* 4078c2ecf20Sopenharmony_ci * Determine regions with motion and send a motion detection event 4088c2ecf20Sopenharmony_ci * in case of changes. 4098c2ecf20Sopenharmony_ci */ 4108c2ecf20Sopenharmony_cistatic void go7007_motion_regions(struct go7007 *go, struct go7007_buffer *vb) 4118c2ecf20Sopenharmony_ci{ 4128c2ecf20Sopenharmony_ci u32 *bytesused = &vb->vb.vb2_buf.planes[0].bytesused; 4138c2ecf20Sopenharmony_ci unsigned motion[4] = { 0, 0, 0, 0 }; 4148c2ecf20Sopenharmony_ci u32 motion_regions = 0; 4158c2ecf20Sopenharmony_ci unsigned stride = (go->width + 7) >> 3; 4168c2ecf20Sopenharmony_ci unsigned x, y; 4178c2ecf20Sopenharmony_ci int i; 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci for (i = 0; i < 216; ++i) 4208c2ecf20Sopenharmony_ci store_byte(vb, go->active_map[i]); 4218c2ecf20Sopenharmony_ci for (y = 0; y < go->height / 16; y++) { 4228c2ecf20Sopenharmony_ci for (x = 0; x < go->width / 16; x++) { 4238c2ecf20Sopenharmony_ci if (!(go->active_map[y * stride + (x >> 3)] & (1 << (x & 7)))) 4248c2ecf20Sopenharmony_ci continue; 4258c2ecf20Sopenharmony_ci motion[go->modet_map[y * (go->width / 16) + x]]++; 4268c2ecf20Sopenharmony_ci } 4278c2ecf20Sopenharmony_ci } 4288c2ecf20Sopenharmony_ci motion_regions = ((motion[0] > 0) << 0) | 4298c2ecf20Sopenharmony_ci ((motion[1] > 0) << 1) | 4308c2ecf20Sopenharmony_ci ((motion[2] > 0) << 2) | 4318c2ecf20Sopenharmony_ci ((motion[3] > 0) << 3); 4328c2ecf20Sopenharmony_ci *bytesused -= 216; 4338c2ecf20Sopenharmony_ci go7007_set_motion_regions(go, vb, motion_regions); 4348c2ecf20Sopenharmony_ci} 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci/* 4378c2ecf20Sopenharmony_ci * Deliver the last video buffer and get a new one to start writing to. 4388c2ecf20Sopenharmony_ci */ 4398c2ecf20Sopenharmony_cistatic struct go7007_buffer *frame_boundary(struct go7007 *go, struct go7007_buffer *vb) 4408c2ecf20Sopenharmony_ci{ 4418c2ecf20Sopenharmony_ci u32 *bytesused; 4428c2ecf20Sopenharmony_ci struct go7007_buffer *vb_tmp = NULL; 4438c2ecf20Sopenharmony_ci unsigned long flags; 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci if (vb == NULL) { 4468c2ecf20Sopenharmony_ci spin_lock_irqsave(&go->spinlock, flags); 4478c2ecf20Sopenharmony_ci if (!list_empty(&go->vidq_active)) 4488c2ecf20Sopenharmony_ci vb = go->active_buf = 4498c2ecf20Sopenharmony_ci list_first_entry(&go->vidq_active, struct go7007_buffer, list); 4508c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&go->spinlock, flags); 4518c2ecf20Sopenharmony_ci go->next_seq++; 4528c2ecf20Sopenharmony_ci return vb; 4538c2ecf20Sopenharmony_ci } 4548c2ecf20Sopenharmony_ci bytesused = &vb->vb.vb2_buf.planes[0].bytesused; 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci vb->vb.sequence = go->next_seq++; 4578c2ecf20Sopenharmony_ci if (vb->modet_active && *bytesused + 216 < GO7007_BUF_SIZE) 4588c2ecf20Sopenharmony_ci go7007_motion_regions(go, vb); 4598c2ecf20Sopenharmony_ci else 4608c2ecf20Sopenharmony_ci go7007_set_motion_regions(go, vb, 0); 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci vb->vb.vb2_buf.timestamp = ktime_get_ns(); 4638c2ecf20Sopenharmony_ci vb_tmp = vb; 4648c2ecf20Sopenharmony_ci spin_lock_irqsave(&go->spinlock, flags); 4658c2ecf20Sopenharmony_ci list_del(&vb->list); 4668c2ecf20Sopenharmony_ci if (list_empty(&go->vidq_active)) 4678c2ecf20Sopenharmony_ci vb = NULL; 4688c2ecf20Sopenharmony_ci else 4698c2ecf20Sopenharmony_ci vb = list_first_entry(&go->vidq_active, 4708c2ecf20Sopenharmony_ci struct go7007_buffer, list); 4718c2ecf20Sopenharmony_ci go->active_buf = vb; 4728c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&go->spinlock, flags); 4738c2ecf20Sopenharmony_ci vb2_buffer_done(&vb_tmp->vb.vb2_buf, VB2_BUF_STATE_DONE); 4748c2ecf20Sopenharmony_ci return vb; 4758c2ecf20Sopenharmony_ci} 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_cistatic void write_bitmap_word(struct go7007 *go) 4788c2ecf20Sopenharmony_ci{ 4798c2ecf20Sopenharmony_ci int x, y, i, stride = ((go->width >> 4) + 7) >> 3; 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_ci for (i = 0; i < 16; ++i) { 4828c2ecf20Sopenharmony_ci y = (((go->parse_length - 1) << 3) + i) / (go->width >> 4); 4838c2ecf20Sopenharmony_ci x = (((go->parse_length - 1) << 3) + i) % (go->width >> 4); 4848c2ecf20Sopenharmony_ci if (stride * y + (x >> 3) < sizeof(go->active_map)) 4858c2ecf20Sopenharmony_ci go->active_map[stride * y + (x >> 3)] |= 4868c2ecf20Sopenharmony_ci (go->modet_word & 1) << (x & 0x7); 4878c2ecf20Sopenharmony_ci go->modet_word >>= 1; 4888c2ecf20Sopenharmony_ci } 4898c2ecf20Sopenharmony_ci} 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_ci/* 4928c2ecf20Sopenharmony_ci * Parse a chunk of the video stream into frames. The frames are not 4938c2ecf20Sopenharmony_ci * delimited by the hardware, so we have to parse the frame boundaries 4948c2ecf20Sopenharmony_ci * based on the type of video stream we're receiving. 4958c2ecf20Sopenharmony_ci */ 4968c2ecf20Sopenharmony_civoid go7007_parse_video_stream(struct go7007 *go, u8 *buf, int length) 4978c2ecf20Sopenharmony_ci{ 4988c2ecf20Sopenharmony_ci struct go7007_buffer *vb = go->active_buf; 4998c2ecf20Sopenharmony_ci int i, seq_start_code = -1, gop_start_code = -1, frame_start_code = -1; 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_ci switch (go->format) { 5028c2ecf20Sopenharmony_ci case V4L2_PIX_FMT_MPEG4: 5038c2ecf20Sopenharmony_ci seq_start_code = 0xB0; 5048c2ecf20Sopenharmony_ci gop_start_code = 0xB3; 5058c2ecf20Sopenharmony_ci frame_start_code = 0xB6; 5068c2ecf20Sopenharmony_ci break; 5078c2ecf20Sopenharmony_ci case V4L2_PIX_FMT_MPEG1: 5088c2ecf20Sopenharmony_ci case V4L2_PIX_FMT_MPEG2: 5098c2ecf20Sopenharmony_ci seq_start_code = 0xB3; 5108c2ecf20Sopenharmony_ci gop_start_code = 0xB8; 5118c2ecf20Sopenharmony_ci frame_start_code = 0x00; 5128c2ecf20Sopenharmony_ci break; 5138c2ecf20Sopenharmony_ci } 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_ci for (i = 0; i < length; ++i) { 5168c2ecf20Sopenharmony_ci if (vb && vb->vb.vb2_buf.planes[0].bytesused >= 5178c2ecf20Sopenharmony_ci GO7007_BUF_SIZE - 3) { 5188c2ecf20Sopenharmony_ci v4l2_info(&go->v4l2_dev, "dropping oversized frame\n"); 5198c2ecf20Sopenharmony_ci vb->vb.vb2_buf.planes[0].bytesused = 0; 5208c2ecf20Sopenharmony_ci vb->frame_offset = 0; 5218c2ecf20Sopenharmony_ci vb->modet_active = 0; 5228c2ecf20Sopenharmony_ci vb = go->active_buf = NULL; 5238c2ecf20Sopenharmony_ci } 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_ci switch (go->state) { 5268c2ecf20Sopenharmony_ci case STATE_DATA: 5278c2ecf20Sopenharmony_ci switch (buf[i]) { 5288c2ecf20Sopenharmony_ci case 0x00: 5298c2ecf20Sopenharmony_ci go->state = STATE_00; 5308c2ecf20Sopenharmony_ci break; 5318c2ecf20Sopenharmony_ci case 0xFF: 5328c2ecf20Sopenharmony_ci go->state = STATE_FF; 5338c2ecf20Sopenharmony_ci break; 5348c2ecf20Sopenharmony_ci default: 5358c2ecf20Sopenharmony_ci store_byte(vb, buf[i]); 5368c2ecf20Sopenharmony_ci break; 5378c2ecf20Sopenharmony_ci } 5388c2ecf20Sopenharmony_ci break; 5398c2ecf20Sopenharmony_ci case STATE_00: 5408c2ecf20Sopenharmony_ci switch (buf[i]) { 5418c2ecf20Sopenharmony_ci case 0x00: 5428c2ecf20Sopenharmony_ci go->state = STATE_00_00; 5438c2ecf20Sopenharmony_ci break; 5448c2ecf20Sopenharmony_ci case 0xFF: 5458c2ecf20Sopenharmony_ci store_byte(vb, 0x00); 5468c2ecf20Sopenharmony_ci go->state = STATE_FF; 5478c2ecf20Sopenharmony_ci break; 5488c2ecf20Sopenharmony_ci default: 5498c2ecf20Sopenharmony_ci store_byte(vb, 0x00); 5508c2ecf20Sopenharmony_ci store_byte(vb, buf[i]); 5518c2ecf20Sopenharmony_ci go->state = STATE_DATA; 5528c2ecf20Sopenharmony_ci break; 5538c2ecf20Sopenharmony_ci } 5548c2ecf20Sopenharmony_ci break; 5558c2ecf20Sopenharmony_ci case STATE_00_00: 5568c2ecf20Sopenharmony_ci switch (buf[i]) { 5578c2ecf20Sopenharmony_ci case 0x00: 5588c2ecf20Sopenharmony_ci store_byte(vb, 0x00); 5598c2ecf20Sopenharmony_ci /* go->state remains STATE_00_00 */ 5608c2ecf20Sopenharmony_ci break; 5618c2ecf20Sopenharmony_ci case 0x01: 5628c2ecf20Sopenharmony_ci go->state = STATE_00_00_01; 5638c2ecf20Sopenharmony_ci break; 5648c2ecf20Sopenharmony_ci case 0xFF: 5658c2ecf20Sopenharmony_ci store_byte(vb, 0x00); 5668c2ecf20Sopenharmony_ci store_byte(vb, 0x00); 5678c2ecf20Sopenharmony_ci go->state = STATE_FF; 5688c2ecf20Sopenharmony_ci break; 5698c2ecf20Sopenharmony_ci default: 5708c2ecf20Sopenharmony_ci store_byte(vb, 0x00); 5718c2ecf20Sopenharmony_ci store_byte(vb, 0x00); 5728c2ecf20Sopenharmony_ci store_byte(vb, buf[i]); 5738c2ecf20Sopenharmony_ci go->state = STATE_DATA; 5748c2ecf20Sopenharmony_ci break; 5758c2ecf20Sopenharmony_ci } 5768c2ecf20Sopenharmony_ci break; 5778c2ecf20Sopenharmony_ci case STATE_00_00_01: 5788c2ecf20Sopenharmony_ci if (buf[i] == 0xF8 && go->modet_enable == 0) { 5798c2ecf20Sopenharmony_ci /* MODET start code, but MODET not enabled */ 5808c2ecf20Sopenharmony_ci store_byte(vb, 0x00); 5818c2ecf20Sopenharmony_ci store_byte(vb, 0x00); 5828c2ecf20Sopenharmony_ci store_byte(vb, 0x01); 5838c2ecf20Sopenharmony_ci store_byte(vb, 0xF8); 5848c2ecf20Sopenharmony_ci go->state = STATE_DATA; 5858c2ecf20Sopenharmony_ci break; 5868c2ecf20Sopenharmony_ci } 5878c2ecf20Sopenharmony_ci /* If this is the start of a new MPEG frame, 5888c2ecf20Sopenharmony_ci * get a new buffer */ 5898c2ecf20Sopenharmony_ci if ((go->format == V4L2_PIX_FMT_MPEG1 || 5908c2ecf20Sopenharmony_ci go->format == V4L2_PIX_FMT_MPEG2 || 5918c2ecf20Sopenharmony_ci go->format == V4L2_PIX_FMT_MPEG4) && 5928c2ecf20Sopenharmony_ci (buf[i] == seq_start_code || 5938c2ecf20Sopenharmony_ci buf[i] == gop_start_code || 5948c2ecf20Sopenharmony_ci buf[i] == frame_start_code)) { 5958c2ecf20Sopenharmony_ci if (vb == NULL || go->seen_frame) 5968c2ecf20Sopenharmony_ci vb = frame_boundary(go, vb); 5978c2ecf20Sopenharmony_ci go->seen_frame = buf[i] == frame_start_code; 5988c2ecf20Sopenharmony_ci if (vb && go->seen_frame) 5998c2ecf20Sopenharmony_ci vb->frame_offset = 6008c2ecf20Sopenharmony_ci vb->vb.vb2_buf.planes[0].bytesused; 6018c2ecf20Sopenharmony_ci } 6028c2ecf20Sopenharmony_ci /* Handle any special chunk types, or just write the 6038c2ecf20Sopenharmony_ci * start code to the (potentially new) buffer */ 6048c2ecf20Sopenharmony_ci switch (buf[i]) { 6058c2ecf20Sopenharmony_ci case 0xF5: /* timestamp */ 6068c2ecf20Sopenharmony_ci go->parse_length = 12; 6078c2ecf20Sopenharmony_ci go->state = STATE_UNPARSED; 6088c2ecf20Sopenharmony_ci break; 6098c2ecf20Sopenharmony_ci case 0xF6: /* vbi */ 6108c2ecf20Sopenharmony_ci go->state = STATE_VBI_LEN_A; 6118c2ecf20Sopenharmony_ci break; 6128c2ecf20Sopenharmony_ci case 0xF8: /* MD map */ 6138c2ecf20Sopenharmony_ci go->parse_length = 0; 6148c2ecf20Sopenharmony_ci memset(go->active_map, 0, 6158c2ecf20Sopenharmony_ci sizeof(go->active_map)); 6168c2ecf20Sopenharmony_ci go->state = STATE_MODET_MAP; 6178c2ecf20Sopenharmony_ci break; 6188c2ecf20Sopenharmony_ci case 0xFF: /* Potential JPEG start code */ 6198c2ecf20Sopenharmony_ci store_byte(vb, 0x00); 6208c2ecf20Sopenharmony_ci store_byte(vb, 0x00); 6218c2ecf20Sopenharmony_ci store_byte(vb, 0x01); 6228c2ecf20Sopenharmony_ci go->state = STATE_FF; 6238c2ecf20Sopenharmony_ci break; 6248c2ecf20Sopenharmony_ci default: 6258c2ecf20Sopenharmony_ci store_byte(vb, 0x00); 6268c2ecf20Sopenharmony_ci store_byte(vb, 0x00); 6278c2ecf20Sopenharmony_ci store_byte(vb, 0x01); 6288c2ecf20Sopenharmony_ci store_byte(vb, buf[i]); 6298c2ecf20Sopenharmony_ci go->state = STATE_DATA; 6308c2ecf20Sopenharmony_ci break; 6318c2ecf20Sopenharmony_ci } 6328c2ecf20Sopenharmony_ci break; 6338c2ecf20Sopenharmony_ci case STATE_FF: 6348c2ecf20Sopenharmony_ci switch (buf[i]) { 6358c2ecf20Sopenharmony_ci case 0x00: 6368c2ecf20Sopenharmony_ci store_byte(vb, 0xFF); 6378c2ecf20Sopenharmony_ci go->state = STATE_00; 6388c2ecf20Sopenharmony_ci break; 6398c2ecf20Sopenharmony_ci case 0xFF: 6408c2ecf20Sopenharmony_ci store_byte(vb, 0xFF); 6418c2ecf20Sopenharmony_ci /* go->state remains STATE_FF */ 6428c2ecf20Sopenharmony_ci break; 6438c2ecf20Sopenharmony_ci case 0xD8: 6448c2ecf20Sopenharmony_ci if (go->format == V4L2_PIX_FMT_MJPEG) 6458c2ecf20Sopenharmony_ci vb = frame_boundary(go, vb); 6468c2ecf20Sopenharmony_ci fallthrough; 6478c2ecf20Sopenharmony_ci default: 6488c2ecf20Sopenharmony_ci store_byte(vb, 0xFF); 6498c2ecf20Sopenharmony_ci store_byte(vb, buf[i]); 6508c2ecf20Sopenharmony_ci go->state = STATE_DATA; 6518c2ecf20Sopenharmony_ci break; 6528c2ecf20Sopenharmony_ci } 6538c2ecf20Sopenharmony_ci break; 6548c2ecf20Sopenharmony_ci case STATE_VBI_LEN_A: 6558c2ecf20Sopenharmony_ci go->parse_length = buf[i] << 8; 6568c2ecf20Sopenharmony_ci go->state = STATE_VBI_LEN_B; 6578c2ecf20Sopenharmony_ci break; 6588c2ecf20Sopenharmony_ci case STATE_VBI_LEN_B: 6598c2ecf20Sopenharmony_ci go->parse_length |= buf[i]; 6608c2ecf20Sopenharmony_ci if (go->parse_length > 0) 6618c2ecf20Sopenharmony_ci go->state = STATE_UNPARSED; 6628c2ecf20Sopenharmony_ci else 6638c2ecf20Sopenharmony_ci go->state = STATE_DATA; 6648c2ecf20Sopenharmony_ci break; 6658c2ecf20Sopenharmony_ci case STATE_MODET_MAP: 6668c2ecf20Sopenharmony_ci if (go->parse_length < 204) { 6678c2ecf20Sopenharmony_ci if (go->parse_length & 1) { 6688c2ecf20Sopenharmony_ci go->modet_word |= buf[i]; 6698c2ecf20Sopenharmony_ci write_bitmap_word(go); 6708c2ecf20Sopenharmony_ci } else 6718c2ecf20Sopenharmony_ci go->modet_word = buf[i] << 8; 6728c2ecf20Sopenharmony_ci } else if (go->parse_length == 207 && vb) { 6738c2ecf20Sopenharmony_ci vb->modet_active = buf[i]; 6748c2ecf20Sopenharmony_ci } 6758c2ecf20Sopenharmony_ci if (++go->parse_length == 208) 6768c2ecf20Sopenharmony_ci go->state = STATE_DATA; 6778c2ecf20Sopenharmony_ci break; 6788c2ecf20Sopenharmony_ci case STATE_UNPARSED: 6798c2ecf20Sopenharmony_ci if (--go->parse_length == 0) 6808c2ecf20Sopenharmony_ci go->state = STATE_DATA; 6818c2ecf20Sopenharmony_ci break; 6828c2ecf20Sopenharmony_ci } 6838c2ecf20Sopenharmony_ci } 6848c2ecf20Sopenharmony_ci} 6858c2ecf20Sopenharmony_ciEXPORT_SYMBOL(go7007_parse_video_stream); 6868c2ecf20Sopenharmony_ci 6878c2ecf20Sopenharmony_ci/* 6888c2ecf20Sopenharmony_ci * Allocate a new go7007 struct. Used by the hardware-specific probe. 6898c2ecf20Sopenharmony_ci */ 6908c2ecf20Sopenharmony_cistruct go7007 *go7007_alloc(const struct go7007_board_info *board, 6918c2ecf20Sopenharmony_ci struct device *dev) 6928c2ecf20Sopenharmony_ci{ 6938c2ecf20Sopenharmony_ci struct go7007 *go; 6948c2ecf20Sopenharmony_ci 6958c2ecf20Sopenharmony_ci go = kzalloc(sizeof(struct go7007), GFP_KERNEL); 6968c2ecf20Sopenharmony_ci if (go == NULL) 6978c2ecf20Sopenharmony_ci return NULL; 6988c2ecf20Sopenharmony_ci go->dev = dev; 6998c2ecf20Sopenharmony_ci go->board_info = board; 7008c2ecf20Sopenharmony_ci go->tuner_type = -1; 7018c2ecf20Sopenharmony_ci mutex_init(&go->hw_lock); 7028c2ecf20Sopenharmony_ci init_waitqueue_head(&go->frame_waitq); 7038c2ecf20Sopenharmony_ci spin_lock_init(&go->spinlock); 7048c2ecf20Sopenharmony_ci go->status = STATUS_INIT; 7058c2ecf20Sopenharmony_ci init_waitqueue_head(&go->interrupt_waitq); 7068c2ecf20Sopenharmony_ci go7007_update_board(go); 7078c2ecf20Sopenharmony_ci go->format = V4L2_PIX_FMT_MJPEG; 7088c2ecf20Sopenharmony_ci go->bitrate = 1500000; 7098c2ecf20Sopenharmony_ci go->fps_scale = 1; 7108c2ecf20Sopenharmony_ci go->aspect_ratio = GO7007_RATIO_1_1; 7118c2ecf20Sopenharmony_ci 7128c2ecf20Sopenharmony_ci return go; 7138c2ecf20Sopenharmony_ci} 7148c2ecf20Sopenharmony_ciEXPORT_SYMBOL(go7007_alloc); 7158c2ecf20Sopenharmony_ci 7168c2ecf20Sopenharmony_civoid go7007_update_board(struct go7007 *go) 7178c2ecf20Sopenharmony_ci{ 7188c2ecf20Sopenharmony_ci const struct go7007_board_info *board = go->board_info; 7198c2ecf20Sopenharmony_ci 7208c2ecf20Sopenharmony_ci if (board->sensor_flags & GO7007_SENSOR_TV) { 7218c2ecf20Sopenharmony_ci go->standard = GO7007_STD_NTSC; 7228c2ecf20Sopenharmony_ci go->std = V4L2_STD_NTSC_M; 7238c2ecf20Sopenharmony_ci go->width = 720; 7248c2ecf20Sopenharmony_ci go->height = 480; 7258c2ecf20Sopenharmony_ci go->sensor_framerate = 30000; 7268c2ecf20Sopenharmony_ci } else { 7278c2ecf20Sopenharmony_ci go->standard = GO7007_STD_OTHER; 7288c2ecf20Sopenharmony_ci go->width = board->sensor_width; 7298c2ecf20Sopenharmony_ci go->height = board->sensor_height; 7308c2ecf20Sopenharmony_ci go->sensor_framerate = board->sensor_framerate; 7318c2ecf20Sopenharmony_ci } 7328c2ecf20Sopenharmony_ci go->encoder_v_offset = board->sensor_v_offset; 7338c2ecf20Sopenharmony_ci go->encoder_h_offset = board->sensor_h_offset; 7348c2ecf20Sopenharmony_ci} 7358c2ecf20Sopenharmony_ciEXPORT_SYMBOL(go7007_update_board); 7368c2ecf20Sopenharmony_ci 7378c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 738