128cae868SHans Verkuil /* 228cae868SHans Verkuil * Copyright (C) 2010-2013 Bluecherry, LLC <http://www.bluecherrydvr.com> 328cae868SHans Verkuil * 428cae868SHans Verkuil * Original author: 528cae868SHans Verkuil * Ben Collins <bcollins@ubuntu.com> 628cae868SHans Verkuil * 728cae868SHans Verkuil * Additional work by: 828cae868SHans Verkuil * John Brooks <john.brooks@bluecherry.net> 928cae868SHans Verkuil * 1028cae868SHans Verkuil * This program is free software; you can redistribute it and/or modify 1128cae868SHans Verkuil * it under the terms of the GNU General Public License as published by 1228cae868SHans Verkuil * the Free Software Foundation; either version 2 of the License, or 1328cae868SHans Verkuil * (at your option) any later version. 1428cae868SHans Verkuil * 1528cae868SHans Verkuil * This program is distributed in the hope that it will be useful, 1628cae868SHans Verkuil * but WITHOUT ANY WARRANTY; without even the implied warranty of 1728cae868SHans Verkuil * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 1828cae868SHans Verkuil * GNU General Public License for more details. 1928cae868SHans Verkuil */ 2028cae868SHans Verkuil 2128cae868SHans Verkuil #include <linux/kernel.h> 2228cae868SHans Verkuil #include <linux/module.h> 2328cae868SHans Verkuil #include <linux/videodev2.h> 2428cae868SHans Verkuil #include <media/v4l2-ioctl.h> 2528cae868SHans Verkuil 2628cae868SHans Verkuil #include "solo6x10.h" 2728cae868SHans Verkuil 2828cae868SHans Verkuil #define SOLO_VCLK_DELAY 3 2928cae868SHans Verkuil #define SOLO_PROGRESSIVE_VSIZE 1024 3028cae868SHans Verkuil 3128cae868SHans Verkuil #define SOLO_MOT_THRESH_W 64 3228cae868SHans Verkuil #define SOLO_MOT_THRESH_H 64 3328cae868SHans Verkuil #define SOLO_MOT_THRESH_SIZE 8192 3428cae868SHans Verkuil #define SOLO_MOT_THRESH_REAL (SOLO_MOT_THRESH_W * SOLO_MOT_THRESH_H) 3528cae868SHans Verkuil #define SOLO_MOT_FLAG_SIZE 1024 3628cae868SHans Verkuil #define SOLO_MOT_FLAG_AREA (SOLO_MOT_FLAG_SIZE * 16) 3728cae868SHans Verkuil 3828cae868SHans Verkuil static void solo_vin_config(struct solo_dev *solo_dev) 3928cae868SHans Verkuil { 4028cae868SHans Verkuil solo_dev->vin_hstart = 8; 4128cae868SHans Verkuil solo_dev->vin_vstart = 2; 4228cae868SHans Verkuil 4328cae868SHans Verkuil solo_reg_write(solo_dev, SOLO_SYS_VCLK, 4428cae868SHans Verkuil SOLO_VCLK_SELECT(2) | 4528cae868SHans Verkuil SOLO_VCLK_VIN1415_DELAY(SOLO_VCLK_DELAY) | 4628cae868SHans Verkuil SOLO_VCLK_VIN1213_DELAY(SOLO_VCLK_DELAY) | 4728cae868SHans Verkuil SOLO_VCLK_VIN1011_DELAY(SOLO_VCLK_DELAY) | 4828cae868SHans Verkuil SOLO_VCLK_VIN0809_DELAY(SOLO_VCLK_DELAY) | 4928cae868SHans Verkuil SOLO_VCLK_VIN0607_DELAY(SOLO_VCLK_DELAY) | 5028cae868SHans Verkuil SOLO_VCLK_VIN0405_DELAY(SOLO_VCLK_DELAY) | 5128cae868SHans Verkuil SOLO_VCLK_VIN0203_DELAY(SOLO_VCLK_DELAY) | 5228cae868SHans Verkuil SOLO_VCLK_VIN0001_DELAY(SOLO_VCLK_DELAY)); 5328cae868SHans Verkuil 5428cae868SHans Verkuil solo_reg_write(solo_dev, SOLO_VI_ACT_I_P, 5528cae868SHans Verkuil SOLO_VI_H_START(solo_dev->vin_hstart) | 5628cae868SHans Verkuil SOLO_VI_V_START(solo_dev->vin_vstart) | 5728cae868SHans Verkuil SOLO_VI_V_STOP(solo_dev->vin_vstart + 5828cae868SHans Verkuil solo_dev->video_vsize)); 5928cae868SHans Verkuil 6028cae868SHans Verkuil solo_reg_write(solo_dev, SOLO_VI_ACT_I_S, 6128cae868SHans Verkuil SOLO_VI_H_START(solo_dev->vout_hstart) | 6228cae868SHans Verkuil SOLO_VI_V_START(solo_dev->vout_vstart) | 6328cae868SHans Verkuil SOLO_VI_V_STOP(solo_dev->vout_vstart + 6428cae868SHans Verkuil solo_dev->video_vsize)); 6528cae868SHans Verkuil 6628cae868SHans Verkuil solo_reg_write(solo_dev, SOLO_VI_ACT_P, 6728cae868SHans Verkuil SOLO_VI_H_START(0) | 6828cae868SHans Verkuil SOLO_VI_V_START(1) | 6928cae868SHans Verkuil SOLO_VI_V_STOP(SOLO_PROGRESSIVE_VSIZE)); 7028cae868SHans Verkuil 7128cae868SHans Verkuil solo_reg_write(solo_dev, SOLO_VI_CH_FORMAT, 7228cae868SHans Verkuil SOLO_VI_FD_SEL_MASK(0) | SOLO_VI_PROG_MASK(0)); 7328cae868SHans Verkuil 74*16790554SMauro Carvalho Chehab /* On 6110, initialize mozaic darkness strength */ 7528cae868SHans Verkuil if (solo_dev->type == SOLO_DEV_6010) 7628cae868SHans Verkuil solo_reg_write(solo_dev, SOLO_VI_FMT_CFG, 0); 7728cae868SHans Verkuil else 7828cae868SHans Verkuil solo_reg_write(solo_dev, SOLO_VI_FMT_CFG, 16 << 22); 7928cae868SHans Verkuil 8028cae868SHans Verkuil solo_reg_write(solo_dev, SOLO_VI_PAGE_SW, 2); 8128cae868SHans Verkuil 8228cae868SHans Verkuil if (solo_dev->video_type == SOLO_VO_FMT_TYPE_NTSC) { 8328cae868SHans Verkuil solo_reg_write(solo_dev, SOLO_VI_PB_CONFIG, 8428cae868SHans Verkuil SOLO_VI_PB_USER_MODE); 8528cae868SHans Verkuil solo_reg_write(solo_dev, SOLO_VI_PB_RANGE_HV, 8628cae868SHans Verkuil SOLO_VI_PB_HSIZE(858) | SOLO_VI_PB_VSIZE(246)); 8728cae868SHans Verkuil solo_reg_write(solo_dev, SOLO_VI_PB_ACT_V, 8828cae868SHans Verkuil SOLO_VI_PB_VSTART(4) | 8928cae868SHans Verkuil SOLO_VI_PB_VSTOP(4 + 240)); 9028cae868SHans Verkuil } else { 9128cae868SHans Verkuil solo_reg_write(solo_dev, SOLO_VI_PB_CONFIG, 9228cae868SHans Verkuil SOLO_VI_PB_USER_MODE | SOLO_VI_PB_PAL); 9328cae868SHans Verkuil solo_reg_write(solo_dev, SOLO_VI_PB_RANGE_HV, 9428cae868SHans Verkuil SOLO_VI_PB_HSIZE(864) | SOLO_VI_PB_VSIZE(294)); 9528cae868SHans Verkuil solo_reg_write(solo_dev, SOLO_VI_PB_ACT_V, 9628cae868SHans Verkuil SOLO_VI_PB_VSTART(4) | 9728cae868SHans Verkuil SOLO_VI_PB_VSTOP(4 + 288)); 9828cae868SHans Verkuil } 9928cae868SHans Verkuil solo_reg_write(solo_dev, SOLO_VI_PB_ACT_H, SOLO_VI_PB_HSTART(16) | 10028cae868SHans Verkuil SOLO_VI_PB_HSTOP(16 + 720)); 10128cae868SHans Verkuil } 10228cae868SHans Verkuil 10328cae868SHans Verkuil static void solo_vout_config_cursor(struct solo_dev *dev) 10428cae868SHans Verkuil { 10528cae868SHans Verkuil int i; 10628cae868SHans Verkuil 10728cae868SHans Verkuil /* Load (blank) cursor bitmap mask (2bpp) */ 10828cae868SHans Verkuil for (i = 0; i < 20; i++) 10928cae868SHans Verkuil solo_reg_write(dev, SOLO_VO_CURSOR_MASK(i), 0); 11028cae868SHans Verkuil 11128cae868SHans Verkuil solo_reg_write(dev, SOLO_VO_CURSOR_POS, 0); 11228cae868SHans Verkuil 11328cae868SHans Verkuil solo_reg_write(dev, SOLO_VO_CURSOR_CLR, 11428cae868SHans Verkuil (0x80 << 24) | (0x80 << 16) | (0x10 << 8) | 0x80); 11528cae868SHans Verkuil solo_reg_write(dev, SOLO_VO_CURSOR_CLR2, (0xe0 << 8) | 0x80); 11628cae868SHans Verkuil } 11728cae868SHans Verkuil 11828cae868SHans Verkuil static void solo_vout_config(struct solo_dev *solo_dev) 11928cae868SHans Verkuil { 12028cae868SHans Verkuil solo_dev->vout_hstart = 6; 12128cae868SHans Verkuil solo_dev->vout_vstart = 8; 12228cae868SHans Verkuil 12328cae868SHans Verkuil solo_reg_write(solo_dev, SOLO_VO_FMT_ENC, 12428cae868SHans Verkuil solo_dev->video_type | 12528cae868SHans Verkuil SOLO_VO_USER_COLOR_SET_NAV | 12628cae868SHans Verkuil SOLO_VO_USER_COLOR_SET_NAH | 12728cae868SHans Verkuil SOLO_VO_NA_COLOR_Y(0) | 12828cae868SHans Verkuil SOLO_VO_NA_COLOR_CB(0) | 12928cae868SHans Verkuil SOLO_VO_NA_COLOR_CR(0)); 13028cae868SHans Verkuil 13128cae868SHans Verkuil solo_reg_write(solo_dev, SOLO_VO_ACT_H, 13228cae868SHans Verkuil SOLO_VO_H_START(solo_dev->vout_hstart) | 13328cae868SHans Verkuil SOLO_VO_H_STOP(solo_dev->vout_hstart + 13428cae868SHans Verkuil solo_dev->video_hsize)); 13528cae868SHans Verkuil 13628cae868SHans Verkuil solo_reg_write(solo_dev, SOLO_VO_ACT_V, 13728cae868SHans Verkuil SOLO_VO_V_START(solo_dev->vout_vstart) | 13828cae868SHans Verkuil SOLO_VO_V_STOP(solo_dev->vout_vstart + 13928cae868SHans Verkuil solo_dev->video_vsize)); 14028cae868SHans Verkuil 14128cae868SHans Verkuil solo_reg_write(solo_dev, SOLO_VO_RANGE_HV, 14228cae868SHans Verkuil SOLO_VO_H_LEN(solo_dev->video_hsize) | 14328cae868SHans Verkuil SOLO_VO_V_LEN(solo_dev->video_vsize)); 14428cae868SHans Verkuil 14528cae868SHans Verkuil /* Border & background colors */ 14628cae868SHans Verkuil solo_reg_write(solo_dev, SOLO_VO_BORDER_LINE_COLOR, 14728cae868SHans Verkuil (0xa0 << 24) | (0x88 << 16) | (0xa0 << 8) | 0x88); 14828cae868SHans Verkuil solo_reg_write(solo_dev, SOLO_VO_BORDER_FILL_COLOR, 14928cae868SHans Verkuil (0x10 << 24) | (0x8f << 16) | (0x10 << 8) | 0x8f); 15028cae868SHans Verkuil solo_reg_write(solo_dev, SOLO_VO_BKG_COLOR, 15128cae868SHans Verkuil (16 << 24) | (128 << 16) | (16 << 8) | 128); 15228cae868SHans Verkuil 15328cae868SHans Verkuil solo_reg_write(solo_dev, SOLO_VO_DISP_ERASE, SOLO_VO_DISP_ERASE_ON); 15428cae868SHans Verkuil 15528cae868SHans Verkuil solo_reg_write(solo_dev, SOLO_VI_WIN_SW, 0); 15628cae868SHans Verkuil 15728cae868SHans Verkuil solo_reg_write(solo_dev, SOLO_VO_ZOOM_CTRL, 0); 15828cae868SHans Verkuil solo_reg_write(solo_dev, SOLO_VO_FREEZE_CTRL, 0); 15928cae868SHans Verkuil 16028cae868SHans Verkuil solo_reg_write(solo_dev, SOLO_VO_DISP_CTRL, SOLO_VO_DISP_ON | 16128cae868SHans Verkuil SOLO_VO_DISP_ERASE_COUNT(8) | 16228cae868SHans Verkuil SOLO_VO_DISP_BASE(SOLO_DISP_EXT_ADDR)); 16328cae868SHans Verkuil 16428cae868SHans Verkuil 16528cae868SHans Verkuil solo_vout_config_cursor(solo_dev); 16628cae868SHans Verkuil 16728cae868SHans Verkuil /* Enable channels we support */ 16828cae868SHans Verkuil solo_reg_write(solo_dev, SOLO_VI_CH_ENA, 16928cae868SHans Verkuil (1 << solo_dev->nr_chans) - 1); 17028cae868SHans Verkuil } 17128cae868SHans Verkuil 17228cae868SHans Verkuil static int solo_dma_vin_region(struct solo_dev *solo_dev, u32 off, 17328cae868SHans Verkuil u16 val, int reg_size) 17428cae868SHans Verkuil { 175c44b6484SHans Verkuil __le16 *buf; 17628cae868SHans Verkuil const int n = 64, size = n * sizeof(*buf); 17728cae868SHans Verkuil int i, ret = 0; 17828cae868SHans Verkuil 17928cae868SHans Verkuil buf = kmalloc(size, GFP_KERNEL); 18028cae868SHans Verkuil if (!buf) 18128cae868SHans Verkuil return -ENOMEM; 18228cae868SHans Verkuil 18328cae868SHans Verkuil for (i = 0; i < n; i++) 18428cae868SHans Verkuil buf[i] = cpu_to_le16(val); 18528cae868SHans Verkuil 18628cae868SHans Verkuil for (i = 0; i < reg_size; i += size) { 18728cae868SHans Verkuil ret = solo_p2m_dma(solo_dev, 1, buf, 18828cae868SHans Verkuil SOLO_MOTION_EXT_ADDR(solo_dev) + off + i, 18928cae868SHans Verkuil size, 0, 0); 19028cae868SHans Verkuil 19128cae868SHans Verkuil if (ret) 19228cae868SHans Verkuil break; 19328cae868SHans Verkuil } 19428cae868SHans Verkuil 19528cae868SHans Verkuil kfree(buf); 19628cae868SHans Verkuil return ret; 19728cae868SHans Verkuil } 19828cae868SHans Verkuil 19928cae868SHans Verkuil int solo_set_motion_threshold(struct solo_dev *solo_dev, u8 ch, u16 val) 20028cae868SHans Verkuil { 20128cae868SHans Verkuil if (ch > solo_dev->nr_chans) 20228cae868SHans Verkuil return -EINVAL; 20328cae868SHans Verkuil 20428cae868SHans Verkuil return solo_dma_vin_region(solo_dev, SOLO_MOT_FLAG_AREA + 20528cae868SHans Verkuil (ch * SOLO_MOT_THRESH_SIZE * 2), 20628cae868SHans Verkuil val, SOLO_MOT_THRESH_SIZE); 20728cae868SHans Verkuil } 20828cae868SHans Verkuil 20928cae868SHans Verkuil int solo_set_motion_block(struct solo_dev *solo_dev, u8 ch, 21028cae868SHans Verkuil const u16 *thresholds) 21128cae868SHans Verkuil { 21228cae868SHans Verkuil const unsigned size = sizeof(u16) * 64; 21328cae868SHans Verkuil u32 off = SOLO_MOT_FLAG_AREA + ch * SOLO_MOT_THRESH_SIZE * 2; 214c44b6484SHans Verkuil __le16 *buf; 21528cae868SHans Verkuil int x, y; 21628cae868SHans Verkuil int ret = 0; 21728cae868SHans Verkuil 21828cae868SHans Verkuil buf = kzalloc(size, GFP_KERNEL); 2190a128308SHans Verkuil if (buf == NULL) 2200a128308SHans Verkuil return -ENOMEM; 22128cae868SHans Verkuil for (y = 0; y < SOLO_MOTION_SZ; y++) { 22228cae868SHans Verkuil for (x = 0; x < SOLO_MOTION_SZ; x++) 22328cae868SHans Verkuil buf[x] = cpu_to_le16(thresholds[y * SOLO_MOTION_SZ + x]); 22428cae868SHans Verkuil ret |= solo_p2m_dma(solo_dev, 1, buf, 22528cae868SHans Verkuil SOLO_MOTION_EXT_ADDR(solo_dev) + off + y * size, 22628cae868SHans Verkuil size, 0, 0); 22728cae868SHans Verkuil } 22828cae868SHans Verkuil kfree(buf); 22928cae868SHans Verkuil return ret; 23028cae868SHans Verkuil } 23128cae868SHans Verkuil 23228cae868SHans Verkuil /* First 8k is motion flag (512 bytes * 16). Following that is an 8k+8k 23328cae868SHans Verkuil * threshold and working table for each channel. At least that's what the 23428cae868SHans Verkuil * spec says. However, this code (taken from rdk) has some mystery 8k 23528cae868SHans Verkuil * block right after the flag area, before the first thresh table. */ 23628cae868SHans Verkuil static void solo_motion_config(struct solo_dev *solo_dev) 23728cae868SHans Verkuil { 23828cae868SHans Verkuil int i; 23928cae868SHans Verkuil 24028cae868SHans Verkuil for (i = 0; i < solo_dev->nr_chans; i++) { 24128cae868SHans Verkuil /* Clear motion flag area */ 24228cae868SHans Verkuil solo_dma_vin_region(solo_dev, i * SOLO_MOT_FLAG_SIZE, 0x0000, 24328cae868SHans Verkuil SOLO_MOT_FLAG_SIZE); 24428cae868SHans Verkuil 24528cae868SHans Verkuil /* Clear working cache table */ 24628cae868SHans Verkuil solo_dma_vin_region(solo_dev, SOLO_MOT_FLAG_AREA + 24728cae868SHans Verkuil (i * SOLO_MOT_THRESH_SIZE * 2) + 24828cae868SHans Verkuil SOLO_MOT_THRESH_SIZE, 0x0000, 24928cae868SHans Verkuil SOLO_MOT_THRESH_SIZE); 25028cae868SHans Verkuil 25128cae868SHans Verkuil /* Set default threshold table */ 25228cae868SHans Verkuil solo_set_motion_threshold(solo_dev, i, SOLO_DEF_MOT_THRESH); 25328cae868SHans Verkuil } 25428cae868SHans Verkuil 25528cae868SHans Verkuil /* Default motion settings */ 25628cae868SHans Verkuil solo_reg_write(solo_dev, SOLO_VI_MOT_ADR, SOLO_VI_MOTION_EN(0) | 25728cae868SHans Verkuil (SOLO_MOTION_EXT_ADDR(solo_dev) >> 16)); 25828cae868SHans Verkuil solo_reg_write(solo_dev, SOLO_VI_MOT_CTRL, 25928cae868SHans Verkuil SOLO_VI_MOTION_FRAME_COUNT(3) | 26028cae868SHans Verkuil SOLO_VI_MOTION_SAMPLE_LENGTH(solo_dev->video_hsize / 16) 26128cae868SHans Verkuil /* | SOLO_VI_MOTION_INTR_START_STOP */ 26228cae868SHans Verkuil | SOLO_VI_MOTION_SAMPLE_COUNT(10)); 26328cae868SHans Verkuil 26428cae868SHans Verkuil solo_reg_write(solo_dev, SOLO_VI_MOTION_BORDER, 0); 26528cae868SHans Verkuil solo_reg_write(solo_dev, SOLO_VI_MOTION_BAR, 0); 26628cae868SHans Verkuil } 26728cae868SHans Verkuil 26828cae868SHans Verkuil int solo_disp_init(struct solo_dev *solo_dev) 26928cae868SHans Verkuil { 27028cae868SHans Verkuil int i; 27128cae868SHans Verkuil 27228cae868SHans Verkuil solo_dev->video_hsize = 704; 27328cae868SHans Verkuil if (solo_dev->video_type == SOLO_VO_FMT_TYPE_NTSC) { 27428cae868SHans Verkuil solo_dev->video_vsize = 240; 27528cae868SHans Verkuil solo_dev->fps = 30; 27628cae868SHans Verkuil } else { 27728cae868SHans Verkuil solo_dev->video_vsize = 288; 27828cae868SHans Verkuil solo_dev->fps = 25; 27928cae868SHans Verkuil } 28028cae868SHans Verkuil 28128cae868SHans Verkuil solo_vin_config(solo_dev); 28228cae868SHans Verkuil solo_motion_config(solo_dev); 28328cae868SHans Verkuil solo_vout_config(solo_dev); 28428cae868SHans Verkuil 28528cae868SHans Verkuil for (i = 0; i < solo_dev->nr_chans; i++) 28628cae868SHans Verkuil solo_reg_write(solo_dev, SOLO_VI_WIN_ON(i), 1); 28728cae868SHans Verkuil 28828cae868SHans Verkuil return 0; 28928cae868SHans Verkuil } 29028cae868SHans Verkuil 29128cae868SHans Verkuil void solo_disp_exit(struct solo_dev *solo_dev) 29228cae868SHans Verkuil { 29328cae868SHans Verkuil int i; 29428cae868SHans Verkuil 29528cae868SHans Verkuil solo_reg_write(solo_dev, SOLO_VO_DISP_CTRL, 0); 29628cae868SHans Verkuil solo_reg_write(solo_dev, SOLO_VO_ZOOM_CTRL, 0); 29728cae868SHans Verkuil solo_reg_write(solo_dev, SOLO_VO_FREEZE_CTRL, 0); 29828cae868SHans Verkuil 29928cae868SHans Verkuil for (i = 0; i < solo_dev->nr_chans; i++) { 30028cae868SHans Verkuil solo_reg_write(solo_dev, SOLO_VI_WIN_CTRL0(i), 0); 30128cae868SHans Verkuil solo_reg_write(solo_dev, SOLO_VI_WIN_CTRL1(i), 0); 30228cae868SHans Verkuil solo_reg_write(solo_dev, SOLO_VI_WIN_ON(i), 0); 30328cae868SHans Verkuil } 30428cae868SHans Verkuil 30528cae868SHans Verkuil /* Set default border */ 30628cae868SHans Verkuil for (i = 0; i < 5; i++) 30728cae868SHans Verkuil solo_reg_write(solo_dev, SOLO_VO_BORDER_X(i), 0); 30828cae868SHans Verkuil 30928cae868SHans Verkuil for (i = 0; i < 5; i++) 31028cae868SHans Verkuil solo_reg_write(solo_dev, SOLO_VO_BORDER_Y(i), 0); 31128cae868SHans Verkuil 31228cae868SHans Verkuil solo_reg_write(solo_dev, SOLO_VO_BORDER_LINE_MASK, 0); 31328cae868SHans Verkuil solo_reg_write(solo_dev, SOLO_VO_BORDER_FILL_MASK, 0); 31428cae868SHans Verkuil 31528cae868SHans Verkuil solo_reg_write(solo_dev, SOLO_VO_RECTANGLE_CTRL(0), 0); 31628cae868SHans Verkuil solo_reg_write(solo_dev, SOLO_VO_RECTANGLE_START(0), 0); 31728cae868SHans Verkuil solo_reg_write(solo_dev, SOLO_VO_RECTANGLE_STOP(0), 0); 31828cae868SHans Verkuil 31928cae868SHans Verkuil solo_reg_write(solo_dev, SOLO_VO_RECTANGLE_CTRL(1), 0); 32028cae868SHans Verkuil solo_reg_write(solo_dev, SOLO_VO_RECTANGLE_START(1), 0); 32128cae868SHans Verkuil solo_reg_write(solo_dev, SOLO_VO_RECTANGLE_STOP(1), 0); 32228cae868SHans Verkuil } 323