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