314 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * Copyright (C) 2010-2013 Bluecherry, LLC <https://www.bluecherrydvr.com>4 *5 * Original author:6 * Ben Collins <bcollins@ubuntu.com>7 *8 * Additional work by:9 * John Brooks <john.brooks@bluecherry.net>10 */11 12#include <linux/kernel.h>13#include <linux/module.h>14#include <linux/videodev2.h>15#include <media/v4l2-ioctl.h>16 17#include "solo6x10.h"18 19#define SOLO_VCLK_DELAY 320#define SOLO_PROGRESSIVE_VSIZE 102421 22#define SOLO_MOT_THRESH_W 6423#define SOLO_MOT_THRESH_H 6424#define SOLO_MOT_THRESH_SIZE 819225#define SOLO_MOT_THRESH_REAL (SOLO_MOT_THRESH_W * SOLO_MOT_THRESH_H)26#define SOLO_MOT_FLAG_SIZE 102427#define SOLO_MOT_FLAG_AREA (SOLO_MOT_FLAG_SIZE * 16)28 29static void solo_vin_config(struct solo_dev *solo_dev)30{31 solo_dev->vin_hstart = 8;32 solo_dev->vin_vstart = 2;33 34 solo_reg_write(solo_dev, SOLO_SYS_VCLK,35 SOLO_VCLK_SELECT(2) |36 SOLO_VCLK_VIN1415_DELAY(SOLO_VCLK_DELAY) |37 SOLO_VCLK_VIN1213_DELAY(SOLO_VCLK_DELAY) |38 SOLO_VCLK_VIN1011_DELAY(SOLO_VCLK_DELAY) |39 SOLO_VCLK_VIN0809_DELAY(SOLO_VCLK_DELAY) |40 SOLO_VCLK_VIN0607_DELAY(SOLO_VCLK_DELAY) |41 SOLO_VCLK_VIN0405_DELAY(SOLO_VCLK_DELAY) |42 SOLO_VCLK_VIN0203_DELAY(SOLO_VCLK_DELAY) |43 SOLO_VCLK_VIN0001_DELAY(SOLO_VCLK_DELAY));44 45 solo_reg_write(solo_dev, SOLO_VI_ACT_I_P,46 SOLO_VI_H_START(solo_dev->vin_hstart) |47 SOLO_VI_V_START(solo_dev->vin_vstart) |48 SOLO_VI_V_STOP(solo_dev->vin_vstart +49 solo_dev->video_vsize));50 51 solo_reg_write(solo_dev, SOLO_VI_ACT_I_S,52 SOLO_VI_H_START(solo_dev->vout_hstart) |53 SOLO_VI_V_START(solo_dev->vout_vstart) |54 SOLO_VI_V_STOP(solo_dev->vout_vstart +55 solo_dev->video_vsize));56 57 solo_reg_write(solo_dev, SOLO_VI_ACT_P,58 SOLO_VI_H_START(0) |59 SOLO_VI_V_START(1) |60 SOLO_VI_V_STOP(SOLO_PROGRESSIVE_VSIZE));61 62 solo_reg_write(solo_dev, SOLO_VI_CH_FORMAT,63 SOLO_VI_FD_SEL_MASK(0) | SOLO_VI_PROG_MASK(0));64 65 /* On 6110, initialize mozaic darkness strength */66 if (solo_dev->type == SOLO_DEV_6010)67 solo_reg_write(solo_dev, SOLO_VI_FMT_CFG, 0);68 else69 solo_reg_write(solo_dev, SOLO_VI_FMT_CFG, 16 << 22);70 71 solo_reg_write(solo_dev, SOLO_VI_PAGE_SW, 2);72 73 if (solo_dev->video_type == SOLO_VO_FMT_TYPE_NTSC) {74 solo_reg_write(solo_dev, SOLO_VI_PB_CONFIG,75 SOLO_VI_PB_USER_MODE);76 solo_reg_write(solo_dev, SOLO_VI_PB_RANGE_HV,77 SOLO_VI_PB_HSIZE(858) | SOLO_VI_PB_VSIZE(246));78 solo_reg_write(solo_dev, SOLO_VI_PB_ACT_V,79 SOLO_VI_PB_VSTART(4) |80 SOLO_VI_PB_VSTOP(4 + 240));81 } else {82 solo_reg_write(solo_dev, SOLO_VI_PB_CONFIG,83 SOLO_VI_PB_USER_MODE | SOLO_VI_PB_PAL);84 solo_reg_write(solo_dev, SOLO_VI_PB_RANGE_HV,85 SOLO_VI_PB_HSIZE(864) | SOLO_VI_PB_VSIZE(294));86 solo_reg_write(solo_dev, SOLO_VI_PB_ACT_V,87 SOLO_VI_PB_VSTART(4) |88 SOLO_VI_PB_VSTOP(4 + 288));89 }90 solo_reg_write(solo_dev, SOLO_VI_PB_ACT_H, SOLO_VI_PB_HSTART(16) |91 SOLO_VI_PB_HSTOP(16 + 720));92}93 94static void solo_vout_config_cursor(struct solo_dev *dev)95{96 int i;97 98 /* Load (blank) cursor bitmap mask (2bpp) */99 for (i = 0; i < 20; i++)100 solo_reg_write(dev, SOLO_VO_CURSOR_MASK(i), 0);101 102 solo_reg_write(dev, SOLO_VO_CURSOR_POS, 0);103 104 solo_reg_write(dev, SOLO_VO_CURSOR_CLR,105 (0x80 << 24) | (0x80 << 16) | (0x10 << 8) | 0x80);106 solo_reg_write(dev, SOLO_VO_CURSOR_CLR2, (0xe0 << 8) | 0x80);107}108 109static void solo_vout_config(struct solo_dev *solo_dev)110{111 solo_dev->vout_hstart = 6;112 solo_dev->vout_vstart = 8;113 114 solo_reg_write(solo_dev, SOLO_VO_FMT_ENC,115 solo_dev->video_type |116 SOLO_VO_USER_COLOR_SET_NAV |117 SOLO_VO_USER_COLOR_SET_NAH |118 SOLO_VO_NA_COLOR_Y(0) |119 SOLO_VO_NA_COLOR_CB(0) |120 SOLO_VO_NA_COLOR_CR(0));121 122 solo_reg_write(solo_dev, SOLO_VO_ACT_H,123 SOLO_VO_H_START(solo_dev->vout_hstart) |124 SOLO_VO_H_STOP(solo_dev->vout_hstart +125 solo_dev->video_hsize));126 127 solo_reg_write(solo_dev, SOLO_VO_ACT_V,128 SOLO_VO_V_START(solo_dev->vout_vstart) |129 SOLO_VO_V_STOP(solo_dev->vout_vstart +130 solo_dev->video_vsize));131 132 solo_reg_write(solo_dev, SOLO_VO_RANGE_HV,133 SOLO_VO_H_LEN(solo_dev->video_hsize) |134 SOLO_VO_V_LEN(solo_dev->video_vsize));135 136 /* Border & background colors */137 solo_reg_write(solo_dev, SOLO_VO_BORDER_LINE_COLOR,138 (0xa0 << 24) | (0x88 << 16) | (0xa0 << 8) | 0x88);139 solo_reg_write(solo_dev, SOLO_VO_BORDER_FILL_COLOR,140 (0x10 << 24) | (0x8f << 16) | (0x10 << 8) | 0x8f);141 solo_reg_write(solo_dev, SOLO_VO_BKG_COLOR,142 (16 << 24) | (128 << 16) | (16 << 8) | 128);143 144 solo_reg_write(solo_dev, SOLO_VO_DISP_ERASE, SOLO_VO_DISP_ERASE_ON);145 146 solo_reg_write(solo_dev, SOLO_VI_WIN_SW, 0);147 148 solo_reg_write(solo_dev, SOLO_VO_ZOOM_CTRL, 0);149 solo_reg_write(solo_dev, SOLO_VO_FREEZE_CTRL, 0);150 151 solo_reg_write(solo_dev, SOLO_VO_DISP_CTRL, SOLO_VO_DISP_ON |152 SOLO_VO_DISP_ERASE_COUNT(8) |153 SOLO_VO_DISP_BASE(SOLO_DISP_EXT_ADDR));154 155 156 solo_vout_config_cursor(solo_dev);157 158 /* Enable channels we support */159 solo_reg_write(solo_dev, SOLO_VI_CH_ENA,160 (1 << solo_dev->nr_chans) - 1);161}162 163static int solo_dma_vin_region(struct solo_dev *solo_dev, u32 off,164 u16 val, int reg_size)165{166 __le16 *buf;167 const int n = 64, size = n * sizeof(*buf);168 int i, ret = 0;169 170 buf = kmalloc(size, GFP_KERNEL);171 if (!buf)172 return -ENOMEM;173 174 for (i = 0; i < n; i++)175 buf[i] = cpu_to_le16(val);176 177 for (i = 0; i < reg_size; i += size) {178 ret = solo_p2m_dma(solo_dev, 1, buf,179 SOLO_MOTION_EXT_ADDR(solo_dev) + off + i,180 size, 0, 0);181 182 if (ret)183 break;184 }185 186 kfree(buf);187 return ret;188}189 190int solo_set_motion_threshold(struct solo_dev *solo_dev, u8 ch, u16 val)191{192 if (ch > solo_dev->nr_chans)193 return -EINVAL;194 195 return solo_dma_vin_region(solo_dev, SOLO_MOT_FLAG_AREA +196 (ch * SOLO_MOT_THRESH_SIZE * 2),197 val, SOLO_MOT_THRESH_SIZE);198}199 200int solo_set_motion_block(struct solo_dev *solo_dev, u8 ch,201 const u16 *thresholds)202{203 const unsigned size = sizeof(u16) * 64;204 u32 off = SOLO_MOT_FLAG_AREA + ch * SOLO_MOT_THRESH_SIZE * 2;205 __le16 *buf;206 int x, y;207 int ret = 0;208 209 buf = kzalloc(size, GFP_KERNEL);210 if (buf == NULL)211 return -ENOMEM;212 for (y = 0; y < SOLO_MOTION_SZ; y++) {213 for (x = 0; x < SOLO_MOTION_SZ; x++)214 buf[x] = cpu_to_le16(thresholds[y * SOLO_MOTION_SZ + x]);215 ret |= solo_p2m_dma(solo_dev, 1, buf,216 SOLO_MOTION_EXT_ADDR(solo_dev) + off + y * size,217 size, 0, 0);218 }219 kfree(buf);220 return ret;221}222 223/* First 8k is motion flag (512 bytes * 16). Following that is an 8k+8k224 * threshold and working table for each channel. At least that's what the225 * spec says. However, this code (taken from rdk) has some mystery 8k226 * block right after the flag area, before the first thresh table. */227static void solo_motion_config(struct solo_dev *solo_dev)228{229 int i;230 231 for (i = 0; i < solo_dev->nr_chans; i++) {232 /* Clear motion flag area */233 solo_dma_vin_region(solo_dev, i * SOLO_MOT_FLAG_SIZE, 0x0000,234 SOLO_MOT_FLAG_SIZE);235 236 /* Clear working cache table */237 solo_dma_vin_region(solo_dev, SOLO_MOT_FLAG_AREA +238 (i * SOLO_MOT_THRESH_SIZE * 2) +239 SOLO_MOT_THRESH_SIZE, 0x0000,240 SOLO_MOT_THRESH_SIZE);241 242 /* Set default threshold table */243 solo_set_motion_threshold(solo_dev, i, SOLO_DEF_MOT_THRESH);244 }245 246 /* Default motion settings */247 solo_reg_write(solo_dev, SOLO_VI_MOT_ADR, SOLO_VI_MOTION_EN(0) |248 (SOLO_MOTION_EXT_ADDR(solo_dev) >> 16));249 solo_reg_write(solo_dev, SOLO_VI_MOT_CTRL,250 SOLO_VI_MOTION_FRAME_COUNT(3) |251 SOLO_VI_MOTION_SAMPLE_LENGTH(solo_dev->video_hsize / 16)252 /* | SOLO_VI_MOTION_INTR_START_STOP */253 | SOLO_VI_MOTION_SAMPLE_COUNT(10));254 255 solo_reg_write(solo_dev, SOLO_VI_MOTION_BORDER, 0);256 solo_reg_write(solo_dev, SOLO_VI_MOTION_BAR, 0);257}258 259int solo_disp_init(struct solo_dev *solo_dev)260{261 int i;262 263 solo_dev->video_hsize = 704;264 if (solo_dev->video_type == SOLO_VO_FMT_TYPE_NTSC) {265 solo_dev->video_vsize = 240;266 solo_dev->fps = 30;267 } else {268 solo_dev->video_vsize = 288;269 solo_dev->fps = 25;270 }271 272 solo_vin_config(solo_dev);273 solo_motion_config(solo_dev);274 solo_vout_config(solo_dev);275 276 for (i = 0; i < solo_dev->nr_chans; i++)277 solo_reg_write(solo_dev, SOLO_VI_WIN_ON(i), 1);278 279 return 0;280}281 282void solo_disp_exit(struct solo_dev *solo_dev)283{284 int i;285 286 solo_reg_write(solo_dev, SOLO_VO_DISP_CTRL, 0);287 solo_reg_write(solo_dev, SOLO_VO_ZOOM_CTRL, 0);288 solo_reg_write(solo_dev, SOLO_VO_FREEZE_CTRL, 0);289 290 for (i = 0; i < solo_dev->nr_chans; i++) {291 solo_reg_write(solo_dev, SOLO_VI_WIN_CTRL0(i), 0);292 solo_reg_write(solo_dev, SOLO_VI_WIN_CTRL1(i), 0);293 solo_reg_write(solo_dev, SOLO_VI_WIN_ON(i), 0);294 }295 296 /* Set default border */297 for (i = 0; i < 5; i++)298 solo_reg_write(solo_dev, SOLO_VO_BORDER_X(i), 0);299 300 for (i = 0; i < 5; i++)301 solo_reg_write(solo_dev, SOLO_VO_BORDER_Y(i), 0);302 303 solo_reg_write(solo_dev, SOLO_VO_BORDER_LINE_MASK, 0);304 solo_reg_write(solo_dev, SOLO_VO_BORDER_FILL_MASK, 0);305 306 solo_reg_write(solo_dev, SOLO_VO_RECTANGLE_CTRL(0), 0);307 solo_reg_write(solo_dev, SOLO_VO_RECTANGLE_START(0), 0);308 solo_reg_write(solo_dev, SOLO_VO_RECTANGLE_STOP(0), 0);309 310 solo_reg_write(solo_dev, SOLO_VO_RECTANGLE_CTRL(1), 0);311 solo_reg_write(solo_dev, SOLO_VO_RECTANGLE_START(1), 0);312 solo_reg_write(solo_dev, SOLO_VO_RECTANGLE_STOP(1), 0);313}314