162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * TW5864 driver - video encoding functions 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2016 Bluecherry, LLC <maintainers@bluecherrydvr.com> 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/module.h> 962306a36Sopenharmony_ci#include <media/v4l2-common.h> 1062306a36Sopenharmony_ci#include <media/v4l2-event.h> 1162306a36Sopenharmony_ci#include <media/videobuf2-dma-contig.h> 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#include "tw5864.h" 1462306a36Sopenharmony_ci#include "tw5864-reg.h" 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#define QUANTIZATION_TABLE_LEN 96 1762306a36Sopenharmony_ci#define VLC_LOOKUP_TABLE_LEN 1024 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_cistatic const u16 forward_quantization_table[QUANTIZATION_TABLE_LEN] = { 2062306a36Sopenharmony_ci 0x3333, 0x1f82, 0x3333, 0x1f82, 0x1f82, 0x147b, 0x1f82, 0x147b, 2162306a36Sopenharmony_ci 0x3333, 0x1f82, 0x3333, 0x1f82, 0x1f82, 0x147b, 0x1f82, 0x147b, 2262306a36Sopenharmony_ci 0x2e8c, 0x1d42, 0x2e8c, 0x1d42, 0x1d42, 0x1234, 0x1d42, 0x1234, 2362306a36Sopenharmony_ci 0x2e8c, 0x1d42, 0x2e8c, 0x1d42, 0x1d42, 0x1234, 0x1d42, 0x1234, 2462306a36Sopenharmony_ci 0x2762, 0x199a, 0x2762, 0x199a, 0x199a, 0x1062, 0x199a, 0x1062, 2562306a36Sopenharmony_ci 0x2762, 0x199a, 0x2762, 0x199a, 0x199a, 0x1062, 0x199a, 0x1062, 2662306a36Sopenharmony_ci 0x2492, 0x16c1, 0x2492, 0x16c1, 0x16c1, 0x0e3f, 0x16c1, 0x0e3f, 2762306a36Sopenharmony_ci 0x2492, 0x16c1, 0x2492, 0x16c1, 0x16c1, 0x0e3f, 0x16c1, 0x0e3f, 2862306a36Sopenharmony_ci 0x2000, 0x147b, 0x2000, 0x147b, 0x147b, 0x0d1b, 0x147b, 0x0d1b, 2962306a36Sopenharmony_ci 0x2000, 0x147b, 0x2000, 0x147b, 0x147b, 0x0d1b, 0x147b, 0x0d1b, 3062306a36Sopenharmony_ci 0x1c72, 0x11cf, 0x1c72, 0x11cf, 0x11cf, 0x0b4d, 0x11cf, 0x0b4d, 3162306a36Sopenharmony_ci 0x1c72, 0x11cf, 0x1c72, 0x11cf, 0x11cf, 0x0b4d, 0x11cf, 0x0b4d 3262306a36Sopenharmony_ci}; 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_cistatic const u16 inverse_quantization_table[QUANTIZATION_TABLE_LEN] = { 3562306a36Sopenharmony_ci 0x800a, 0x800d, 0x800a, 0x800d, 0x800d, 0x8010, 0x800d, 0x8010, 3662306a36Sopenharmony_ci 0x800a, 0x800d, 0x800a, 0x800d, 0x800d, 0x8010, 0x800d, 0x8010, 3762306a36Sopenharmony_ci 0x800b, 0x800e, 0x800b, 0x800e, 0x800e, 0x8012, 0x800e, 0x8012, 3862306a36Sopenharmony_ci 0x800b, 0x800e, 0x800b, 0x800e, 0x800e, 0x8012, 0x800e, 0x8012, 3962306a36Sopenharmony_ci 0x800d, 0x8010, 0x800d, 0x8010, 0x8010, 0x8014, 0x8010, 0x8014, 4062306a36Sopenharmony_ci 0x800d, 0x8010, 0x800d, 0x8010, 0x8010, 0x8014, 0x8010, 0x8014, 4162306a36Sopenharmony_ci 0x800e, 0x8012, 0x800e, 0x8012, 0x8012, 0x8017, 0x8012, 0x8017, 4262306a36Sopenharmony_ci 0x800e, 0x8012, 0x800e, 0x8012, 0x8012, 0x8017, 0x8012, 0x8017, 4362306a36Sopenharmony_ci 0x8010, 0x8014, 0x8010, 0x8014, 0x8014, 0x8019, 0x8014, 0x8019, 4462306a36Sopenharmony_ci 0x8010, 0x8014, 0x8010, 0x8014, 0x8014, 0x8019, 0x8014, 0x8019, 4562306a36Sopenharmony_ci 0x8012, 0x8017, 0x8012, 0x8017, 0x8017, 0x801d, 0x8017, 0x801d, 4662306a36Sopenharmony_ci 0x8012, 0x8017, 0x8012, 0x8017, 0x8017, 0x801d, 0x8017, 0x801d 4762306a36Sopenharmony_ci}; 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_cistatic const u16 encoder_vlc_lookup_table[VLC_LOOKUP_TABLE_LEN] = { 5062306a36Sopenharmony_ci 0x011, 0x000, 0x000, 0x000, 0x065, 0x021, 0x000, 0x000, 0x087, 0x064, 5162306a36Sopenharmony_ci 0x031, 0x000, 0x097, 0x086, 0x075, 0x053, 0x0a7, 0x096, 0x085, 0x063, 5262306a36Sopenharmony_ci 0x0b7, 0x0a6, 0x095, 0x074, 0x0df, 0x0b6, 0x0a5, 0x084, 0x0db, 0x0de, 5362306a36Sopenharmony_ci 0x0b5, 0x094, 0x0d8, 0x0da, 0x0dd, 0x0a4, 0x0ef, 0x0ee, 0x0d9, 0x0b4, 5462306a36Sopenharmony_ci 0x0eb, 0x0ea, 0x0ed, 0x0dc, 0x0ff, 0x0fe, 0x0e9, 0x0ec, 0x0fb, 0x0fa, 5562306a36Sopenharmony_ci 0x0fd, 0x0e8, 0x10f, 0x0f1, 0x0f9, 0x0fc, 0x10b, 0x10e, 0x10d, 0x0f8, 5662306a36Sopenharmony_ci 0x107, 0x10a, 0x109, 0x10c, 0x104, 0x106, 0x105, 0x108, 0x023, 0x000, 5762306a36Sopenharmony_ci 0x000, 0x000, 0x06b, 0x022, 0x000, 0x000, 0x067, 0x057, 0x033, 0x000, 5862306a36Sopenharmony_ci 0x077, 0x06a, 0x069, 0x045, 0x087, 0x066, 0x065, 0x044, 0x084, 0x076, 5962306a36Sopenharmony_ci 0x075, 0x056, 0x097, 0x086, 0x085, 0x068, 0x0bf, 0x096, 0x095, 0x064, 6062306a36Sopenharmony_ci 0x0bb, 0x0be, 0x0bd, 0x074, 0x0cf, 0x0ba, 0x0b9, 0x094, 0x0cb, 0x0ce, 6162306a36Sopenharmony_ci 0x0cd, 0x0bc, 0x0c8, 0x0ca, 0x0c9, 0x0b8, 0x0df, 0x0de, 0x0dd, 0x0cc, 6262306a36Sopenharmony_ci 0x0db, 0x0da, 0x0d9, 0x0dc, 0x0d7, 0x0eb, 0x0d6, 0x0d8, 0x0e9, 0x0e8, 6362306a36Sopenharmony_ci 0x0ea, 0x0d1, 0x0e7, 0x0e6, 0x0e5, 0x0e4, 0x04f, 0x000, 0x000, 0x000, 6462306a36Sopenharmony_ci 0x06f, 0x04e, 0x000, 0x000, 0x06b, 0x05f, 0x04d, 0x000, 0x068, 0x05c, 6562306a36Sopenharmony_ci 0x05e, 0x04c, 0x07f, 0x05a, 0x05b, 0x04b, 0x07b, 0x058, 0x059, 0x04a, 6662306a36Sopenharmony_ci 0x079, 0x06e, 0x06d, 0x049, 0x078, 0x06a, 0x069, 0x048, 0x08f, 0x07e, 6762306a36Sopenharmony_ci 0x07d, 0x05d, 0x08b, 0x08e, 0x07a, 0x06c, 0x09f, 0x08a, 0x08d, 0x07c, 6862306a36Sopenharmony_ci 0x09b, 0x09e, 0x089, 0x08c, 0x098, 0x09a, 0x09d, 0x088, 0x0ad, 0x097, 6962306a36Sopenharmony_ci 0x099, 0x09c, 0x0a9, 0x0ac, 0x0ab, 0x0aa, 0x0a5, 0x0a8, 0x0a7, 0x0a6, 7062306a36Sopenharmony_ci 0x0a1, 0x0a4, 0x0a3, 0x0a2, 0x021, 0x000, 0x000, 0x000, 0x067, 0x011, 7162306a36Sopenharmony_ci 0x000, 0x000, 0x064, 0x066, 0x031, 0x000, 0x063, 0x073, 0x072, 0x065, 7262306a36Sopenharmony_ci 0x062, 0x083, 0x082, 0x070, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 7362306a36Sopenharmony_ci 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 7462306a36Sopenharmony_ci 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 7562306a36Sopenharmony_ci 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 7662306a36Sopenharmony_ci 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 7762306a36Sopenharmony_ci 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 7862306a36Sopenharmony_ci 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 7962306a36Sopenharmony_ci 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 8062306a36Sopenharmony_ci 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 8162306a36Sopenharmony_ci 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 8262306a36Sopenharmony_ci 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 8362306a36Sopenharmony_ci 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 8462306a36Sopenharmony_ci 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 8562306a36Sopenharmony_ci 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 8662306a36Sopenharmony_ci 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 8762306a36Sopenharmony_ci 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 8862306a36Sopenharmony_ci 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 8962306a36Sopenharmony_ci 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 9062306a36Sopenharmony_ci 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 9162306a36Sopenharmony_ci 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 9262306a36Sopenharmony_ci 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 9362306a36Sopenharmony_ci 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 9462306a36Sopenharmony_ci 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 9562306a36Sopenharmony_ci 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 9662306a36Sopenharmony_ci 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 9762306a36Sopenharmony_ci 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 9862306a36Sopenharmony_ci 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 9962306a36Sopenharmony_ci 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 10062306a36Sopenharmony_ci 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 10162306a36Sopenharmony_ci 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 10262306a36Sopenharmony_ci 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x011, 0x010, 10362306a36Sopenharmony_ci 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 10462306a36Sopenharmony_ci 0x000, 0x000, 0x000, 0x000, 0x011, 0x021, 0x020, 0x000, 0x000, 0x000, 10562306a36Sopenharmony_ci 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 10662306a36Sopenharmony_ci 0x023, 0x022, 0x021, 0x020, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 10762306a36Sopenharmony_ci 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x023, 0x022, 0x021, 0x031, 10862306a36Sopenharmony_ci 0x030, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 10962306a36Sopenharmony_ci 0x000, 0x000, 0x023, 0x022, 0x033, 0x032, 0x031, 0x030, 0x000, 0x000, 11062306a36Sopenharmony_ci 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x023, 0x030, 11162306a36Sopenharmony_ci 0x031, 0x033, 0x032, 0x035, 0x034, 0x000, 0x000, 0x000, 0x000, 0x000, 11262306a36Sopenharmony_ci 0x000, 0x000, 0x000, 0x000, 0x037, 0x036, 0x035, 0x034, 0x033, 0x032, 11362306a36Sopenharmony_ci 0x031, 0x041, 0x051, 0x061, 0x071, 0x081, 0x091, 0x0a1, 0x0b1, 0x000, 11462306a36Sopenharmony_ci 0x002, 0x000, 0x0e4, 0x011, 0x0f4, 0x002, 0x024, 0x003, 0x005, 0x012, 11562306a36Sopenharmony_ci 0x034, 0x013, 0x065, 0x024, 0x013, 0x063, 0x015, 0x022, 0x075, 0x034, 11662306a36Sopenharmony_ci 0x044, 0x023, 0x023, 0x073, 0x054, 0x033, 0x033, 0x004, 0x043, 0x014, 11762306a36Sopenharmony_ci 0x011, 0x043, 0x014, 0x001, 0x025, 0x015, 0x035, 0x025, 0x064, 0x055, 11862306a36Sopenharmony_ci 0x045, 0x035, 0x074, 0x065, 0x085, 0x0d5, 0x012, 0x095, 0x055, 0x045, 11962306a36Sopenharmony_ci 0x095, 0x0e5, 0x084, 0x075, 0x022, 0x0a5, 0x094, 0x085, 0x032, 0x0b5, 12062306a36Sopenharmony_ci 0x003, 0x0c5, 0x001, 0x044, 0x0a5, 0x032, 0x0b5, 0x094, 0x0c5, 0x0a4, 12162306a36Sopenharmony_ci 0x0a4, 0x054, 0x0d5, 0x0b4, 0x0b4, 0x064, 0x0f5, 0x0f5, 0x053, 0x0d4, 12262306a36Sopenharmony_ci 0x0e5, 0x0c4, 0x105, 0x105, 0x0c4, 0x074, 0x063, 0x0e4, 0x0d4, 0x084, 12362306a36Sopenharmony_ci 0x073, 0x0f4, 0x004, 0x005, 0x000, 0x053, 0x000, 0x000, 0x000, 0x000, 12462306a36Sopenharmony_ci 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 12562306a36Sopenharmony_ci 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 12662306a36Sopenharmony_ci 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 12762306a36Sopenharmony_ci 0x000, 0x000, 0x011, 0x021, 0x031, 0x030, 0x011, 0x021, 0x020, 0x000, 12862306a36Sopenharmony_ci 0x011, 0x010, 0x000, 0x000, 0x011, 0x033, 0x032, 0x043, 0x042, 0x053, 12962306a36Sopenharmony_ci 0x052, 0x063, 0x062, 0x073, 0x072, 0x083, 0x082, 0x093, 0x092, 0x091, 13062306a36Sopenharmony_ci 0x037, 0x036, 0x035, 0x034, 0x033, 0x045, 0x044, 0x043, 0x042, 0x053, 13162306a36Sopenharmony_ci 0x052, 0x063, 0x062, 0x061, 0x060, 0x000, 0x045, 0x037, 0x036, 0x035, 13262306a36Sopenharmony_ci 0x044, 0x043, 0x034, 0x033, 0x042, 0x053, 0x052, 0x061, 0x051, 0x060, 13362306a36Sopenharmony_ci 0x000, 0x000, 0x053, 0x037, 0x045, 0x044, 0x036, 0x035, 0x034, 0x043, 13462306a36Sopenharmony_ci 0x033, 0x042, 0x052, 0x051, 0x050, 0x000, 0x000, 0x000, 0x045, 0x044, 13562306a36Sopenharmony_ci 0x043, 0x037, 0x036, 0x035, 0x034, 0x033, 0x042, 0x051, 0x041, 0x050, 13662306a36Sopenharmony_ci 0x000, 0x000, 0x000, 0x000, 0x061, 0x051, 0x037, 0x036, 0x035, 0x034, 13762306a36Sopenharmony_ci 0x033, 0x032, 0x041, 0x031, 0x060, 0x000, 0x000, 0x000, 0x000, 0x000, 13862306a36Sopenharmony_ci 0x061, 0x051, 0x035, 0x034, 0x033, 0x023, 0x032, 0x041, 0x031, 0x060, 13962306a36Sopenharmony_ci 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x061, 0x041, 0x051, 0x033, 14062306a36Sopenharmony_ci 0x023, 0x022, 0x032, 0x031, 0x060, 0x000, 0x000, 0x000, 0x000, 0x000, 14162306a36Sopenharmony_ci 0x000, 0x000, 0x061, 0x060, 0x041, 0x023, 0x022, 0x031, 0x021, 0x051, 14262306a36Sopenharmony_ci 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x051, 0x050, 14362306a36Sopenharmony_ci 0x031, 0x023, 0x022, 0x021, 0x041, 0x000, 0x000, 0x000, 0x000, 0x000, 14462306a36Sopenharmony_ci 0x000, 0x000, 0x000, 0x000, 0x040, 0x041, 0x031, 0x032, 0x011, 0x033, 14562306a36Sopenharmony_ci 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 14662306a36Sopenharmony_ci 0x040, 0x041, 0x021, 0x011, 0x031, 0x000, 0x000, 0x000, 0x000, 0x000, 14762306a36Sopenharmony_ci 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x030, 0x031, 0x011, 0x021, 14862306a36Sopenharmony_ci 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 14962306a36Sopenharmony_ci 0x000, 0x000, 0x020, 0x021, 0x011, 0x000, 0x000, 0x000, 0x000, 0x000, 15062306a36Sopenharmony_ci 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x010, 0x011, 15162306a36Sopenharmony_ci 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 15262306a36Sopenharmony_ci 0x000, 0x000, 0x000, 0x000 15362306a36Sopenharmony_ci}; 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_cistatic const unsigned int lambda_lookup_table[] = { 15662306a36Sopenharmony_ci 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 15762306a36Sopenharmony_ci 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 15862306a36Sopenharmony_ci 0x0040, 0x0040, 0x0040, 0x0040, 0x0060, 0x0060, 0x0060, 0x0080, 15962306a36Sopenharmony_ci 0x0080, 0x0080, 0x00a0, 0x00c0, 0x00c0, 0x00e0, 0x0100, 0x0120, 16062306a36Sopenharmony_ci 0x0140, 0x0160, 0x01a0, 0x01c0, 0x0200, 0x0240, 0x0280, 0x02e0, 16162306a36Sopenharmony_ci 0x0320, 0x03a0, 0x0400, 0x0480, 0x0500, 0x05a0, 0x0660, 0x0720, 16262306a36Sopenharmony_ci 0x0800, 0x0900, 0x0a20, 0x0b60 16362306a36Sopenharmony_ci}; 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_cistatic const unsigned int intra4x4_lambda3[] = { 16662306a36Sopenharmony_ci 1, 1, 1, 1, 1, 1, 1, 1, 16762306a36Sopenharmony_ci 1, 1, 1, 1, 1, 1, 1, 1, 16862306a36Sopenharmony_ci 2, 2, 2, 2, 3, 3, 3, 4, 16962306a36Sopenharmony_ci 4, 4, 5, 6, 6, 7, 8, 9, 17062306a36Sopenharmony_ci 10, 11, 13, 14, 16, 18, 20, 23, 17162306a36Sopenharmony_ci 25, 29, 32, 36, 40, 45, 51, 57, 17262306a36Sopenharmony_ci 64, 72, 81, 91 17362306a36Sopenharmony_ci}; 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_cistatic v4l2_std_id tw5864_get_v4l2_std(enum tw5864_vid_std std); 17662306a36Sopenharmony_cistatic enum tw5864_vid_std tw5864_from_v4l2_std(v4l2_std_id v4l2_std); 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_cistatic void tw5864_handle_frame_task(struct tasklet_struct *t); 17962306a36Sopenharmony_cistatic void tw5864_handle_frame(struct tw5864_h264_frame *frame); 18062306a36Sopenharmony_cistatic void tw5864_frame_interval_set(struct tw5864_input *input); 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_cistatic int tw5864_queue_setup(struct vb2_queue *q, unsigned int *num_buffers, 18362306a36Sopenharmony_ci unsigned int *num_planes, unsigned int sizes[], 18462306a36Sopenharmony_ci struct device *alloc_ctxs[]) 18562306a36Sopenharmony_ci{ 18662306a36Sopenharmony_ci if (*num_planes) 18762306a36Sopenharmony_ci return sizes[0] < H264_VLC_BUF_SIZE ? -EINVAL : 0; 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci sizes[0] = H264_VLC_BUF_SIZE; 19062306a36Sopenharmony_ci *num_planes = 1; 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci return 0; 19362306a36Sopenharmony_ci} 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_cistatic void tw5864_buf_queue(struct vb2_buffer *vb) 19662306a36Sopenharmony_ci{ 19762306a36Sopenharmony_ci struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); 19862306a36Sopenharmony_ci struct vb2_queue *vq = vb->vb2_queue; 19962306a36Sopenharmony_ci struct tw5864_input *dev = vb2_get_drv_priv(vq); 20062306a36Sopenharmony_ci struct tw5864_buf *buf = container_of(vbuf, struct tw5864_buf, vb); 20162306a36Sopenharmony_ci unsigned long flags; 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci spin_lock_irqsave(&dev->slock, flags); 20462306a36Sopenharmony_ci list_add_tail(&buf->list, &dev->active); 20562306a36Sopenharmony_ci spin_unlock_irqrestore(&dev->slock, flags); 20662306a36Sopenharmony_ci} 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_cistatic int tw5864_input_std_get(struct tw5864_input *input, 20962306a36Sopenharmony_ci enum tw5864_vid_std *std) 21062306a36Sopenharmony_ci{ 21162306a36Sopenharmony_ci struct tw5864_dev *dev = input->root; 21262306a36Sopenharmony_ci u8 std_reg = tw_indir_readb(TW5864_INDIR_VIN_E(input->nr)); 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci *std = (std_reg & 0x70) >> 4; 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci if (std_reg & 0x80) { 21762306a36Sopenharmony_ci dev_dbg(&dev->pci->dev, 21862306a36Sopenharmony_ci "Video format detection is in progress, please wait\n"); 21962306a36Sopenharmony_ci return -EAGAIN; 22062306a36Sopenharmony_ci } 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci return 0; 22362306a36Sopenharmony_ci} 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_cistatic int tw5864_enable_input(struct tw5864_input *input) 22662306a36Sopenharmony_ci{ 22762306a36Sopenharmony_ci struct tw5864_dev *dev = input->root; 22862306a36Sopenharmony_ci int nr = input->nr; 22962306a36Sopenharmony_ci unsigned long flags; 23062306a36Sopenharmony_ci int d1_width = 720; 23162306a36Sopenharmony_ci int d1_height; 23262306a36Sopenharmony_ci int frame_width_bus_value = 0; 23362306a36Sopenharmony_ci int frame_height_bus_value = 0; 23462306a36Sopenharmony_ci int reg_frame_bus = 0x1c; 23562306a36Sopenharmony_ci int fmt_reg_value = 0; 23662306a36Sopenharmony_ci int downscale_enabled = 0; 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci dev_dbg(&dev->pci->dev, "Enabling channel %d\n", nr); 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci input->frame_seqno = 0; 24162306a36Sopenharmony_ci input->frame_gop_seqno = 0; 24262306a36Sopenharmony_ci input->h264_idr_pic_id = 0; 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci input->reg_dsp_qp = input->qp; 24562306a36Sopenharmony_ci input->reg_dsp_ref_mvp_lambda = lambda_lookup_table[input->qp]; 24662306a36Sopenharmony_ci input->reg_dsp_i4x4_weight = intra4x4_lambda3[input->qp]; 24762306a36Sopenharmony_ci input->reg_emu = TW5864_EMU_EN_LPF | TW5864_EMU_EN_BHOST 24862306a36Sopenharmony_ci | TW5864_EMU_EN_SEN | TW5864_EMU_EN_ME | TW5864_EMU_EN_DDR; 24962306a36Sopenharmony_ci input->reg_dsp = nr /* channel id */ 25062306a36Sopenharmony_ci | TW5864_DSP_CHROM_SW 25162306a36Sopenharmony_ci | ((0xa << 8) & TW5864_DSP_MB_DELAY) 25262306a36Sopenharmony_ci ; 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci input->resolution = D1; 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci d1_height = (input->std == STD_NTSC) ? 480 : 576; 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci input->width = d1_width; 25962306a36Sopenharmony_ci input->height = d1_height; 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci input->reg_interlacing = 0x4; 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci switch (input->resolution) { 26462306a36Sopenharmony_ci case D1: 26562306a36Sopenharmony_ci frame_width_bus_value = 0x2cf; 26662306a36Sopenharmony_ci frame_height_bus_value = input->height - 1; 26762306a36Sopenharmony_ci reg_frame_bus = 0x1c; 26862306a36Sopenharmony_ci fmt_reg_value = 0; 26962306a36Sopenharmony_ci downscale_enabled = 0; 27062306a36Sopenharmony_ci input->reg_dsp_codec |= TW5864_CIF_MAP_MD | TW5864_HD1_MAP_MD; 27162306a36Sopenharmony_ci input->reg_emu |= TW5864_DSP_FRAME_TYPE_D1; 27262306a36Sopenharmony_ci input->reg_interlacing = TW5864_DI_EN | TW5864_DSP_INTER_ST; 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci tw_setl(TW5864_FULL_HALF_FLAG, 1 << nr); 27562306a36Sopenharmony_ci break; 27662306a36Sopenharmony_ci case HD1: 27762306a36Sopenharmony_ci input->height /= 2; 27862306a36Sopenharmony_ci input->width /= 2; 27962306a36Sopenharmony_ci frame_width_bus_value = 0x2cf; 28062306a36Sopenharmony_ci frame_height_bus_value = input->height * 2 - 1; 28162306a36Sopenharmony_ci reg_frame_bus = 0x1c; 28262306a36Sopenharmony_ci fmt_reg_value = 0; 28362306a36Sopenharmony_ci downscale_enabled = 0; 28462306a36Sopenharmony_ci input->reg_dsp_codec |= TW5864_HD1_MAP_MD; 28562306a36Sopenharmony_ci input->reg_emu |= TW5864_DSP_FRAME_TYPE_D1; 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci tw_clearl(TW5864_FULL_HALF_FLAG, 1 << nr); 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci break; 29062306a36Sopenharmony_ci case CIF: 29162306a36Sopenharmony_ci input->height /= 4; 29262306a36Sopenharmony_ci input->width /= 2; 29362306a36Sopenharmony_ci frame_width_bus_value = 0x15f; 29462306a36Sopenharmony_ci frame_height_bus_value = input->height * 2 - 1; 29562306a36Sopenharmony_ci reg_frame_bus = 0x07; 29662306a36Sopenharmony_ci fmt_reg_value = 1; 29762306a36Sopenharmony_ci downscale_enabled = 1; 29862306a36Sopenharmony_ci input->reg_dsp_codec |= TW5864_CIF_MAP_MD; 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci tw_clearl(TW5864_FULL_HALF_FLAG, 1 << nr); 30162306a36Sopenharmony_ci break; 30262306a36Sopenharmony_ci case QCIF: 30362306a36Sopenharmony_ci input->height /= 4; 30462306a36Sopenharmony_ci input->width /= 4; 30562306a36Sopenharmony_ci frame_width_bus_value = 0x15f; 30662306a36Sopenharmony_ci frame_height_bus_value = input->height * 2 - 1; 30762306a36Sopenharmony_ci reg_frame_bus = 0x07; 30862306a36Sopenharmony_ci fmt_reg_value = 1; 30962306a36Sopenharmony_ci downscale_enabled = 1; 31062306a36Sopenharmony_ci input->reg_dsp_codec |= TW5864_CIF_MAP_MD; 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci tw_clearl(TW5864_FULL_HALF_FLAG, 1 << nr); 31362306a36Sopenharmony_ci break; 31462306a36Sopenharmony_ci } 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci /* analog input width / 4 */ 31762306a36Sopenharmony_ci tw_indir_writeb(TW5864_INDIR_IN_PIC_WIDTH(nr), d1_width / 4); 31862306a36Sopenharmony_ci tw_indir_writeb(TW5864_INDIR_IN_PIC_HEIGHT(nr), d1_height / 4); 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci /* output width / 4 */ 32162306a36Sopenharmony_ci tw_indir_writeb(TW5864_INDIR_OUT_PIC_WIDTH(nr), input->width / 4); 32262306a36Sopenharmony_ci tw_indir_writeb(TW5864_INDIR_OUT_PIC_HEIGHT(nr), input->height / 4); 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci /* 32562306a36Sopenharmony_ci * Crop width from 720 to 704. 32662306a36Sopenharmony_ci * Above register settings need value 720 involved. 32762306a36Sopenharmony_ci */ 32862306a36Sopenharmony_ci input->width = 704; 32962306a36Sopenharmony_ci tw_indir_writeb(TW5864_INDIR_CROP_ETC, 33062306a36Sopenharmony_ci tw_indir_readb(TW5864_INDIR_CROP_ETC) | 33162306a36Sopenharmony_ci TW5864_INDIR_CROP_ETC_CROP_EN); 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci tw_writel(TW5864_DSP_PIC_MAX_MB, 33462306a36Sopenharmony_ci ((input->width / 16) << 8) | (input->height / 16)); 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci tw_writel(TW5864_FRAME_WIDTH_BUS_A(nr), 33762306a36Sopenharmony_ci frame_width_bus_value); 33862306a36Sopenharmony_ci tw_writel(TW5864_FRAME_WIDTH_BUS_B(nr), 33962306a36Sopenharmony_ci frame_width_bus_value); 34062306a36Sopenharmony_ci tw_writel(TW5864_FRAME_HEIGHT_BUS_A(nr), 34162306a36Sopenharmony_ci frame_height_bus_value); 34262306a36Sopenharmony_ci tw_writel(TW5864_FRAME_HEIGHT_BUS_B(nr), 34362306a36Sopenharmony_ci (frame_height_bus_value + 1) / 2 - 1); 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci tw5864_frame_interval_set(input); 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci if (downscale_enabled) 34862306a36Sopenharmony_ci tw_setl(TW5864_H264EN_CH_DNS, 1 << nr); 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci tw_mask_shift_writel(TW5864_H264EN_CH_FMT_REG1, 0x3, 2 * nr, 35162306a36Sopenharmony_ci fmt_reg_value); 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci tw_mask_shift_writel((nr < 2 35462306a36Sopenharmony_ci ? TW5864_H264EN_RATE_MAX_LINE_REG1 35562306a36Sopenharmony_ci : TW5864_H264EN_RATE_MAX_LINE_REG2), 35662306a36Sopenharmony_ci 0x1f, 5 * (nr % 2), 35762306a36Sopenharmony_ci input->std == STD_NTSC ? 29 : 24); 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci tw_mask_shift_writel((nr < 2) ? TW5864_FRAME_BUS1 : 36062306a36Sopenharmony_ci TW5864_FRAME_BUS2, 0xff, (nr % 2) * 8, 36162306a36Sopenharmony_ci reg_frame_bus); 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci spin_lock_irqsave(&dev->slock, flags); 36462306a36Sopenharmony_ci input->enabled = 1; 36562306a36Sopenharmony_ci spin_unlock_irqrestore(&dev->slock, flags); 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci return 0; 36862306a36Sopenharmony_ci} 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_civoid tw5864_request_encoded_frame(struct tw5864_input *input) 37162306a36Sopenharmony_ci{ 37262306a36Sopenharmony_ci struct tw5864_dev *dev = input->root; 37362306a36Sopenharmony_ci u32 enc_buf_id_new; 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci tw_setl(TW5864_DSP_CODEC, TW5864_CIF_MAP_MD | TW5864_HD1_MAP_MD); 37662306a36Sopenharmony_ci tw_writel(TW5864_EMU, input->reg_emu); 37762306a36Sopenharmony_ci tw_writel(TW5864_INTERLACING, input->reg_interlacing); 37862306a36Sopenharmony_ci tw_writel(TW5864_DSP, input->reg_dsp); 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci tw_writel(TW5864_DSP_QP, input->reg_dsp_qp); 38162306a36Sopenharmony_ci tw_writel(TW5864_DSP_REF_MVP_LAMBDA, input->reg_dsp_ref_mvp_lambda); 38262306a36Sopenharmony_ci tw_writel(TW5864_DSP_I4x4_WEIGHT, input->reg_dsp_i4x4_weight); 38362306a36Sopenharmony_ci tw_mask_shift_writel(TW5864_DSP_INTRA_MODE, TW5864_DSP_INTRA_MODE_MASK, 38462306a36Sopenharmony_ci TW5864_DSP_INTRA_MODE_SHIFT, 38562306a36Sopenharmony_ci TW5864_DSP_INTRA_MODE_16x16); 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci if (input->frame_gop_seqno == 0) { 38862306a36Sopenharmony_ci /* Produce I-frame */ 38962306a36Sopenharmony_ci tw_writel(TW5864_MOTION_SEARCH_ETC, TW5864_INTRA_EN); 39062306a36Sopenharmony_ci input->h264_idr_pic_id++; 39162306a36Sopenharmony_ci input->h264_idr_pic_id &= TW5864_DSP_REF_FRM; 39262306a36Sopenharmony_ci } else { 39362306a36Sopenharmony_ci /* Produce P-frame */ 39462306a36Sopenharmony_ci tw_writel(TW5864_MOTION_SEARCH_ETC, TW5864_INTRA_EN | 39562306a36Sopenharmony_ci TW5864_ME_EN | BIT(5) /* SRCH_OPT default */); 39662306a36Sopenharmony_ci } 39762306a36Sopenharmony_ci tw5864_prepare_frame_headers(input); 39862306a36Sopenharmony_ci tw_writel(TW5864_VLC, 39962306a36Sopenharmony_ci TW5864_VLC_PCI_SEL | 40062306a36Sopenharmony_ci ((input->tail_nb_bits + 24) << TW5864_VLC_BIT_ALIGN_SHIFT) | 40162306a36Sopenharmony_ci input->reg_dsp_qp); 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci enc_buf_id_new = tw_mask_shift_readl(TW5864_ENC_BUF_PTR_REC1, 0x3, 40462306a36Sopenharmony_ci 2 * input->nr); 40562306a36Sopenharmony_ci tw_writel(TW5864_DSP_ENC_ORG_PTR_REG, 40662306a36Sopenharmony_ci enc_buf_id_new << TW5864_DSP_ENC_ORG_PTR_SHIFT); 40762306a36Sopenharmony_ci tw_writel(TW5864_DSP_ENC_REC, 40862306a36Sopenharmony_ci enc_buf_id_new << 12 | ((enc_buf_id_new + 3) & 3)); 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci tw_writel(TW5864_SLICE, TW5864_START_NSLICE); 41162306a36Sopenharmony_ci tw_writel(TW5864_SLICE, 0); 41262306a36Sopenharmony_ci} 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_cistatic int tw5864_disable_input(struct tw5864_input *input) 41562306a36Sopenharmony_ci{ 41662306a36Sopenharmony_ci struct tw5864_dev *dev = input->root; 41762306a36Sopenharmony_ci unsigned long flags; 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci dev_dbg(&dev->pci->dev, "Disabling channel %d\n", input->nr); 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci spin_lock_irqsave(&dev->slock, flags); 42262306a36Sopenharmony_ci input->enabled = 0; 42362306a36Sopenharmony_ci spin_unlock_irqrestore(&dev->slock, flags); 42462306a36Sopenharmony_ci return 0; 42562306a36Sopenharmony_ci} 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_cistatic int tw5864_start_streaming(struct vb2_queue *q, unsigned int count) 42862306a36Sopenharmony_ci{ 42962306a36Sopenharmony_ci struct tw5864_input *input = vb2_get_drv_priv(q); 43062306a36Sopenharmony_ci int ret; 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci ret = tw5864_enable_input(input); 43362306a36Sopenharmony_ci if (!ret) 43462306a36Sopenharmony_ci return 0; 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci while (!list_empty(&input->active)) { 43762306a36Sopenharmony_ci struct tw5864_buf *buf = list_entry(input->active.next, 43862306a36Sopenharmony_ci struct tw5864_buf, list); 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci list_del(&buf->list); 44162306a36Sopenharmony_ci vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_QUEUED); 44262306a36Sopenharmony_ci } 44362306a36Sopenharmony_ci return ret; 44462306a36Sopenharmony_ci} 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_cistatic void tw5864_stop_streaming(struct vb2_queue *q) 44762306a36Sopenharmony_ci{ 44862306a36Sopenharmony_ci unsigned long flags; 44962306a36Sopenharmony_ci struct tw5864_input *input = vb2_get_drv_priv(q); 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci tw5864_disable_input(input); 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci spin_lock_irqsave(&input->slock, flags); 45462306a36Sopenharmony_ci if (input->vb) { 45562306a36Sopenharmony_ci vb2_buffer_done(&input->vb->vb.vb2_buf, VB2_BUF_STATE_ERROR); 45662306a36Sopenharmony_ci input->vb = NULL; 45762306a36Sopenharmony_ci } 45862306a36Sopenharmony_ci while (!list_empty(&input->active)) { 45962306a36Sopenharmony_ci struct tw5864_buf *buf = list_entry(input->active.next, 46062306a36Sopenharmony_ci struct tw5864_buf, list); 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci list_del(&buf->list); 46362306a36Sopenharmony_ci vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); 46462306a36Sopenharmony_ci } 46562306a36Sopenharmony_ci spin_unlock_irqrestore(&input->slock, flags); 46662306a36Sopenharmony_ci} 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_cistatic const struct vb2_ops tw5864_video_qops = { 46962306a36Sopenharmony_ci .queue_setup = tw5864_queue_setup, 47062306a36Sopenharmony_ci .buf_queue = tw5864_buf_queue, 47162306a36Sopenharmony_ci .start_streaming = tw5864_start_streaming, 47262306a36Sopenharmony_ci .stop_streaming = tw5864_stop_streaming, 47362306a36Sopenharmony_ci .wait_prepare = vb2_ops_wait_prepare, 47462306a36Sopenharmony_ci .wait_finish = vb2_ops_wait_finish, 47562306a36Sopenharmony_ci}; 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_cistatic int tw5864_s_ctrl(struct v4l2_ctrl *ctrl) 47862306a36Sopenharmony_ci{ 47962306a36Sopenharmony_ci struct tw5864_input *input = 48062306a36Sopenharmony_ci container_of(ctrl->handler, struct tw5864_input, hdl); 48162306a36Sopenharmony_ci struct tw5864_dev *dev = input->root; 48262306a36Sopenharmony_ci unsigned long flags; 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci switch (ctrl->id) { 48562306a36Sopenharmony_ci case V4L2_CID_BRIGHTNESS: 48662306a36Sopenharmony_ci tw_indir_writeb(TW5864_INDIR_VIN_A_BRIGHT(input->nr), 48762306a36Sopenharmony_ci (u8)ctrl->val); 48862306a36Sopenharmony_ci break; 48962306a36Sopenharmony_ci case V4L2_CID_HUE: 49062306a36Sopenharmony_ci tw_indir_writeb(TW5864_INDIR_VIN_7_HUE(input->nr), 49162306a36Sopenharmony_ci (u8)ctrl->val); 49262306a36Sopenharmony_ci break; 49362306a36Sopenharmony_ci case V4L2_CID_CONTRAST: 49462306a36Sopenharmony_ci tw_indir_writeb(TW5864_INDIR_VIN_9_CNTRST(input->nr), 49562306a36Sopenharmony_ci (u8)ctrl->val); 49662306a36Sopenharmony_ci break; 49762306a36Sopenharmony_ci case V4L2_CID_SATURATION: 49862306a36Sopenharmony_ci tw_indir_writeb(TW5864_INDIR_VIN_B_SAT_U(input->nr), 49962306a36Sopenharmony_ci (u8)ctrl->val); 50062306a36Sopenharmony_ci tw_indir_writeb(TW5864_INDIR_VIN_C_SAT_V(input->nr), 50162306a36Sopenharmony_ci (u8)ctrl->val); 50262306a36Sopenharmony_ci break; 50362306a36Sopenharmony_ci case V4L2_CID_MPEG_VIDEO_GOP_SIZE: 50462306a36Sopenharmony_ci input->gop = ctrl->val; 50562306a36Sopenharmony_ci return 0; 50662306a36Sopenharmony_ci case V4L2_CID_MPEG_VIDEO_H264_MIN_QP: 50762306a36Sopenharmony_ci spin_lock_irqsave(&input->slock, flags); 50862306a36Sopenharmony_ci input->qp = ctrl->val; 50962306a36Sopenharmony_ci input->reg_dsp_qp = input->qp; 51062306a36Sopenharmony_ci input->reg_dsp_ref_mvp_lambda = lambda_lookup_table[input->qp]; 51162306a36Sopenharmony_ci input->reg_dsp_i4x4_weight = intra4x4_lambda3[input->qp]; 51262306a36Sopenharmony_ci spin_unlock_irqrestore(&input->slock, flags); 51362306a36Sopenharmony_ci return 0; 51462306a36Sopenharmony_ci case V4L2_CID_DETECT_MD_GLOBAL_THRESHOLD: 51562306a36Sopenharmony_ci memset(input->md_threshold_grid_values, ctrl->val, 51662306a36Sopenharmony_ci sizeof(input->md_threshold_grid_values)); 51762306a36Sopenharmony_ci return 0; 51862306a36Sopenharmony_ci case V4L2_CID_DETECT_MD_MODE: 51962306a36Sopenharmony_ci return 0; 52062306a36Sopenharmony_ci case V4L2_CID_DETECT_MD_THRESHOLD_GRID: 52162306a36Sopenharmony_ci /* input->md_threshold_grid_ctrl->p_new.p_u16 contains data */ 52262306a36Sopenharmony_ci memcpy(input->md_threshold_grid_values, 52362306a36Sopenharmony_ci input->md_threshold_grid_ctrl->p_new.p_u16, 52462306a36Sopenharmony_ci sizeof(input->md_threshold_grid_values)); 52562306a36Sopenharmony_ci return 0; 52662306a36Sopenharmony_ci } 52762306a36Sopenharmony_ci return 0; 52862306a36Sopenharmony_ci} 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_cistatic int tw5864_fmt_vid_cap(struct file *file, void *priv, 53162306a36Sopenharmony_ci struct v4l2_format *f) 53262306a36Sopenharmony_ci{ 53362306a36Sopenharmony_ci struct tw5864_input *input = video_drvdata(file); 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci f->fmt.pix.width = 704; 53662306a36Sopenharmony_ci switch (input->std) { 53762306a36Sopenharmony_ci default: 53862306a36Sopenharmony_ci WARN_ON_ONCE(1); 53962306a36Sopenharmony_ci return -EINVAL; 54062306a36Sopenharmony_ci case STD_NTSC: 54162306a36Sopenharmony_ci f->fmt.pix.height = 480; 54262306a36Sopenharmony_ci break; 54362306a36Sopenharmony_ci case STD_PAL: 54462306a36Sopenharmony_ci case STD_SECAM: 54562306a36Sopenharmony_ci f->fmt.pix.height = 576; 54662306a36Sopenharmony_ci break; 54762306a36Sopenharmony_ci } 54862306a36Sopenharmony_ci f->fmt.pix.field = V4L2_FIELD_INTERLACED; 54962306a36Sopenharmony_ci f->fmt.pix.pixelformat = V4L2_PIX_FMT_H264; 55062306a36Sopenharmony_ci f->fmt.pix.sizeimage = H264_VLC_BUF_SIZE; 55162306a36Sopenharmony_ci f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; 55262306a36Sopenharmony_ci return 0; 55362306a36Sopenharmony_ci} 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_cistatic int tw5864_enum_input(struct file *file, void *priv, 55662306a36Sopenharmony_ci struct v4l2_input *i) 55762306a36Sopenharmony_ci{ 55862306a36Sopenharmony_ci struct tw5864_input *input = video_drvdata(file); 55962306a36Sopenharmony_ci struct tw5864_dev *dev = input->root; 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_ci u8 indir_0x000 = tw_indir_readb(TW5864_INDIR_VIN_0(input->nr)); 56262306a36Sopenharmony_ci u8 indir_0x00d = tw_indir_readb(TW5864_INDIR_VIN_D(input->nr)); 56362306a36Sopenharmony_ci u8 v1 = indir_0x000; 56462306a36Sopenharmony_ci u8 v2 = indir_0x00d; 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_ci if (i->index) 56762306a36Sopenharmony_ci return -EINVAL; 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci i->type = V4L2_INPUT_TYPE_CAMERA; 57062306a36Sopenharmony_ci snprintf(i->name, sizeof(i->name), "Encoder %d", input->nr); 57162306a36Sopenharmony_ci i->std = TW5864_NORMS; 57262306a36Sopenharmony_ci if (v1 & (1 << 7)) 57362306a36Sopenharmony_ci i->status |= V4L2_IN_ST_NO_SYNC; 57462306a36Sopenharmony_ci if (!(v1 & (1 << 6))) 57562306a36Sopenharmony_ci i->status |= V4L2_IN_ST_NO_H_LOCK; 57662306a36Sopenharmony_ci if (v1 & (1 << 2)) 57762306a36Sopenharmony_ci i->status |= V4L2_IN_ST_NO_SIGNAL; 57862306a36Sopenharmony_ci if (v1 & (1 << 1)) 57962306a36Sopenharmony_ci i->status |= V4L2_IN_ST_NO_COLOR; 58062306a36Sopenharmony_ci if (v2 & (1 << 2)) 58162306a36Sopenharmony_ci i->status |= V4L2_IN_ST_MACROVISION; 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_ci return 0; 58462306a36Sopenharmony_ci} 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_cistatic int tw5864_g_input(struct file *file, void *priv, unsigned int *i) 58762306a36Sopenharmony_ci{ 58862306a36Sopenharmony_ci *i = 0; 58962306a36Sopenharmony_ci return 0; 59062306a36Sopenharmony_ci} 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_cistatic int tw5864_s_input(struct file *file, void *priv, unsigned int i) 59362306a36Sopenharmony_ci{ 59462306a36Sopenharmony_ci if (i) 59562306a36Sopenharmony_ci return -EINVAL; 59662306a36Sopenharmony_ci return 0; 59762306a36Sopenharmony_ci} 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_cistatic int tw5864_querycap(struct file *file, void *priv, 60062306a36Sopenharmony_ci struct v4l2_capability *cap) 60162306a36Sopenharmony_ci{ 60262306a36Sopenharmony_ci struct tw5864_input *input = video_drvdata(file); 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_ci strscpy(cap->driver, "tw5864", sizeof(cap->driver)); 60562306a36Sopenharmony_ci snprintf(cap->card, sizeof(cap->card), "TW5864 Encoder %d", 60662306a36Sopenharmony_ci input->nr); 60762306a36Sopenharmony_ci return 0; 60862306a36Sopenharmony_ci} 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_cistatic int tw5864_querystd(struct file *file, void *priv, v4l2_std_id *std) 61162306a36Sopenharmony_ci{ 61262306a36Sopenharmony_ci struct tw5864_input *input = video_drvdata(file); 61362306a36Sopenharmony_ci enum tw5864_vid_std tw_std; 61462306a36Sopenharmony_ci int ret; 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ci ret = tw5864_input_std_get(input, &tw_std); 61762306a36Sopenharmony_ci if (ret) 61862306a36Sopenharmony_ci return ret; 61962306a36Sopenharmony_ci *std = tw5864_get_v4l2_std(tw_std); 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_ci return 0; 62262306a36Sopenharmony_ci} 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_cistatic int tw5864_g_std(struct file *file, void *priv, v4l2_std_id *std) 62562306a36Sopenharmony_ci{ 62662306a36Sopenharmony_ci struct tw5864_input *input = video_drvdata(file); 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_ci *std = input->v4l2_std; 62962306a36Sopenharmony_ci return 0; 63062306a36Sopenharmony_ci} 63162306a36Sopenharmony_ci 63262306a36Sopenharmony_cistatic int tw5864_s_std(struct file *file, void *priv, v4l2_std_id std) 63362306a36Sopenharmony_ci{ 63462306a36Sopenharmony_ci struct tw5864_input *input = video_drvdata(file); 63562306a36Sopenharmony_ci struct tw5864_dev *dev = input->root; 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_ci input->v4l2_std = std; 63862306a36Sopenharmony_ci input->std = tw5864_from_v4l2_std(std); 63962306a36Sopenharmony_ci tw_indir_writeb(TW5864_INDIR_VIN_E(input->nr), input->std); 64062306a36Sopenharmony_ci return 0; 64162306a36Sopenharmony_ci} 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_cistatic int tw5864_enum_fmt_vid_cap(struct file *file, void *priv, 64462306a36Sopenharmony_ci struct v4l2_fmtdesc *f) 64562306a36Sopenharmony_ci{ 64662306a36Sopenharmony_ci if (f->index) 64762306a36Sopenharmony_ci return -EINVAL; 64862306a36Sopenharmony_ci 64962306a36Sopenharmony_ci f->pixelformat = V4L2_PIX_FMT_H264; 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_ci return 0; 65262306a36Sopenharmony_ci} 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_cistatic int tw5864_subscribe_event(struct v4l2_fh *fh, 65562306a36Sopenharmony_ci const struct v4l2_event_subscription *sub) 65662306a36Sopenharmony_ci{ 65762306a36Sopenharmony_ci switch (sub->type) { 65862306a36Sopenharmony_ci case V4L2_EVENT_MOTION_DET: 65962306a36Sopenharmony_ci /* 66062306a36Sopenharmony_ci * Allow for up to 30 events (1 second for NTSC) to be stored. 66162306a36Sopenharmony_ci */ 66262306a36Sopenharmony_ci return v4l2_event_subscribe(fh, sub, 30, NULL); 66362306a36Sopenharmony_ci default: 66462306a36Sopenharmony_ci return v4l2_ctrl_subscribe_event(fh, sub); 66562306a36Sopenharmony_ci } 66662306a36Sopenharmony_ci} 66762306a36Sopenharmony_ci 66862306a36Sopenharmony_cistatic void tw5864_frame_interval_set(struct tw5864_input *input) 66962306a36Sopenharmony_ci{ 67062306a36Sopenharmony_ci /* 67162306a36Sopenharmony_ci * This register value seems to follow such approach: In each second 67262306a36Sopenharmony_ci * interval, when processing Nth frame, it checks Nth bit of register 67362306a36Sopenharmony_ci * value and, if the bit is 1, it processes the frame, otherwise the 67462306a36Sopenharmony_ci * frame is discarded. 67562306a36Sopenharmony_ci * So unary representation would work, but more or less equal gaps 67662306a36Sopenharmony_ci * between the frames should be preserved. 67762306a36Sopenharmony_ci * 67862306a36Sopenharmony_ci * For 1 FPS - 0x00000001 67962306a36Sopenharmony_ci * 00000000 00000000 00000000 00000001 68062306a36Sopenharmony_ci * 68162306a36Sopenharmony_ci * For max FPS - set all 25/30 lower bits: 68262306a36Sopenharmony_ci * 00111111 11111111 11111111 11111111 (NTSC) 68362306a36Sopenharmony_ci * 00000001 11111111 11111111 11111111 (PAL) 68462306a36Sopenharmony_ci * 68562306a36Sopenharmony_ci * For half of max FPS - use such pattern: 68662306a36Sopenharmony_ci * 00010101 01010101 01010101 01010101 (NTSC) 68762306a36Sopenharmony_ci * 00000001 01010101 01010101 01010101 (PAL) 68862306a36Sopenharmony_ci * 68962306a36Sopenharmony_ci * Et cetera. 69062306a36Sopenharmony_ci * 69162306a36Sopenharmony_ci * The value supplied to hardware is capped by mask of 25/30 lower bits. 69262306a36Sopenharmony_ci */ 69362306a36Sopenharmony_ci struct tw5864_dev *dev = input->root; 69462306a36Sopenharmony_ci u32 unary_framerate = 0; 69562306a36Sopenharmony_ci int shift = 0; 69662306a36Sopenharmony_ci int std_max_fps = input->std == STD_NTSC ? 30 : 25; 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_ci for (shift = 0; shift < std_max_fps; shift += input->frame_interval) 69962306a36Sopenharmony_ci unary_framerate |= 0x00000001 << shift; 70062306a36Sopenharmony_ci 70162306a36Sopenharmony_ci tw_writel(TW5864_H264EN_RATE_CNTL_LO_WORD(input->nr, 0), 70262306a36Sopenharmony_ci unary_framerate >> 16); 70362306a36Sopenharmony_ci tw_writel(TW5864_H264EN_RATE_CNTL_HI_WORD(input->nr, 0), 70462306a36Sopenharmony_ci unary_framerate & 0xffff); 70562306a36Sopenharmony_ci} 70662306a36Sopenharmony_ci 70762306a36Sopenharmony_cistatic int tw5864_frameinterval_get(struct tw5864_input *input, 70862306a36Sopenharmony_ci struct v4l2_fract *frameinterval) 70962306a36Sopenharmony_ci{ 71062306a36Sopenharmony_ci struct tw5864_dev *dev = input->root; 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_ci switch (input->std) { 71362306a36Sopenharmony_ci case STD_NTSC: 71462306a36Sopenharmony_ci frameinterval->numerator = 1001; 71562306a36Sopenharmony_ci frameinterval->denominator = 30000; 71662306a36Sopenharmony_ci break; 71762306a36Sopenharmony_ci case STD_PAL: 71862306a36Sopenharmony_ci case STD_SECAM: 71962306a36Sopenharmony_ci frameinterval->numerator = 1; 72062306a36Sopenharmony_ci frameinterval->denominator = 25; 72162306a36Sopenharmony_ci break; 72262306a36Sopenharmony_ci default: 72362306a36Sopenharmony_ci dev_warn(&dev->pci->dev, "tw5864_frameinterval_get requested for unknown std %d\n", 72462306a36Sopenharmony_ci input->std); 72562306a36Sopenharmony_ci return -EINVAL; 72662306a36Sopenharmony_ci } 72762306a36Sopenharmony_ci 72862306a36Sopenharmony_ci return 0; 72962306a36Sopenharmony_ci} 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_cistatic int tw5864_enum_framesizes(struct file *file, void *priv, 73262306a36Sopenharmony_ci struct v4l2_frmsizeenum *fsize) 73362306a36Sopenharmony_ci{ 73462306a36Sopenharmony_ci struct tw5864_input *input = video_drvdata(file); 73562306a36Sopenharmony_ci 73662306a36Sopenharmony_ci if (fsize->index > 0) 73762306a36Sopenharmony_ci return -EINVAL; 73862306a36Sopenharmony_ci if (fsize->pixel_format != V4L2_PIX_FMT_H264) 73962306a36Sopenharmony_ci return -EINVAL; 74062306a36Sopenharmony_ci 74162306a36Sopenharmony_ci fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; 74262306a36Sopenharmony_ci fsize->discrete.width = 704; 74362306a36Sopenharmony_ci fsize->discrete.height = input->std == STD_NTSC ? 480 : 576; 74462306a36Sopenharmony_ci 74562306a36Sopenharmony_ci return 0; 74662306a36Sopenharmony_ci} 74762306a36Sopenharmony_ci 74862306a36Sopenharmony_cistatic int tw5864_enum_frameintervals(struct file *file, void *priv, 74962306a36Sopenharmony_ci struct v4l2_frmivalenum *fintv) 75062306a36Sopenharmony_ci{ 75162306a36Sopenharmony_ci struct tw5864_input *input = video_drvdata(file); 75262306a36Sopenharmony_ci struct v4l2_fract frameinterval; 75362306a36Sopenharmony_ci int std_max_fps = input->std == STD_NTSC ? 30 : 25; 75462306a36Sopenharmony_ci struct v4l2_frmsizeenum fsize = { .index = fintv->index, 75562306a36Sopenharmony_ci .pixel_format = fintv->pixel_format }; 75662306a36Sopenharmony_ci int ret; 75762306a36Sopenharmony_ci 75862306a36Sopenharmony_ci ret = tw5864_enum_framesizes(file, priv, &fsize); 75962306a36Sopenharmony_ci if (ret) 76062306a36Sopenharmony_ci return ret; 76162306a36Sopenharmony_ci 76262306a36Sopenharmony_ci if (fintv->width != fsize.discrete.width || 76362306a36Sopenharmony_ci fintv->height != fsize.discrete.height) 76462306a36Sopenharmony_ci return -EINVAL; 76562306a36Sopenharmony_ci 76662306a36Sopenharmony_ci fintv->type = V4L2_FRMIVAL_TYPE_STEPWISE; 76762306a36Sopenharmony_ci 76862306a36Sopenharmony_ci ret = tw5864_frameinterval_get(input, &frameinterval); 76962306a36Sopenharmony_ci if (ret) 77062306a36Sopenharmony_ci return ret; 77162306a36Sopenharmony_ci 77262306a36Sopenharmony_ci fintv->stepwise.step = frameinterval; 77362306a36Sopenharmony_ci fintv->stepwise.min = frameinterval; 77462306a36Sopenharmony_ci fintv->stepwise.max = frameinterval; 77562306a36Sopenharmony_ci fintv->stepwise.max.numerator *= std_max_fps; 77662306a36Sopenharmony_ci 77762306a36Sopenharmony_ci return ret; 77862306a36Sopenharmony_ci} 77962306a36Sopenharmony_ci 78062306a36Sopenharmony_cistatic int tw5864_g_parm(struct file *file, void *priv, 78162306a36Sopenharmony_ci struct v4l2_streamparm *sp) 78262306a36Sopenharmony_ci{ 78362306a36Sopenharmony_ci struct tw5864_input *input = video_drvdata(file); 78462306a36Sopenharmony_ci struct v4l2_captureparm *cp = &sp->parm.capture; 78562306a36Sopenharmony_ci int ret; 78662306a36Sopenharmony_ci 78762306a36Sopenharmony_ci cp->capability = V4L2_CAP_TIMEPERFRAME; 78862306a36Sopenharmony_ci 78962306a36Sopenharmony_ci ret = tw5864_frameinterval_get(input, &cp->timeperframe); 79062306a36Sopenharmony_ci if (ret) 79162306a36Sopenharmony_ci return ret; 79262306a36Sopenharmony_ci 79362306a36Sopenharmony_ci cp->timeperframe.numerator *= input->frame_interval; 79462306a36Sopenharmony_ci cp->capturemode = 0; 79562306a36Sopenharmony_ci cp->readbuffers = 2; 79662306a36Sopenharmony_ci 79762306a36Sopenharmony_ci return ret; 79862306a36Sopenharmony_ci} 79962306a36Sopenharmony_ci 80062306a36Sopenharmony_cistatic int tw5864_s_parm(struct file *file, void *priv, 80162306a36Sopenharmony_ci struct v4l2_streamparm *sp) 80262306a36Sopenharmony_ci{ 80362306a36Sopenharmony_ci struct tw5864_input *input = video_drvdata(file); 80462306a36Sopenharmony_ci struct v4l2_fract *t = &sp->parm.capture.timeperframe; 80562306a36Sopenharmony_ci struct v4l2_fract time_base; 80662306a36Sopenharmony_ci int ret; 80762306a36Sopenharmony_ci 80862306a36Sopenharmony_ci ret = tw5864_frameinterval_get(input, &time_base); 80962306a36Sopenharmony_ci if (ret) 81062306a36Sopenharmony_ci return ret; 81162306a36Sopenharmony_ci 81262306a36Sopenharmony_ci if (!t->numerator || !t->denominator) { 81362306a36Sopenharmony_ci t->numerator = time_base.numerator * input->frame_interval; 81462306a36Sopenharmony_ci t->denominator = time_base.denominator; 81562306a36Sopenharmony_ci } else if (t->denominator != time_base.denominator) { 81662306a36Sopenharmony_ci t->numerator = t->numerator * time_base.denominator / 81762306a36Sopenharmony_ci t->denominator; 81862306a36Sopenharmony_ci t->denominator = time_base.denominator; 81962306a36Sopenharmony_ci } 82062306a36Sopenharmony_ci 82162306a36Sopenharmony_ci input->frame_interval = t->numerator / time_base.numerator; 82262306a36Sopenharmony_ci if (input->frame_interval < 1) 82362306a36Sopenharmony_ci input->frame_interval = 1; 82462306a36Sopenharmony_ci tw5864_frame_interval_set(input); 82562306a36Sopenharmony_ci return tw5864_g_parm(file, priv, sp); 82662306a36Sopenharmony_ci} 82762306a36Sopenharmony_ci 82862306a36Sopenharmony_cistatic const struct v4l2_ctrl_ops tw5864_ctrl_ops = { 82962306a36Sopenharmony_ci .s_ctrl = tw5864_s_ctrl, 83062306a36Sopenharmony_ci}; 83162306a36Sopenharmony_ci 83262306a36Sopenharmony_cistatic const struct v4l2_file_operations video_fops = { 83362306a36Sopenharmony_ci .owner = THIS_MODULE, 83462306a36Sopenharmony_ci .open = v4l2_fh_open, 83562306a36Sopenharmony_ci .release = vb2_fop_release, 83662306a36Sopenharmony_ci .read = vb2_fop_read, 83762306a36Sopenharmony_ci .poll = vb2_fop_poll, 83862306a36Sopenharmony_ci .mmap = vb2_fop_mmap, 83962306a36Sopenharmony_ci .unlocked_ioctl = video_ioctl2, 84062306a36Sopenharmony_ci}; 84162306a36Sopenharmony_ci 84262306a36Sopenharmony_ci#ifdef CONFIG_VIDEO_ADV_DEBUG 84362306a36Sopenharmony_ci 84462306a36Sopenharmony_ci#define INDIR_SPACE_MAP_SHIFT 0x100000 84562306a36Sopenharmony_ci 84662306a36Sopenharmony_cistatic int tw5864_g_reg(struct file *file, void *fh, 84762306a36Sopenharmony_ci struct v4l2_dbg_register *reg) 84862306a36Sopenharmony_ci{ 84962306a36Sopenharmony_ci struct tw5864_input *input = video_drvdata(file); 85062306a36Sopenharmony_ci struct tw5864_dev *dev = input->root; 85162306a36Sopenharmony_ci 85262306a36Sopenharmony_ci if (reg->reg < INDIR_SPACE_MAP_SHIFT) { 85362306a36Sopenharmony_ci if (reg->reg > 0x87fff) 85462306a36Sopenharmony_ci return -EINVAL; 85562306a36Sopenharmony_ci reg->size = 4; 85662306a36Sopenharmony_ci reg->val = tw_readl(reg->reg); 85762306a36Sopenharmony_ci } else { 85862306a36Sopenharmony_ci __u64 indir_addr = reg->reg - INDIR_SPACE_MAP_SHIFT; 85962306a36Sopenharmony_ci 86062306a36Sopenharmony_ci if (indir_addr > 0xefe) 86162306a36Sopenharmony_ci return -EINVAL; 86262306a36Sopenharmony_ci reg->size = 1; 86362306a36Sopenharmony_ci reg->val = tw_indir_readb(reg->reg); 86462306a36Sopenharmony_ci } 86562306a36Sopenharmony_ci return 0; 86662306a36Sopenharmony_ci} 86762306a36Sopenharmony_ci 86862306a36Sopenharmony_cistatic int tw5864_s_reg(struct file *file, void *fh, 86962306a36Sopenharmony_ci const struct v4l2_dbg_register *reg) 87062306a36Sopenharmony_ci{ 87162306a36Sopenharmony_ci struct tw5864_input *input = video_drvdata(file); 87262306a36Sopenharmony_ci struct tw5864_dev *dev = input->root; 87362306a36Sopenharmony_ci 87462306a36Sopenharmony_ci if (reg->reg < INDIR_SPACE_MAP_SHIFT) { 87562306a36Sopenharmony_ci if (reg->reg > 0x87fff) 87662306a36Sopenharmony_ci return -EINVAL; 87762306a36Sopenharmony_ci tw_writel(reg->reg, reg->val); 87862306a36Sopenharmony_ci } else { 87962306a36Sopenharmony_ci __u64 indir_addr = reg->reg - INDIR_SPACE_MAP_SHIFT; 88062306a36Sopenharmony_ci 88162306a36Sopenharmony_ci if (indir_addr > 0xefe) 88262306a36Sopenharmony_ci return -EINVAL; 88362306a36Sopenharmony_ci tw_indir_writeb(reg->reg, reg->val); 88462306a36Sopenharmony_ci } 88562306a36Sopenharmony_ci return 0; 88662306a36Sopenharmony_ci} 88762306a36Sopenharmony_ci#endif 88862306a36Sopenharmony_ci 88962306a36Sopenharmony_cistatic const struct v4l2_ioctl_ops video_ioctl_ops = { 89062306a36Sopenharmony_ci .vidioc_querycap = tw5864_querycap, 89162306a36Sopenharmony_ci .vidioc_enum_fmt_vid_cap = tw5864_enum_fmt_vid_cap, 89262306a36Sopenharmony_ci .vidioc_reqbufs = vb2_ioctl_reqbufs, 89362306a36Sopenharmony_ci .vidioc_create_bufs = vb2_ioctl_create_bufs, 89462306a36Sopenharmony_ci .vidioc_querybuf = vb2_ioctl_querybuf, 89562306a36Sopenharmony_ci .vidioc_qbuf = vb2_ioctl_qbuf, 89662306a36Sopenharmony_ci .vidioc_dqbuf = vb2_ioctl_dqbuf, 89762306a36Sopenharmony_ci .vidioc_expbuf = vb2_ioctl_expbuf, 89862306a36Sopenharmony_ci .vidioc_querystd = tw5864_querystd, 89962306a36Sopenharmony_ci .vidioc_s_std = tw5864_s_std, 90062306a36Sopenharmony_ci .vidioc_g_std = tw5864_g_std, 90162306a36Sopenharmony_ci .vidioc_enum_input = tw5864_enum_input, 90262306a36Sopenharmony_ci .vidioc_g_input = tw5864_g_input, 90362306a36Sopenharmony_ci .vidioc_s_input = tw5864_s_input, 90462306a36Sopenharmony_ci .vidioc_streamon = vb2_ioctl_streamon, 90562306a36Sopenharmony_ci .vidioc_streamoff = vb2_ioctl_streamoff, 90662306a36Sopenharmony_ci .vidioc_try_fmt_vid_cap = tw5864_fmt_vid_cap, 90762306a36Sopenharmony_ci .vidioc_s_fmt_vid_cap = tw5864_fmt_vid_cap, 90862306a36Sopenharmony_ci .vidioc_g_fmt_vid_cap = tw5864_fmt_vid_cap, 90962306a36Sopenharmony_ci .vidioc_log_status = v4l2_ctrl_log_status, 91062306a36Sopenharmony_ci .vidioc_subscribe_event = tw5864_subscribe_event, 91162306a36Sopenharmony_ci .vidioc_unsubscribe_event = v4l2_event_unsubscribe, 91262306a36Sopenharmony_ci .vidioc_enum_framesizes = tw5864_enum_framesizes, 91362306a36Sopenharmony_ci .vidioc_enum_frameintervals = tw5864_enum_frameintervals, 91462306a36Sopenharmony_ci .vidioc_s_parm = tw5864_s_parm, 91562306a36Sopenharmony_ci .vidioc_g_parm = tw5864_g_parm, 91662306a36Sopenharmony_ci#ifdef CONFIG_VIDEO_ADV_DEBUG 91762306a36Sopenharmony_ci .vidioc_g_register = tw5864_g_reg, 91862306a36Sopenharmony_ci .vidioc_s_register = tw5864_s_reg, 91962306a36Sopenharmony_ci#endif 92062306a36Sopenharmony_ci}; 92162306a36Sopenharmony_ci 92262306a36Sopenharmony_cistatic const struct video_device tw5864_video_template = { 92362306a36Sopenharmony_ci .name = "tw5864_video", 92462306a36Sopenharmony_ci .fops = &video_fops, 92562306a36Sopenharmony_ci .ioctl_ops = &video_ioctl_ops, 92662306a36Sopenharmony_ci .release = video_device_release_empty, 92762306a36Sopenharmony_ci .tvnorms = TW5864_NORMS, 92862306a36Sopenharmony_ci .device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE | 92962306a36Sopenharmony_ci V4L2_CAP_STREAMING, 93062306a36Sopenharmony_ci}; 93162306a36Sopenharmony_ci 93262306a36Sopenharmony_ci/* Motion Detection Threshold matrix */ 93362306a36Sopenharmony_cistatic const struct v4l2_ctrl_config tw5864_md_thresholds = { 93462306a36Sopenharmony_ci .ops = &tw5864_ctrl_ops, 93562306a36Sopenharmony_ci .id = V4L2_CID_DETECT_MD_THRESHOLD_GRID, 93662306a36Sopenharmony_ci .dims = {MD_CELLS_HOR, MD_CELLS_VERT}, 93762306a36Sopenharmony_ci .def = 14, 93862306a36Sopenharmony_ci /* See tw5864_md_metric_from_mvd() */ 93962306a36Sopenharmony_ci .max = 2 * 0x0f, 94062306a36Sopenharmony_ci .step = 1, 94162306a36Sopenharmony_ci}; 94262306a36Sopenharmony_ci 94362306a36Sopenharmony_cistatic int tw5864_video_input_init(struct tw5864_input *dev, int video_nr); 94462306a36Sopenharmony_cistatic void tw5864_video_input_fini(struct tw5864_input *dev); 94562306a36Sopenharmony_cistatic void tw5864_encoder_tables_upload(struct tw5864_dev *dev); 94662306a36Sopenharmony_ci 94762306a36Sopenharmony_ciint tw5864_video_init(struct tw5864_dev *dev, int *video_nr) 94862306a36Sopenharmony_ci{ 94962306a36Sopenharmony_ci int i; 95062306a36Sopenharmony_ci int ret; 95162306a36Sopenharmony_ci unsigned long flags; 95262306a36Sopenharmony_ci int last_dma_allocated = -1; 95362306a36Sopenharmony_ci int last_input_nr_registered = -1; 95462306a36Sopenharmony_ci 95562306a36Sopenharmony_ci for (i = 0; i < H264_BUF_CNT; i++) { 95662306a36Sopenharmony_ci struct tw5864_h264_frame *frame = &dev->h264_buf[i]; 95762306a36Sopenharmony_ci 95862306a36Sopenharmony_ci frame->vlc.addr = dma_alloc_coherent(&dev->pci->dev, 95962306a36Sopenharmony_ci H264_VLC_BUF_SIZE, 96062306a36Sopenharmony_ci &frame->vlc.dma_addr, 96162306a36Sopenharmony_ci GFP_KERNEL | GFP_DMA32); 96262306a36Sopenharmony_ci if (!frame->vlc.addr) { 96362306a36Sopenharmony_ci dev_err(&dev->pci->dev, "dma alloc fail\n"); 96462306a36Sopenharmony_ci ret = -ENOMEM; 96562306a36Sopenharmony_ci goto free_dma; 96662306a36Sopenharmony_ci } 96762306a36Sopenharmony_ci frame->mv.addr = dma_alloc_coherent(&dev->pci->dev, 96862306a36Sopenharmony_ci H264_MV_BUF_SIZE, 96962306a36Sopenharmony_ci &frame->mv.dma_addr, 97062306a36Sopenharmony_ci GFP_KERNEL | GFP_DMA32); 97162306a36Sopenharmony_ci if (!frame->mv.addr) { 97262306a36Sopenharmony_ci dev_err(&dev->pci->dev, "dma alloc fail\n"); 97362306a36Sopenharmony_ci ret = -ENOMEM; 97462306a36Sopenharmony_ci dma_free_coherent(&dev->pci->dev, H264_VLC_BUF_SIZE, 97562306a36Sopenharmony_ci frame->vlc.addr, frame->vlc.dma_addr); 97662306a36Sopenharmony_ci goto free_dma; 97762306a36Sopenharmony_ci } 97862306a36Sopenharmony_ci last_dma_allocated = i; 97962306a36Sopenharmony_ci } 98062306a36Sopenharmony_ci 98162306a36Sopenharmony_ci tw5864_encoder_tables_upload(dev); 98262306a36Sopenharmony_ci 98362306a36Sopenharmony_ci /* Picture is distorted without this block */ 98462306a36Sopenharmony_ci /* use falling edge to sample 54M to 108M */ 98562306a36Sopenharmony_ci tw_indir_writeb(TW5864_INDIR_VD_108_POL, TW5864_INDIR_VD_108_POL_BOTH); 98662306a36Sopenharmony_ci tw_indir_writeb(TW5864_INDIR_CLK0_SEL, 0x00); 98762306a36Sopenharmony_ci 98862306a36Sopenharmony_ci tw_indir_writeb(TW5864_INDIR_DDRA_DLL_DQS_SEL0, 0x02); 98962306a36Sopenharmony_ci tw_indir_writeb(TW5864_INDIR_DDRA_DLL_DQS_SEL1, 0x02); 99062306a36Sopenharmony_ci tw_indir_writeb(TW5864_INDIR_DDRA_DLL_CLK90_SEL, 0x02); 99162306a36Sopenharmony_ci tw_indir_writeb(TW5864_INDIR_DDRB_DLL_DQS_SEL0, 0x02); 99262306a36Sopenharmony_ci tw_indir_writeb(TW5864_INDIR_DDRB_DLL_DQS_SEL1, 0x02); 99362306a36Sopenharmony_ci tw_indir_writeb(TW5864_INDIR_DDRB_DLL_CLK90_SEL, 0x02); 99462306a36Sopenharmony_ci 99562306a36Sopenharmony_ci /* video input reset */ 99662306a36Sopenharmony_ci tw_indir_writeb(TW5864_INDIR_RESET, 0); 99762306a36Sopenharmony_ci tw_indir_writeb(TW5864_INDIR_RESET, TW5864_INDIR_RESET_VD | 99862306a36Sopenharmony_ci TW5864_INDIR_RESET_DLL | TW5864_INDIR_RESET_MUX_CORE); 99962306a36Sopenharmony_ci msleep(20); 100062306a36Sopenharmony_ci 100162306a36Sopenharmony_ci /* 100262306a36Sopenharmony_ci * Select Part A mode for all channels. 100362306a36Sopenharmony_ci * tw_setl instead of tw_clearl for Part B mode. 100462306a36Sopenharmony_ci * 100562306a36Sopenharmony_ci * I guess "Part B" is primarily for downscaled version of same channel 100662306a36Sopenharmony_ci * which goes in Part A of same bus 100762306a36Sopenharmony_ci */ 100862306a36Sopenharmony_ci tw_writel(TW5864_FULL_HALF_MODE_SEL, 0); 100962306a36Sopenharmony_ci 101062306a36Sopenharmony_ci tw_indir_writeb(TW5864_INDIR_PV_VD_CK_POL, 101162306a36Sopenharmony_ci TW5864_INDIR_PV_VD_CK_POL_VD(0) | 101262306a36Sopenharmony_ci TW5864_INDIR_PV_VD_CK_POL_VD(1) | 101362306a36Sopenharmony_ci TW5864_INDIR_PV_VD_CK_POL_VD(2) | 101462306a36Sopenharmony_ci TW5864_INDIR_PV_VD_CK_POL_VD(3)); 101562306a36Sopenharmony_ci 101662306a36Sopenharmony_ci spin_lock_irqsave(&dev->slock, flags); 101762306a36Sopenharmony_ci dev->encoder_busy = 0; 101862306a36Sopenharmony_ci dev->h264_buf_r_index = 0; 101962306a36Sopenharmony_ci dev->h264_buf_w_index = 0; 102062306a36Sopenharmony_ci tw_writel(TW5864_VLC_STREAM_BASE_ADDR, 102162306a36Sopenharmony_ci dev->h264_buf[dev->h264_buf_w_index].vlc.dma_addr); 102262306a36Sopenharmony_ci tw_writel(TW5864_MV_STREAM_BASE_ADDR, 102362306a36Sopenharmony_ci dev->h264_buf[dev->h264_buf_w_index].mv.dma_addr); 102462306a36Sopenharmony_ci spin_unlock_irqrestore(&dev->slock, flags); 102562306a36Sopenharmony_ci 102662306a36Sopenharmony_ci tw_writel(TW5864_SEN_EN_CH, 0x000f); 102762306a36Sopenharmony_ci tw_writel(TW5864_H264EN_CH_EN, 0x000f); 102862306a36Sopenharmony_ci 102962306a36Sopenharmony_ci tw_writel(TW5864_H264EN_BUS0_MAP, 0x00000000); 103062306a36Sopenharmony_ci tw_writel(TW5864_H264EN_BUS1_MAP, 0x00001111); 103162306a36Sopenharmony_ci tw_writel(TW5864_H264EN_BUS2_MAP, 0x00002222); 103262306a36Sopenharmony_ci tw_writel(TW5864_H264EN_BUS3_MAP, 0x00003333); 103362306a36Sopenharmony_ci 103462306a36Sopenharmony_ci /* 103562306a36Sopenharmony_ci * Quote from Intersil (manufacturer): 103662306a36Sopenharmony_ci * 0x0038 is managed by HW, and by default it won't pass the pointer set 103762306a36Sopenharmony_ci * at 0x0010. So if you don't do encoding, 0x0038 should stay at '3' 103862306a36Sopenharmony_ci * (with 4 frames in buffer). If you encode one frame and then move 103962306a36Sopenharmony_ci * 0x0010 to '1' for example, HW will take one more frame and set it to 104062306a36Sopenharmony_ci * buffer #0, and then you should see 0x0038 is set to '0'. There is 104162306a36Sopenharmony_ci * only one HW encoder engine, so 4 channels cannot get encoded 104262306a36Sopenharmony_ci * simultaneously. But each channel does have its own buffer (for 104362306a36Sopenharmony_ci * original frames and reconstructed frames). So there is no problem to 104462306a36Sopenharmony_ci * manage encoding for 4 channels at same time and no need to force 104562306a36Sopenharmony_ci * I-frames in switching channels. 104662306a36Sopenharmony_ci * End of quote. 104762306a36Sopenharmony_ci * 104862306a36Sopenharmony_ci * If we set 0x0010 (TW5864_ENC_BUF_PTR_REC1) to 0 (for any channel), we 104962306a36Sopenharmony_ci * have no "rolling" (until we change this value). 105062306a36Sopenharmony_ci * If we set 0x0010 (TW5864_ENC_BUF_PTR_REC1) to 0x3, it starts to roll 105162306a36Sopenharmony_ci * continuously together with 0x0038. 105262306a36Sopenharmony_ci */ 105362306a36Sopenharmony_ci tw_writel(TW5864_ENC_BUF_PTR_REC1, 0x00ff); 105462306a36Sopenharmony_ci tw_writel(TW5864_PCI_INTTM_SCALE, 0); 105562306a36Sopenharmony_ci 105662306a36Sopenharmony_ci tw_writel(TW5864_INTERLACING, TW5864_DI_EN); 105762306a36Sopenharmony_ci tw_writel(TW5864_MASTER_ENB_REG, TW5864_PCI_VLC_INTR_ENB); 105862306a36Sopenharmony_ci tw_writel(TW5864_PCI_INTR_CTL, 105962306a36Sopenharmony_ci TW5864_TIMER_INTR_ENB | TW5864_PCI_MAST_ENB | 106062306a36Sopenharmony_ci TW5864_MVD_VLC_MAST_ENB); 106162306a36Sopenharmony_ci 106262306a36Sopenharmony_ci dev->irqmask |= TW5864_INTR_VLC_DONE | TW5864_INTR_TIMER; 106362306a36Sopenharmony_ci tw5864_irqmask_apply(dev); 106462306a36Sopenharmony_ci 106562306a36Sopenharmony_ci tasklet_setup(&dev->tasklet, tw5864_handle_frame_task); 106662306a36Sopenharmony_ci 106762306a36Sopenharmony_ci for (i = 0; i < TW5864_INPUTS; i++) { 106862306a36Sopenharmony_ci dev->inputs[i].root = dev; 106962306a36Sopenharmony_ci dev->inputs[i].nr = i; 107062306a36Sopenharmony_ci ret = tw5864_video_input_init(&dev->inputs[i], video_nr[i]); 107162306a36Sopenharmony_ci if (ret) 107262306a36Sopenharmony_ci goto fini_video_inputs; 107362306a36Sopenharmony_ci last_input_nr_registered = i; 107462306a36Sopenharmony_ci } 107562306a36Sopenharmony_ci 107662306a36Sopenharmony_ci return 0; 107762306a36Sopenharmony_ci 107862306a36Sopenharmony_cifini_video_inputs: 107962306a36Sopenharmony_ci for (i = last_input_nr_registered; i >= 0; i--) 108062306a36Sopenharmony_ci tw5864_video_input_fini(&dev->inputs[i]); 108162306a36Sopenharmony_ci 108262306a36Sopenharmony_ci tasklet_kill(&dev->tasklet); 108362306a36Sopenharmony_ci 108462306a36Sopenharmony_cifree_dma: 108562306a36Sopenharmony_ci for (i = last_dma_allocated; i >= 0; i--) { 108662306a36Sopenharmony_ci dma_free_coherent(&dev->pci->dev, H264_VLC_BUF_SIZE, 108762306a36Sopenharmony_ci dev->h264_buf[i].vlc.addr, 108862306a36Sopenharmony_ci dev->h264_buf[i].vlc.dma_addr); 108962306a36Sopenharmony_ci dma_free_coherent(&dev->pci->dev, H264_MV_BUF_SIZE, 109062306a36Sopenharmony_ci dev->h264_buf[i].mv.addr, 109162306a36Sopenharmony_ci dev->h264_buf[i].mv.dma_addr); 109262306a36Sopenharmony_ci } 109362306a36Sopenharmony_ci 109462306a36Sopenharmony_ci return ret; 109562306a36Sopenharmony_ci} 109662306a36Sopenharmony_ci 109762306a36Sopenharmony_cistatic int tw5864_video_input_init(struct tw5864_input *input, int video_nr) 109862306a36Sopenharmony_ci{ 109962306a36Sopenharmony_ci struct tw5864_dev *dev = input->root; 110062306a36Sopenharmony_ci int ret; 110162306a36Sopenharmony_ci struct v4l2_ctrl_handler *hdl = &input->hdl; 110262306a36Sopenharmony_ci 110362306a36Sopenharmony_ci mutex_init(&input->lock); 110462306a36Sopenharmony_ci spin_lock_init(&input->slock); 110562306a36Sopenharmony_ci 110662306a36Sopenharmony_ci /* setup video buffers queue */ 110762306a36Sopenharmony_ci INIT_LIST_HEAD(&input->active); 110862306a36Sopenharmony_ci input->vidq.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 110962306a36Sopenharmony_ci input->vidq.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; 111062306a36Sopenharmony_ci input->vidq.io_modes = VB2_MMAP | VB2_READ | VB2_DMABUF; 111162306a36Sopenharmony_ci input->vidq.ops = &tw5864_video_qops; 111262306a36Sopenharmony_ci input->vidq.mem_ops = &vb2_dma_contig_memops; 111362306a36Sopenharmony_ci input->vidq.drv_priv = input; 111462306a36Sopenharmony_ci input->vidq.gfp_flags = 0; 111562306a36Sopenharmony_ci input->vidq.buf_struct_size = sizeof(struct tw5864_buf); 111662306a36Sopenharmony_ci input->vidq.lock = &input->lock; 111762306a36Sopenharmony_ci input->vidq.min_buffers_needed = 2; 111862306a36Sopenharmony_ci input->vidq.dev = &input->root->pci->dev; 111962306a36Sopenharmony_ci ret = vb2_queue_init(&input->vidq); 112062306a36Sopenharmony_ci if (ret) 112162306a36Sopenharmony_ci goto free_mutex; 112262306a36Sopenharmony_ci 112362306a36Sopenharmony_ci input->vdev = tw5864_video_template; 112462306a36Sopenharmony_ci input->vdev.v4l2_dev = &input->root->v4l2_dev; 112562306a36Sopenharmony_ci input->vdev.lock = &input->lock; 112662306a36Sopenharmony_ci input->vdev.queue = &input->vidq; 112762306a36Sopenharmony_ci video_set_drvdata(&input->vdev, input); 112862306a36Sopenharmony_ci 112962306a36Sopenharmony_ci /* Initialize the device control structures */ 113062306a36Sopenharmony_ci v4l2_ctrl_handler_init(hdl, 6); 113162306a36Sopenharmony_ci v4l2_ctrl_new_std(hdl, &tw5864_ctrl_ops, 113262306a36Sopenharmony_ci V4L2_CID_BRIGHTNESS, -128, 127, 1, 0); 113362306a36Sopenharmony_ci v4l2_ctrl_new_std(hdl, &tw5864_ctrl_ops, 113462306a36Sopenharmony_ci V4L2_CID_CONTRAST, 0, 255, 1, 100); 113562306a36Sopenharmony_ci v4l2_ctrl_new_std(hdl, &tw5864_ctrl_ops, 113662306a36Sopenharmony_ci V4L2_CID_SATURATION, 0, 255, 1, 128); 113762306a36Sopenharmony_ci v4l2_ctrl_new_std(hdl, &tw5864_ctrl_ops, V4L2_CID_HUE, -128, 127, 1, 0); 113862306a36Sopenharmony_ci v4l2_ctrl_new_std(hdl, &tw5864_ctrl_ops, V4L2_CID_MPEG_VIDEO_GOP_SIZE, 113962306a36Sopenharmony_ci 1, MAX_GOP_SIZE, 1, GOP_SIZE); 114062306a36Sopenharmony_ci v4l2_ctrl_new_std(hdl, &tw5864_ctrl_ops, 114162306a36Sopenharmony_ci V4L2_CID_MPEG_VIDEO_H264_MIN_QP, 28, 51, 1, QP_VALUE); 114262306a36Sopenharmony_ci v4l2_ctrl_new_std_menu(hdl, &tw5864_ctrl_ops, 114362306a36Sopenharmony_ci V4L2_CID_DETECT_MD_MODE, 114462306a36Sopenharmony_ci V4L2_DETECT_MD_MODE_THRESHOLD_GRID, 0, 114562306a36Sopenharmony_ci V4L2_DETECT_MD_MODE_DISABLED); 114662306a36Sopenharmony_ci v4l2_ctrl_new_std(hdl, &tw5864_ctrl_ops, 114762306a36Sopenharmony_ci V4L2_CID_DETECT_MD_GLOBAL_THRESHOLD, 114862306a36Sopenharmony_ci tw5864_md_thresholds.min, tw5864_md_thresholds.max, 114962306a36Sopenharmony_ci tw5864_md_thresholds.step, tw5864_md_thresholds.def); 115062306a36Sopenharmony_ci input->md_threshold_grid_ctrl = 115162306a36Sopenharmony_ci v4l2_ctrl_new_custom(hdl, &tw5864_md_thresholds, NULL); 115262306a36Sopenharmony_ci if (hdl->error) { 115362306a36Sopenharmony_ci ret = hdl->error; 115462306a36Sopenharmony_ci goto free_v4l2_hdl; 115562306a36Sopenharmony_ci } 115662306a36Sopenharmony_ci input->vdev.ctrl_handler = hdl; 115762306a36Sopenharmony_ci v4l2_ctrl_handler_setup(hdl); 115862306a36Sopenharmony_ci 115962306a36Sopenharmony_ci input->qp = QP_VALUE; 116062306a36Sopenharmony_ci input->gop = GOP_SIZE; 116162306a36Sopenharmony_ci input->frame_interval = 1; 116262306a36Sopenharmony_ci 116362306a36Sopenharmony_ci ret = video_register_device(&input->vdev, VFL_TYPE_VIDEO, video_nr); 116462306a36Sopenharmony_ci if (ret) 116562306a36Sopenharmony_ci goto free_v4l2_hdl; 116662306a36Sopenharmony_ci 116762306a36Sopenharmony_ci dev_info(&input->root->pci->dev, "Registered video device %s\n", 116862306a36Sopenharmony_ci video_device_node_name(&input->vdev)); 116962306a36Sopenharmony_ci 117062306a36Sopenharmony_ci /* 117162306a36Sopenharmony_ci * Set default video standard. Doesn't matter which, the detected value 117262306a36Sopenharmony_ci * will be found out by VIDIOC_QUERYSTD handler. 117362306a36Sopenharmony_ci */ 117462306a36Sopenharmony_ci input->v4l2_std = V4L2_STD_NTSC_M; 117562306a36Sopenharmony_ci input->std = STD_NTSC; 117662306a36Sopenharmony_ci 117762306a36Sopenharmony_ci tw_indir_writeb(TW5864_INDIR_VIN_E(video_nr), 0x07); 117862306a36Sopenharmony_ci /* to initiate auto format recognition */ 117962306a36Sopenharmony_ci tw_indir_writeb(TW5864_INDIR_VIN_F(video_nr), 0xff); 118062306a36Sopenharmony_ci 118162306a36Sopenharmony_ci return 0; 118262306a36Sopenharmony_ci 118362306a36Sopenharmony_cifree_v4l2_hdl: 118462306a36Sopenharmony_ci v4l2_ctrl_handler_free(hdl); 118562306a36Sopenharmony_cifree_mutex: 118662306a36Sopenharmony_ci mutex_destroy(&input->lock); 118762306a36Sopenharmony_ci 118862306a36Sopenharmony_ci return ret; 118962306a36Sopenharmony_ci} 119062306a36Sopenharmony_ci 119162306a36Sopenharmony_cistatic void tw5864_video_input_fini(struct tw5864_input *dev) 119262306a36Sopenharmony_ci{ 119362306a36Sopenharmony_ci vb2_video_unregister_device(&dev->vdev); 119462306a36Sopenharmony_ci v4l2_ctrl_handler_free(&dev->hdl); 119562306a36Sopenharmony_ci} 119662306a36Sopenharmony_ci 119762306a36Sopenharmony_civoid tw5864_video_fini(struct tw5864_dev *dev) 119862306a36Sopenharmony_ci{ 119962306a36Sopenharmony_ci int i; 120062306a36Sopenharmony_ci 120162306a36Sopenharmony_ci tasklet_kill(&dev->tasklet); 120262306a36Sopenharmony_ci 120362306a36Sopenharmony_ci for (i = 0; i < TW5864_INPUTS; i++) 120462306a36Sopenharmony_ci tw5864_video_input_fini(&dev->inputs[i]); 120562306a36Sopenharmony_ci 120662306a36Sopenharmony_ci for (i = 0; i < H264_BUF_CNT; i++) { 120762306a36Sopenharmony_ci dma_free_coherent(&dev->pci->dev, H264_VLC_BUF_SIZE, 120862306a36Sopenharmony_ci dev->h264_buf[i].vlc.addr, 120962306a36Sopenharmony_ci dev->h264_buf[i].vlc.dma_addr); 121062306a36Sopenharmony_ci dma_free_coherent(&dev->pci->dev, H264_MV_BUF_SIZE, 121162306a36Sopenharmony_ci dev->h264_buf[i].mv.addr, 121262306a36Sopenharmony_ci dev->h264_buf[i].mv.dma_addr); 121362306a36Sopenharmony_ci } 121462306a36Sopenharmony_ci} 121562306a36Sopenharmony_ci 121662306a36Sopenharmony_civoid tw5864_prepare_frame_headers(struct tw5864_input *input) 121762306a36Sopenharmony_ci{ 121862306a36Sopenharmony_ci struct tw5864_buf *vb = input->vb; 121962306a36Sopenharmony_ci u8 *dst; 122062306a36Sopenharmony_ci size_t dst_space; 122162306a36Sopenharmony_ci unsigned long flags; 122262306a36Sopenharmony_ci 122362306a36Sopenharmony_ci if (!vb) { 122462306a36Sopenharmony_ci spin_lock_irqsave(&input->slock, flags); 122562306a36Sopenharmony_ci if (list_empty(&input->active)) { 122662306a36Sopenharmony_ci spin_unlock_irqrestore(&input->slock, flags); 122762306a36Sopenharmony_ci input->vb = NULL; 122862306a36Sopenharmony_ci return; 122962306a36Sopenharmony_ci } 123062306a36Sopenharmony_ci vb = list_first_entry(&input->active, struct tw5864_buf, list); 123162306a36Sopenharmony_ci list_del(&vb->list); 123262306a36Sopenharmony_ci spin_unlock_irqrestore(&input->slock, flags); 123362306a36Sopenharmony_ci } 123462306a36Sopenharmony_ci 123562306a36Sopenharmony_ci dst = vb2_plane_vaddr(&vb->vb.vb2_buf, 0); 123662306a36Sopenharmony_ci dst_space = vb2_plane_size(&vb->vb.vb2_buf, 0); 123762306a36Sopenharmony_ci 123862306a36Sopenharmony_ci /* 123962306a36Sopenharmony_ci * Low-level bitstream writing functions don't have a fine way to say 124062306a36Sopenharmony_ci * correctly that supplied buffer is too small. So we just check there 124162306a36Sopenharmony_ci * and warn, and don't care at lower level. 124262306a36Sopenharmony_ci * Currently all headers take below 32 bytes. 124362306a36Sopenharmony_ci * The buffer is supposed to have plenty of free space at this point, 124462306a36Sopenharmony_ci * anyway. 124562306a36Sopenharmony_ci */ 124662306a36Sopenharmony_ci if (WARN_ON_ONCE(dst_space < 128)) 124762306a36Sopenharmony_ci return; 124862306a36Sopenharmony_ci 124962306a36Sopenharmony_ci /* 125062306a36Sopenharmony_ci * Generate H264 headers: 125162306a36Sopenharmony_ci * If this is first frame, put SPS and PPS 125262306a36Sopenharmony_ci */ 125362306a36Sopenharmony_ci if (input->frame_gop_seqno == 0) 125462306a36Sopenharmony_ci tw5864_h264_put_stream_header(&dst, &dst_space, input->qp, 125562306a36Sopenharmony_ci input->width, input->height); 125662306a36Sopenharmony_ci 125762306a36Sopenharmony_ci /* Put slice header */ 125862306a36Sopenharmony_ci tw5864_h264_put_slice_header(&dst, &dst_space, input->h264_idr_pic_id, 125962306a36Sopenharmony_ci input->frame_gop_seqno, 126062306a36Sopenharmony_ci &input->tail_nb_bits, &input->tail); 126162306a36Sopenharmony_ci input->vb = vb; 126262306a36Sopenharmony_ci input->buf_cur_ptr = dst; 126362306a36Sopenharmony_ci input->buf_cur_space_left = dst_space; 126462306a36Sopenharmony_ci} 126562306a36Sopenharmony_ci 126662306a36Sopenharmony_ci/* 126762306a36Sopenharmony_ci * Returns heuristic motion detection metric value from known components of 126862306a36Sopenharmony_ci * hardware-provided Motion Vector Data. 126962306a36Sopenharmony_ci */ 127062306a36Sopenharmony_cistatic unsigned int tw5864_md_metric_from_mvd(u32 mvd) 127162306a36Sopenharmony_ci{ 127262306a36Sopenharmony_ci /* 127362306a36Sopenharmony_ci * Format of motion vector data exposed by tw5864, according to 127462306a36Sopenharmony_ci * manufacturer: 127562306a36Sopenharmony_ci * mv_x 10 bits 127662306a36Sopenharmony_ci * mv_y 10 bits 127762306a36Sopenharmony_ci * non_zero_members 8 bits 127862306a36Sopenharmony_ci * mb_type 3 bits 127962306a36Sopenharmony_ci * reserved 1 bit 128062306a36Sopenharmony_ci * 128162306a36Sopenharmony_ci * non_zero_members: number of non-zero residuals in each macro block 128262306a36Sopenharmony_ci * after quantization 128362306a36Sopenharmony_ci * 128462306a36Sopenharmony_ci * unsigned int reserved = mvd >> 31; 128562306a36Sopenharmony_ci * unsigned int mb_type = (mvd >> 28) & 0x7; 128662306a36Sopenharmony_ci * unsigned int non_zero_members = (mvd >> 20) & 0xff; 128762306a36Sopenharmony_ci */ 128862306a36Sopenharmony_ci unsigned int mv_y = (mvd >> 10) & 0x3ff; 128962306a36Sopenharmony_ci unsigned int mv_x = mvd & 0x3ff; 129062306a36Sopenharmony_ci 129162306a36Sopenharmony_ci /* heuristic: */ 129262306a36Sopenharmony_ci mv_x &= 0x0f; 129362306a36Sopenharmony_ci mv_y &= 0x0f; 129462306a36Sopenharmony_ci 129562306a36Sopenharmony_ci return mv_y + mv_x; 129662306a36Sopenharmony_ci} 129762306a36Sopenharmony_ci 129862306a36Sopenharmony_cistatic int tw5864_is_motion_triggered(struct tw5864_h264_frame *frame) 129962306a36Sopenharmony_ci{ 130062306a36Sopenharmony_ci struct tw5864_input *input = frame->input; 130162306a36Sopenharmony_ci u32 *mv = (u32 *)frame->mv.addr; 130262306a36Sopenharmony_ci int i; 130362306a36Sopenharmony_ci int detected = 0; 130462306a36Sopenharmony_ci 130562306a36Sopenharmony_ci for (i = 0; i < MD_CELLS; i++) { 130662306a36Sopenharmony_ci const u16 thresh = input->md_threshold_grid_values[i]; 130762306a36Sopenharmony_ci const unsigned int metric = tw5864_md_metric_from_mvd(mv[i]); 130862306a36Sopenharmony_ci 130962306a36Sopenharmony_ci if (metric > thresh) 131062306a36Sopenharmony_ci detected = 1; 131162306a36Sopenharmony_ci 131262306a36Sopenharmony_ci if (detected) 131362306a36Sopenharmony_ci break; 131462306a36Sopenharmony_ci } 131562306a36Sopenharmony_ci return detected; 131662306a36Sopenharmony_ci} 131762306a36Sopenharmony_ci 131862306a36Sopenharmony_cistatic void tw5864_handle_frame_task(struct tasklet_struct *t) 131962306a36Sopenharmony_ci{ 132062306a36Sopenharmony_ci struct tw5864_dev *dev = from_tasklet(dev, t, tasklet); 132162306a36Sopenharmony_ci unsigned long flags; 132262306a36Sopenharmony_ci int batch_size = H264_BUF_CNT; 132362306a36Sopenharmony_ci 132462306a36Sopenharmony_ci spin_lock_irqsave(&dev->slock, flags); 132562306a36Sopenharmony_ci while (dev->h264_buf_r_index != dev->h264_buf_w_index && batch_size--) { 132662306a36Sopenharmony_ci struct tw5864_h264_frame *frame = 132762306a36Sopenharmony_ci &dev->h264_buf[dev->h264_buf_r_index]; 132862306a36Sopenharmony_ci 132962306a36Sopenharmony_ci spin_unlock_irqrestore(&dev->slock, flags); 133062306a36Sopenharmony_ci dma_sync_single_for_cpu(&dev->pci->dev, frame->vlc.dma_addr, 133162306a36Sopenharmony_ci H264_VLC_BUF_SIZE, DMA_FROM_DEVICE); 133262306a36Sopenharmony_ci dma_sync_single_for_cpu(&dev->pci->dev, frame->mv.dma_addr, 133362306a36Sopenharmony_ci H264_MV_BUF_SIZE, DMA_FROM_DEVICE); 133462306a36Sopenharmony_ci tw5864_handle_frame(frame); 133562306a36Sopenharmony_ci dma_sync_single_for_device(&dev->pci->dev, frame->vlc.dma_addr, 133662306a36Sopenharmony_ci H264_VLC_BUF_SIZE, DMA_FROM_DEVICE); 133762306a36Sopenharmony_ci dma_sync_single_for_device(&dev->pci->dev, frame->mv.dma_addr, 133862306a36Sopenharmony_ci H264_MV_BUF_SIZE, DMA_FROM_DEVICE); 133962306a36Sopenharmony_ci spin_lock_irqsave(&dev->slock, flags); 134062306a36Sopenharmony_ci 134162306a36Sopenharmony_ci dev->h264_buf_r_index++; 134262306a36Sopenharmony_ci dev->h264_buf_r_index %= H264_BUF_CNT; 134362306a36Sopenharmony_ci } 134462306a36Sopenharmony_ci spin_unlock_irqrestore(&dev->slock, flags); 134562306a36Sopenharmony_ci} 134662306a36Sopenharmony_ci 134762306a36Sopenharmony_ci#ifdef DEBUG 134862306a36Sopenharmony_cistatic u32 tw5864_vlc_checksum(u32 *data, int len) 134962306a36Sopenharmony_ci{ 135062306a36Sopenharmony_ci u32 val, count_len = len; 135162306a36Sopenharmony_ci 135262306a36Sopenharmony_ci val = *data++; 135362306a36Sopenharmony_ci while (((count_len >> 2) - 1) > 0) { 135462306a36Sopenharmony_ci val ^= *data++; 135562306a36Sopenharmony_ci count_len -= 4; 135662306a36Sopenharmony_ci } 135762306a36Sopenharmony_ci val ^= htonl((len >> 2)); 135862306a36Sopenharmony_ci return val; 135962306a36Sopenharmony_ci} 136062306a36Sopenharmony_ci#endif 136162306a36Sopenharmony_ci 136262306a36Sopenharmony_cistatic void tw5864_handle_frame(struct tw5864_h264_frame *frame) 136362306a36Sopenharmony_ci{ 136462306a36Sopenharmony_ci#define SKIP_VLCBUF_BYTES 3 136562306a36Sopenharmony_ci struct tw5864_input *input = frame->input; 136662306a36Sopenharmony_ci struct tw5864_dev *dev = input->root; 136762306a36Sopenharmony_ci struct tw5864_buf *vb; 136862306a36Sopenharmony_ci struct vb2_v4l2_buffer *v4l2_buf; 136962306a36Sopenharmony_ci int frame_len = frame->vlc_len - SKIP_VLCBUF_BYTES; 137062306a36Sopenharmony_ci u8 *dst = input->buf_cur_ptr; 137162306a36Sopenharmony_ci u8 tail_mask, vlc_mask = 0; 137262306a36Sopenharmony_ci int i; 137362306a36Sopenharmony_ci u8 vlc_first_byte = ((u8 *)(frame->vlc.addr + SKIP_VLCBUF_BYTES))[0]; 137462306a36Sopenharmony_ci unsigned long flags; 137562306a36Sopenharmony_ci int zero_run; 137662306a36Sopenharmony_ci u8 *src; 137762306a36Sopenharmony_ci u8 *src_end; 137862306a36Sopenharmony_ci 137962306a36Sopenharmony_ci#ifdef DEBUG 138062306a36Sopenharmony_ci if (frame->checksum != 138162306a36Sopenharmony_ci tw5864_vlc_checksum((u32 *)frame->vlc.addr, frame_len)) 138262306a36Sopenharmony_ci dev_err(&dev->pci->dev, 138362306a36Sopenharmony_ci "Checksum of encoded frame doesn't match!\n"); 138462306a36Sopenharmony_ci#endif 138562306a36Sopenharmony_ci 138662306a36Sopenharmony_ci spin_lock_irqsave(&input->slock, flags); 138762306a36Sopenharmony_ci vb = input->vb; 138862306a36Sopenharmony_ci input->vb = NULL; 138962306a36Sopenharmony_ci spin_unlock_irqrestore(&input->slock, flags); 139062306a36Sopenharmony_ci 139162306a36Sopenharmony_ci if (!vb) { /* Gone because of disabling */ 139262306a36Sopenharmony_ci dev_dbg(&dev->pci->dev, "vb is empty, dropping frame\n"); 139362306a36Sopenharmony_ci return; 139462306a36Sopenharmony_ci } 139562306a36Sopenharmony_ci 139662306a36Sopenharmony_ci v4l2_buf = to_vb2_v4l2_buffer(&vb->vb.vb2_buf); 139762306a36Sopenharmony_ci 139862306a36Sopenharmony_ci /* 139962306a36Sopenharmony_ci * Check for space. 140062306a36Sopenharmony_ci * Mind the overhead of startcode emulation prevention. 140162306a36Sopenharmony_ci */ 140262306a36Sopenharmony_ci if (input->buf_cur_space_left < frame_len * 5 / 4) { 140362306a36Sopenharmony_ci dev_err_once(&dev->pci->dev, 140462306a36Sopenharmony_ci "Left space in vb2 buffer, %d bytes, is less than considered safely enough to put frame of length %d. Dropping this frame.\n", 140562306a36Sopenharmony_ci input->buf_cur_space_left, frame_len); 140662306a36Sopenharmony_ci return; 140762306a36Sopenharmony_ci } 140862306a36Sopenharmony_ci 140962306a36Sopenharmony_ci for (i = 0; i < 8 - input->tail_nb_bits; i++) 141062306a36Sopenharmony_ci vlc_mask |= 1 << i; 141162306a36Sopenharmony_ci tail_mask = (~vlc_mask) & 0xff; 141262306a36Sopenharmony_ci 141362306a36Sopenharmony_ci dst[0] = (input->tail & tail_mask) | (vlc_first_byte & vlc_mask); 141462306a36Sopenharmony_ci frame_len--; 141562306a36Sopenharmony_ci dst++; 141662306a36Sopenharmony_ci 141762306a36Sopenharmony_ci /* H.264 startcode emulation prevention */ 141862306a36Sopenharmony_ci src = frame->vlc.addr + SKIP_VLCBUF_BYTES + 1; 141962306a36Sopenharmony_ci src_end = src + frame_len; 142062306a36Sopenharmony_ci zero_run = 0; 142162306a36Sopenharmony_ci for (; src < src_end; src++) { 142262306a36Sopenharmony_ci if (zero_run < 2) { 142362306a36Sopenharmony_ci if (*src == 0) 142462306a36Sopenharmony_ci ++zero_run; 142562306a36Sopenharmony_ci else 142662306a36Sopenharmony_ci zero_run = 0; 142762306a36Sopenharmony_ci } else { 142862306a36Sopenharmony_ci if ((*src & ~0x03) == 0) 142962306a36Sopenharmony_ci *dst++ = 0x03; 143062306a36Sopenharmony_ci zero_run = *src == 0; 143162306a36Sopenharmony_ci } 143262306a36Sopenharmony_ci *dst++ = *src; 143362306a36Sopenharmony_ci } 143462306a36Sopenharmony_ci 143562306a36Sopenharmony_ci vb2_set_plane_payload(&vb->vb.vb2_buf, 0, 143662306a36Sopenharmony_ci dst - (u8 *)vb2_plane_vaddr(&vb->vb.vb2_buf, 0)); 143762306a36Sopenharmony_ci 143862306a36Sopenharmony_ci vb->vb.vb2_buf.timestamp = frame->timestamp; 143962306a36Sopenharmony_ci v4l2_buf->field = V4L2_FIELD_INTERLACED; 144062306a36Sopenharmony_ci v4l2_buf->sequence = frame->seqno; 144162306a36Sopenharmony_ci 144262306a36Sopenharmony_ci /* Check for motion flags */ 144362306a36Sopenharmony_ci if (frame->gop_seqno /* P-frame */ && 144462306a36Sopenharmony_ci tw5864_is_motion_triggered(frame)) { 144562306a36Sopenharmony_ci struct v4l2_event ev = { 144662306a36Sopenharmony_ci .type = V4L2_EVENT_MOTION_DET, 144762306a36Sopenharmony_ci .u.motion_det = { 144862306a36Sopenharmony_ci .flags = V4L2_EVENT_MD_FL_HAVE_FRAME_SEQ, 144962306a36Sopenharmony_ci .frame_sequence = v4l2_buf->sequence, 145062306a36Sopenharmony_ci }, 145162306a36Sopenharmony_ci }; 145262306a36Sopenharmony_ci 145362306a36Sopenharmony_ci v4l2_event_queue(&input->vdev, &ev); 145462306a36Sopenharmony_ci } 145562306a36Sopenharmony_ci 145662306a36Sopenharmony_ci vb2_buffer_done(&vb->vb.vb2_buf, VB2_BUF_STATE_DONE); 145762306a36Sopenharmony_ci} 145862306a36Sopenharmony_ci 145962306a36Sopenharmony_cistatic v4l2_std_id tw5864_get_v4l2_std(enum tw5864_vid_std std) 146062306a36Sopenharmony_ci{ 146162306a36Sopenharmony_ci switch (std) { 146262306a36Sopenharmony_ci case STD_NTSC: return V4L2_STD_NTSC_M; 146362306a36Sopenharmony_ci case STD_PAL: return V4L2_STD_PAL_B; 146462306a36Sopenharmony_ci case STD_SECAM: return V4L2_STD_SECAM_B; 146562306a36Sopenharmony_ci case STD_NTSC443: return V4L2_STD_NTSC_443; 146662306a36Sopenharmony_ci case STD_PAL_M: return V4L2_STD_PAL_M; 146762306a36Sopenharmony_ci case STD_PAL_CN: return V4L2_STD_PAL_Nc; 146862306a36Sopenharmony_ci case STD_PAL_60: return V4L2_STD_PAL_60; 146962306a36Sopenharmony_ci case STD_INVALID: return V4L2_STD_UNKNOWN; 147062306a36Sopenharmony_ci } 147162306a36Sopenharmony_ci return 0; 147262306a36Sopenharmony_ci} 147362306a36Sopenharmony_ci 147462306a36Sopenharmony_cistatic enum tw5864_vid_std tw5864_from_v4l2_std(v4l2_std_id v4l2_std) 147562306a36Sopenharmony_ci{ 147662306a36Sopenharmony_ci if (v4l2_std & V4L2_STD_NTSC_M) 147762306a36Sopenharmony_ci return STD_NTSC; 147862306a36Sopenharmony_ci if (v4l2_std & V4L2_STD_PAL_B) 147962306a36Sopenharmony_ci return STD_PAL; 148062306a36Sopenharmony_ci if (v4l2_std & V4L2_STD_SECAM_B) 148162306a36Sopenharmony_ci return STD_SECAM; 148262306a36Sopenharmony_ci if (v4l2_std & V4L2_STD_NTSC_443) 148362306a36Sopenharmony_ci return STD_NTSC443; 148462306a36Sopenharmony_ci if (v4l2_std & V4L2_STD_PAL_M) 148562306a36Sopenharmony_ci return STD_PAL_M; 148662306a36Sopenharmony_ci if (v4l2_std & V4L2_STD_PAL_Nc) 148762306a36Sopenharmony_ci return STD_PAL_CN; 148862306a36Sopenharmony_ci if (v4l2_std & V4L2_STD_PAL_60) 148962306a36Sopenharmony_ci return STD_PAL_60; 149062306a36Sopenharmony_ci 149162306a36Sopenharmony_ci return STD_INVALID; 149262306a36Sopenharmony_ci} 149362306a36Sopenharmony_ci 149462306a36Sopenharmony_cistatic void tw5864_encoder_tables_upload(struct tw5864_dev *dev) 149562306a36Sopenharmony_ci{ 149662306a36Sopenharmony_ci int i; 149762306a36Sopenharmony_ci 149862306a36Sopenharmony_ci tw_writel(TW5864_VLC_RD, 0x1); 149962306a36Sopenharmony_ci for (i = 0; i < VLC_LOOKUP_TABLE_LEN; i++) { 150062306a36Sopenharmony_ci tw_writel((TW5864_VLC_STREAM_MEM_START + i * 4), 150162306a36Sopenharmony_ci encoder_vlc_lookup_table[i]); 150262306a36Sopenharmony_ci } 150362306a36Sopenharmony_ci tw_writel(TW5864_VLC_RD, 0x0); 150462306a36Sopenharmony_ci 150562306a36Sopenharmony_ci for (i = 0; i < QUANTIZATION_TABLE_LEN; i++) { 150662306a36Sopenharmony_ci tw_writel((TW5864_QUAN_TAB + i * 4), 150762306a36Sopenharmony_ci forward_quantization_table[i]); 150862306a36Sopenharmony_ci } 150962306a36Sopenharmony_ci 151062306a36Sopenharmony_ci for (i = 0; i < QUANTIZATION_TABLE_LEN; i++) { 151162306a36Sopenharmony_ci tw_writel((TW5864_QUAN_TAB + i * 4), 151262306a36Sopenharmony_ci inverse_quantization_table[i]); 151362306a36Sopenharmony_ci } 151462306a36Sopenharmony_ci} 1515