1*ba07fd2fSBen Hoff // SPDX-License-Identifier: GPL-2.0-only 2*ba07fd2fSBen Hoff #include <linux/kernel.h> 3*ba07fd2fSBen Hoff #include <linux/string.h> 4*ba07fd2fSBen Hoff #include <linux/pci.h> 5*ba07fd2fSBen Hoff #include <linux/errno.h> 6*ba07fd2fSBen Hoff #include <linux/io.h> 7*ba07fd2fSBen Hoff #include <linux/math64.h> 8*ba07fd2fSBen Hoff 9*ba07fd2fSBen Hoff #include <media/v4l2-ioctl.h> 10*ba07fd2fSBen Hoff #include <media/v4l2-dev.h> 11*ba07fd2fSBen Hoff #include <media/v4l2-dv-timings.h> 12*ba07fd2fSBen Hoff #include <media/videobuf2-core.h> 13*ba07fd2fSBen Hoff #include <media/videobuf2-v4l2.h> 14*ba07fd2fSBen Hoff 15*ba07fd2fSBen Hoff #include "hws.h" 16*ba07fd2fSBen Hoff #include "hws_reg.h" 17*ba07fd2fSBen Hoff #include "hws_video.h" 18*ba07fd2fSBen Hoff #include "hws_v4l2_ioctl.h" 19*ba07fd2fSBen Hoff 20*ba07fd2fSBen Hoff struct hws_dv_mode { 21*ba07fd2fSBen Hoff struct v4l2_dv_timings timings; 22*ba07fd2fSBen Hoff u32 refresh_hz; 23*ba07fd2fSBen Hoff }; 24*ba07fd2fSBen Hoff 25*ba07fd2fSBen Hoff static const struct hws_dv_mode * 26*ba07fd2fSBen Hoff hws_find_dv_by_wh(u32 w, u32 h, bool interlaced); 27*ba07fd2fSBen Hoff static const struct hws_dv_mode * 28*ba07fd2fSBen Hoff hws_find_dv_by_wh_fps(u32 w, u32 h, bool interlaced, u32 fps); 29*ba07fd2fSBen Hoff static u32 hws_get_live_fps(struct hws_video *vid); 30*ba07fd2fSBen Hoff static u32 hws_input_status(struct hws_video *vid); 31*ba07fd2fSBen Hoff static int hws_fill_dv_timings(u32 w, u32 h, bool interlace, u32 fps, 32*ba07fd2fSBen Hoff struct v4l2_dv_timings *timings); 33*ba07fd2fSBen Hoff 34*ba07fd2fSBen Hoff static const struct hws_dv_mode hws_dv_modes[] = { 35*ba07fd2fSBen Hoff { 36*ba07fd2fSBen Hoff { 37*ba07fd2fSBen Hoff .type = V4L2_DV_BT_656_1120, 38*ba07fd2fSBen Hoff .bt = { 39*ba07fd2fSBen Hoff .width = 1920, 40*ba07fd2fSBen Hoff .height = 1080, 41*ba07fd2fSBen Hoff .hfrontporch = 88, 42*ba07fd2fSBen Hoff .hsync = 44, 43*ba07fd2fSBen Hoff .hbackporch = 148, 44*ba07fd2fSBen Hoff .vfrontporch = 4, 45*ba07fd2fSBen Hoff .vsync = 5, 46*ba07fd2fSBen Hoff .vbackporch = 36, 47*ba07fd2fSBen Hoff .pixelclock = 148500000, 48*ba07fd2fSBen Hoff .polarities = V4L2_DV_VSYNC_POS_POL | 49*ba07fd2fSBen Hoff V4L2_DV_HSYNC_POS_POL, 50*ba07fd2fSBen Hoff .interlaced = 0, 51*ba07fd2fSBen Hoff }, 52*ba07fd2fSBen Hoff }, 53*ba07fd2fSBen Hoff 60, 54*ba07fd2fSBen Hoff }, 55*ba07fd2fSBen Hoff { 56*ba07fd2fSBen Hoff { 57*ba07fd2fSBen Hoff .type = V4L2_DV_BT_656_1120, 58*ba07fd2fSBen Hoff .bt = { 59*ba07fd2fSBen Hoff .width = 1920, 60*ba07fd2fSBen Hoff .height = 1080, 61*ba07fd2fSBen Hoff .hfrontporch = 88, 62*ba07fd2fSBen Hoff .hsync = 44, 63*ba07fd2fSBen Hoff .hbackporch = 148, 64*ba07fd2fSBen Hoff .vfrontporch = 4, 65*ba07fd2fSBen Hoff .vsync = 5, 66*ba07fd2fSBen Hoff .vbackporch = 36, 67*ba07fd2fSBen Hoff .pixelclock = 74250000, 68*ba07fd2fSBen Hoff .polarities = V4L2_DV_VSYNC_POS_POL | 69*ba07fd2fSBen Hoff V4L2_DV_HSYNC_POS_POL, 70*ba07fd2fSBen Hoff .interlaced = 0, 71*ba07fd2fSBen Hoff }, 72*ba07fd2fSBen Hoff }, 73*ba07fd2fSBen Hoff 30, 74*ba07fd2fSBen Hoff }, 75*ba07fd2fSBen Hoff { 76*ba07fd2fSBen Hoff { 77*ba07fd2fSBen Hoff .type = V4L2_DV_BT_656_1120, 78*ba07fd2fSBen Hoff .bt = { 79*ba07fd2fSBen Hoff .width = 1280, 80*ba07fd2fSBen Hoff .height = 720, 81*ba07fd2fSBen Hoff .hfrontporch = 110, 82*ba07fd2fSBen Hoff .hsync = 40, 83*ba07fd2fSBen Hoff .hbackporch = 220, 84*ba07fd2fSBen Hoff .vfrontporch = 5, 85*ba07fd2fSBen Hoff .vsync = 5, 86*ba07fd2fSBen Hoff .vbackporch = 20, 87*ba07fd2fSBen Hoff .pixelclock = 74250000, 88*ba07fd2fSBen Hoff .polarities = V4L2_DV_VSYNC_POS_POL | 89*ba07fd2fSBen Hoff V4L2_DV_HSYNC_POS_POL, 90*ba07fd2fSBen Hoff .interlaced = 0, 91*ba07fd2fSBen Hoff }, 92*ba07fd2fSBen Hoff }, 93*ba07fd2fSBen Hoff 60, 94*ba07fd2fSBen Hoff }, 95*ba07fd2fSBen Hoff { 96*ba07fd2fSBen Hoff { 97*ba07fd2fSBen Hoff .type = V4L2_DV_BT_656_1120, 98*ba07fd2fSBen Hoff .bt = { 99*ba07fd2fSBen Hoff .width = 720, 100*ba07fd2fSBen Hoff .height = 480, 101*ba07fd2fSBen Hoff .interlaced = 0, 102*ba07fd2fSBen Hoff }, 103*ba07fd2fSBen Hoff }, 104*ba07fd2fSBen Hoff 60, 105*ba07fd2fSBen Hoff }, 106*ba07fd2fSBen Hoff { 107*ba07fd2fSBen Hoff { 108*ba07fd2fSBen Hoff .type = V4L2_DV_BT_656_1120, 109*ba07fd2fSBen Hoff .bt = { 110*ba07fd2fSBen Hoff .width = 720, 111*ba07fd2fSBen Hoff .height = 576, 112*ba07fd2fSBen Hoff .interlaced = 0, 113*ba07fd2fSBen Hoff }, 114*ba07fd2fSBen Hoff }, 115*ba07fd2fSBen Hoff 50, 116*ba07fd2fSBen Hoff }, 117*ba07fd2fSBen Hoff { 118*ba07fd2fSBen Hoff { 119*ba07fd2fSBen Hoff .type = V4L2_DV_BT_656_1120, 120*ba07fd2fSBen Hoff .bt = { 121*ba07fd2fSBen Hoff .width = 800, 122*ba07fd2fSBen Hoff .height = 600, 123*ba07fd2fSBen Hoff .interlaced = 0, 124*ba07fd2fSBen Hoff }, 125*ba07fd2fSBen Hoff }, 126*ba07fd2fSBen Hoff 60, 127*ba07fd2fSBen Hoff }, 128*ba07fd2fSBen Hoff { 129*ba07fd2fSBen Hoff { 130*ba07fd2fSBen Hoff .type = V4L2_DV_BT_656_1120, 131*ba07fd2fSBen Hoff .bt = { 132*ba07fd2fSBen Hoff .width = 640, 133*ba07fd2fSBen Hoff .height = 480, 134*ba07fd2fSBen Hoff .interlaced = 0, 135*ba07fd2fSBen Hoff }, 136*ba07fd2fSBen Hoff }, 137*ba07fd2fSBen Hoff 60, 138*ba07fd2fSBen Hoff }, 139*ba07fd2fSBen Hoff { 140*ba07fd2fSBen Hoff { 141*ba07fd2fSBen Hoff .type = V4L2_DV_BT_656_1120, 142*ba07fd2fSBen Hoff .bt = { 143*ba07fd2fSBen Hoff .width = 1024, 144*ba07fd2fSBen Hoff .height = 768, 145*ba07fd2fSBen Hoff .interlaced = 0, 146*ba07fd2fSBen Hoff }, 147*ba07fd2fSBen Hoff }, 148*ba07fd2fSBen Hoff 60, 149*ba07fd2fSBen Hoff }, 150*ba07fd2fSBen Hoff { 151*ba07fd2fSBen Hoff { 152*ba07fd2fSBen Hoff .type = V4L2_DV_BT_656_1120, 153*ba07fd2fSBen Hoff .bt = { 154*ba07fd2fSBen Hoff .width = 1280, 155*ba07fd2fSBen Hoff .height = 768, 156*ba07fd2fSBen Hoff .interlaced = 0, 157*ba07fd2fSBen Hoff }, 158*ba07fd2fSBen Hoff }, 159*ba07fd2fSBen Hoff 60, 160*ba07fd2fSBen Hoff }, 161*ba07fd2fSBen Hoff { 162*ba07fd2fSBen Hoff { 163*ba07fd2fSBen Hoff .type = V4L2_DV_BT_656_1120, 164*ba07fd2fSBen Hoff .bt = { 165*ba07fd2fSBen Hoff .width = 1280, 166*ba07fd2fSBen Hoff .height = 800, 167*ba07fd2fSBen Hoff .interlaced = 0, 168*ba07fd2fSBen Hoff }, 169*ba07fd2fSBen Hoff }, 170*ba07fd2fSBen Hoff 60, 171*ba07fd2fSBen Hoff }, 172*ba07fd2fSBen Hoff { 173*ba07fd2fSBen Hoff { 174*ba07fd2fSBen Hoff .type = V4L2_DV_BT_656_1120, 175*ba07fd2fSBen Hoff .bt = { 176*ba07fd2fSBen Hoff .width = 1280, 177*ba07fd2fSBen Hoff .height = 1024, 178*ba07fd2fSBen Hoff .interlaced = 0, 179*ba07fd2fSBen Hoff }, 180*ba07fd2fSBen Hoff }, 181*ba07fd2fSBen Hoff 60, 182*ba07fd2fSBen Hoff }, 183*ba07fd2fSBen Hoff { 184*ba07fd2fSBen Hoff { 185*ba07fd2fSBen Hoff .type = V4L2_DV_BT_656_1120, 186*ba07fd2fSBen Hoff .bt = { 187*ba07fd2fSBen Hoff .width = 1360, 188*ba07fd2fSBen Hoff .height = 768, 189*ba07fd2fSBen Hoff .interlaced = 0, 190*ba07fd2fSBen Hoff }, 191*ba07fd2fSBen Hoff }, 192*ba07fd2fSBen Hoff 60, 193*ba07fd2fSBen Hoff }, 194*ba07fd2fSBen Hoff { 195*ba07fd2fSBen Hoff { 196*ba07fd2fSBen Hoff .type = V4L2_DV_BT_656_1120, 197*ba07fd2fSBen Hoff .bt = { 198*ba07fd2fSBen Hoff .width = 1440, 199*ba07fd2fSBen Hoff .height = 900, 200*ba07fd2fSBen Hoff .interlaced = 0, 201*ba07fd2fSBen Hoff }, 202*ba07fd2fSBen Hoff }, 203*ba07fd2fSBen Hoff 60, 204*ba07fd2fSBen Hoff }, 205*ba07fd2fSBen Hoff { 206*ba07fd2fSBen Hoff { 207*ba07fd2fSBen Hoff .type = V4L2_DV_BT_656_1120, 208*ba07fd2fSBen Hoff .bt = { 209*ba07fd2fSBen Hoff .width = 1680, 210*ba07fd2fSBen Hoff .height = 1050, 211*ba07fd2fSBen Hoff .interlaced = 0, 212*ba07fd2fSBen Hoff }, 213*ba07fd2fSBen Hoff }, 214*ba07fd2fSBen Hoff 60, 215*ba07fd2fSBen Hoff }, 216*ba07fd2fSBen Hoff /* Portrait */ 217*ba07fd2fSBen Hoff { 218*ba07fd2fSBen Hoff { 219*ba07fd2fSBen Hoff .type = V4L2_DV_BT_656_1120, 220*ba07fd2fSBen Hoff .bt = { 221*ba07fd2fSBen Hoff .width = 1080, 222*ba07fd2fSBen Hoff .height = 1920, 223*ba07fd2fSBen Hoff .interlaced = 0, 224*ba07fd2fSBen Hoff }, 225*ba07fd2fSBen Hoff }, 226*ba07fd2fSBen Hoff 60, 227*ba07fd2fSBen Hoff }, 228*ba07fd2fSBen Hoff }; 229*ba07fd2fSBen Hoff 230*ba07fd2fSBen Hoff static const size_t hws_dv_modes_cnt = ARRAY_SIZE(hws_dv_modes); 231*ba07fd2fSBen Hoff 232*ba07fd2fSBen Hoff /* YUYV: 16 bpp; align to 64 as you did elsewhere */ 233*ba07fd2fSBen Hoff static inline u32 hws_calc_bpl_yuyv(u32 w) { return ALIGN(w * 2, 64); } 234*ba07fd2fSBen Hoff static inline u32 hws_calc_size_yuyv(u32 w, u32 h) { return hws_calc_bpl_yuyv(w) * h; } 235*ba07fd2fSBen Hoff static inline u32 hws_calc_half_size(u32 sizeimage) 236*ba07fd2fSBen Hoff { 237*ba07fd2fSBen Hoff return sizeimage / 2; 238*ba07fd2fSBen Hoff } 239*ba07fd2fSBen Hoff 240*ba07fd2fSBen Hoff static inline void hws_hw_write_bchs(struct hws_pcie_dev *hws, unsigned int ch, 241*ba07fd2fSBen Hoff u8 br, u8 co, u8 hu, u8 sa) 242*ba07fd2fSBen Hoff { 243*ba07fd2fSBen Hoff u32 packed = (sa << 24) | (hu << 16) | (co << 8) | br; 244*ba07fd2fSBen Hoff 245*ba07fd2fSBen Hoff if (!hws || !hws->bar0_base || ch >= hws->max_channels) 246*ba07fd2fSBen Hoff return; 247*ba07fd2fSBen Hoff writel_relaxed(packed, hws->bar0_base + HWS_REG_BCHS(ch)); 248*ba07fd2fSBen Hoff (void)readl(hws->bar0_base + HWS_REG_BCHS(ch)); /* post write */ 249*ba07fd2fSBen Hoff } 250*ba07fd2fSBen Hoff 251*ba07fd2fSBen Hoff /* Helper: find a supported DV mode by W/H + interlace flag */ 252*ba07fd2fSBen Hoff static const struct hws_dv_mode * 253*ba07fd2fSBen Hoff hws_match_supported_dv(const struct v4l2_dv_timings *req) 254*ba07fd2fSBen Hoff { 255*ba07fd2fSBen Hoff const struct v4l2_bt_timings *bt; 256*ba07fd2fSBen Hoff u32 fps; 257*ba07fd2fSBen Hoff 258*ba07fd2fSBen Hoff if (!req || req->type != V4L2_DV_BT_656_1120) 259*ba07fd2fSBen Hoff return NULL; 260*ba07fd2fSBen Hoff 261*ba07fd2fSBen Hoff bt = &req->bt; 262*ba07fd2fSBen Hoff fps = 0; 263*ba07fd2fSBen Hoff if (bt->pixelclock) { 264*ba07fd2fSBen Hoff u32 total_w = bt->width + bt->hfrontporch + bt->hsync + 265*ba07fd2fSBen Hoff bt->hbackporch; 266*ba07fd2fSBen Hoff u32 total_h = bt->height + bt->vfrontporch + bt->vsync + 267*ba07fd2fSBen Hoff bt->vbackporch; 268*ba07fd2fSBen Hoff 269*ba07fd2fSBen Hoff if (total_w && total_h) 270*ba07fd2fSBen Hoff fps = DIV_ROUND_CLOSEST_ULL((u64)bt->pixelclock, 271*ba07fd2fSBen Hoff (u64)total_w * total_h); 272*ba07fd2fSBen Hoff } 273*ba07fd2fSBen Hoff if (fps) { 274*ba07fd2fSBen Hoff const struct hws_dv_mode *exact = 275*ba07fd2fSBen Hoff hws_find_dv_by_wh_fps(bt->width, bt->height, 276*ba07fd2fSBen Hoff !!bt->interlaced, fps); 277*ba07fd2fSBen Hoff if (exact) 278*ba07fd2fSBen Hoff return exact; 279*ba07fd2fSBen Hoff } 280*ba07fd2fSBen Hoff return hws_find_dv_by_wh(bt->width, bt->height, !!bt->interlaced); 281*ba07fd2fSBen Hoff } 282*ba07fd2fSBen Hoff 283*ba07fd2fSBen Hoff /* Helper: find a supported DV mode by W/H + interlace flag */ 284*ba07fd2fSBen Hoff static const struct hws_dv_mode * 285*ba07fd2fSBen Hoff hws_find_dv_by_wh(u32 w, u32 h, bool interlaced) 286*ba07fd2fSBen Hoff { 287*ba07fd2fSBen Hoff size_t i; 288*ba07fd2fSBen Hoff 289*ba07fd2fSBen Hoff for (i = 0; i < ARRAY_SIZE(hws_dv_modes); i++) { 290*ba07fd2fSBen Hoff const struct hws_dv_mode *t = &hws_dv_modes[i]; 291*ba07fd2fSBen Hoff const struct v4l2_bt_timings *bt = &t->timings.bt; 292*ba07fd2fSBen Hoff 293*ba07fd2fSBen Hoff if (t->timings.type != V4L2_DV_BT_656_1120) 294*ba07fd2fSBen Hoff continue; 295*ba07fd2fSBen Hoff 296*ba07fd2fSBen Hoff if (bt->width == w && bt->height == h && 297*ba07fd2fSBen Hoff !!bt->interlaced == interlaced) 298*ba07fd2fSBen Hoff return t; 299*ba07fd2fSBen Hoff } 300*ba07fd2fSBen Hoff return NULL; 301*ba07fd2fSBen Hoff } 302*ba07fd2fSBen Hoff 303*ba07fd2fSBen Hoff static const struct hws_dv_mode * 304*ba07fd2fSBen Hoff hws_find_dv_by_wh_fps(u32 w, u32 h, bool interlaced, u32 fps) 305*ba07fd2fSBen Hoff { 306*ba07fd2fSBen Hoff size_t i; 307*ba07fd2fSBen Hoff 308*ba07fd2fSBen Hoff for (i = 0; i < ARRAY_SIZE(hws_dv_modes); i++) { 309*ba07fd2fSBen Hoff const struct hws_dv_mode *t = &hws_dv_modes[i]; 310*ba07fd2fSBen Hoff const struct v4l2_bt_timings *bt = &t->timings.bt; 311*ba07fd2fSBen Hoff 312*ba07fd2fSBen Hoff if (t->timings.type != V4L2_DV_BT_656_1120) 313*ba07fd2fSBen Hoff continue; 314*ba07fd2fSBen Hoff 315*ba07fd2fSBen Hoff if (bt->width == w && bt->height == h && 316*ba07fd2fSBen Hoff !!bt->interlaced == interlaced && 317*ba07fd2fSBen Hoff t->refresh_hz == fps) 318*ba07fd2fSBen Hoff return t; 319*ba07fd2fSBen Hoff } 320*ba07fd2fSBen Hoff return NULL; 321*ba07fd2fSBen Hoff } 322*ba07fd2fSBen Hoff 323*ba07fd2fSBen Hoff static bool hws_get_live_dv_geometry(struct hws_video *vid, 324*ba07fd2fSBen Hoff u32 *w, u32 *h, bool *interlaced) 325*ba07fd2fSBen Hoff { 326*ba07fd2fSBen Hoff struct hws_pcie_dev *pdx; 327*ba07fd2fSBen Hoff u32 reg; 328*ba07fd2fSBen Hoff 329*ba07fd2fSBen Hoff if (!vid) 330*ba07fd2fSBen Hoff return false; 331*ba07fd2fSBen Hoff 332*ba07fd2fSBen Hoff pdx = vid->parent; 333*ba07fd2fSBen Hoff if (!pdx || !pdx->bar0_base) 334*ba07fd2fSBen Hoff return false; 335*ba07fd2fSBen Hoff 336*ba07fd2fSBen Hoff reg = readl(pdx->bar0_base + HWS_REG_IN_RES(vid->channel_index)); 337*ba07fd2fSBen Hoff if (!reg || reg == 0xFFFFFFFF) 338*ba07fd2fSBen Hoff return false; 339*ba07fd2fSBen Hoff 340*ba07fd2fSBen Hoff if (w) 341*ba07fd2fSBen Hoff *w = reg & 0xFFFF; 342*ba07fd2fSBen Hoff if (h) 343*ba07fd2fSBen Hoff *h = (reg >> 16) & 0xFFFF; 344*ba07fd2fSBen Hoff if (interlaced) { 345*ba07fd2fSBen Hoff reg = readl(pdx->bar0_base + HWS_REG_ACTIVE_STATUS); 346*ba07fd2fSBen Hoff *interlaced = !!(reg & BIT(8 + vid->channel_index)); 347*ba07fd2fSBen Hoff } 348*ba07fd2fSBen Hoff return true; 349*ba07fd2fSBen Hoff } 350*ba07fd2fSBen Hoff 351*ba07fd2fSBen Hoff static u32 hws_get_live_fps(struct hws_video *vid) 352*ba07fd2fSBen Hoff { 353*ba07fd2fSBen Hoff struct hws_pcie_dev *pdx; 354*ba07fd2fSBen Hoff u32 fps; 355*ba07fd2fSBen Hoff 356*ba07fd2fSBen Hoff if (!vid) 357*ba07fd2fSBen Hoff return 0; 358*ba07fd2fSBen Hoff 359*ba07fd2fSBen Hoff pdx = vid->parent; 360*ba07fd2fSBen Hoff if (!pdx || !pdx->bar0_base) 361*ba07fd2fSBen Hoff return 0; 362*ba07fd2fSBen Hoff 363*ba07fd2fSBen Hoff fps = readl(pdx->bar0_base + HWS_REG_FRAME_RATE(vid->channel_index)); 364*ba07fd2fSBen Hoff if (!fps || fps == 0xFFFFFFFF || fps > 240) 365*ba07fd2fSBen Hoff return 0; 366*ba07fd2fSBen Hoff 367*ba07fd2fSBen Hoff return fps; 368*ba07fd2fSBen Hoff } 369*ba07fd2fSBen Hoff 370*ba07fd2fSBen Hoff static u32 hws_pick_fps_from_mode(u32 w, u32 h, bool interlaced) 371*ba07fd2fSBen Hoff { 372*ba07fd2fSBen Hoff const struct hws_dv_mode *m = hws_find_dv_by_wh(w, h, interlaced); 373*ba07fd2fSBen Hoff 374*ba07fd2fSBen Hoff if (m && m->refresh_hz) 375*ba07fd2fSBen Hoff return m->refresh_hz; 376*ba07fd2fSBen Hoff /* Fallback to a sane default */ 377*ba07fd2fSBen Hoff return 60; 378*ba07fd2fSBen Hoff } 379*ba07fd2fSBen Hoff 380*ba07fd2fSBen Hoff static int hws_fill_dv_timings(u32 w, u32 h, bool interlace, u32 fps, 381*ba07fd2fSBen Hoff struct v4l2_dv_timings *timings) 382*ba07fd2fSBen Hoff { 383*ba07fd2fSBen Hoff const struct hws_dv_mode *m; 384*ba07fd2fSBen Hoff 385*ba07fd2fSBen Hoff m = fps ? hws_find_dv_by_wh_fps(w, h, interlace, fps) : NULL; 386*ba07fd2fSBen Hoff if (!m) 387*ba07fd2fSBen Hoff m = hws_find_dv_by_wh(w, h, interlace); 388*ba07fd2fSBen Hoff if (!m) 389*ba07fd2fSBen Hoff return -ENOLINK; 390*ba07fd2fSBen Hoff 391*ba07fd2fSBen Hoff *timings = m->timings; 392*ba07fd2fSBen Hoff return 0; 393*ba07fd2fSBen Hoff } 394*ba07fd2fSBen Hoff 395*ba07fd2fSBen Hoff static u32 hws_input_status(struct hws_video *vid) 396*ba07fd2fSBen Hoff { 397*ba07fd2fSBen Hoff struct hws_pcie_dev *pdx; 398*ba07fd2fSBen Hoff u32 reg; 399*ba07fd2fSBen Hoff 400*ba07fd2fSBen Hoff if (!vid) 401*ba07fd2fSBen Hoff return V4L2_IN_ST_NO_SIGNAL; 402*ba07fd2fSBen Hoff 403*ba07fd2fSBen Hoff pdx = vid->parent; 404*ba07fd2fSBen Hoff if (!pdx || !pdx->bar0_base) 405*ba07fd2fSBen Hoff return V4L2_IN_ST_NO_SIGNAL; 406*ba07fd2fSBen Hoff 407*ba07fd2fSBen Hoff reg = readl(pdx->bar0_base + HWS_REG_ACTIVE_STATUS); 408*ba07fd2fSBen Hoff if (reg == 0xffffffff) 409*ba07fd2fSBen Hoff return V4L2_IN_ST_NO_SIGNAL; 410*ba07fd2fSBen Hoff 411*ba07fd2fSBen Hoff return (reg & BIT(vid->channel_index)) ? 0 : V4L2_IN_ST_NO_SIGNAL; 412*ba07fd2fSBen Hoff } 413*ba07fd2fSBen Hoff 414*ba07fd2fSBen Hoff /* Query the *current detected* DV timings on the input. 415*ba07fd2fSBen Hoff * If you have a real hardware detector, call it here; otherwise we 416*ba07fd2fSBen Hoff * derive from the cached pix state and map to the closest supported DV mode. 417*ba07fd2fSBen Hoff */ 418*ba07fd2fSBen Hoff int hws_vidioc_query_dv_timings(struct file *file, void *fh, 419*ba07fd2fSBen Hoff struct v4l2_dv_timings *timings) 420*ba07fd2fSBen Hoff { 421*ba07fd2fSBen Hoff struct hws_video *vid = video_drvdata(file); 422*ba07fd2fSBen Hoff u32 w, h; 423*ba07fd2fSBen Hoff u32 fps; 424*ba07fd2fSBen Hoff bool interlace; 425*ba07fd2fSBen Hoff 426*ba07fd2fSBen Hoff if (!timings) 427*ba07fd2fSBen Hoff return -EINVAL; 428*ba07fd2fSBen Hoff 429*ba07fd2fSBen Hoff w = vid->pix.width; 430*ba07fd2fSBen Hoff h = vid->pix.height; 431*ba07fd2fSBen Hoff interlace = vid->pix.interlaced; 432*ba07fd2fSBen Hoff hws_get_live_dv_geometry(vid, &w, &h, &interlace); 433*ba07fd2fSBen Hoff fps = hws_get_live_fps(vid); 434*ba07fd2fSBen Hoff if (!fps) 435*ba07fd2fSBen Hoff fps = vid->current_fps ? vid->current_fps : 436*ba07fd2fSBen Hoff hws_pick_fps_from_mode(w, h, interlace); 437*ba07fd2fSBen Hoff 438*ba07fd2fSBen Hoff return hws_fill_dv_timings(w, h, interlace, fps, timings); 439*ba07fd2fSBen Hoff } 440*ba07fd2fSBen Hoff 441*ba07fd2fSBen Hoff /* Enumerate the Nth supported DV timings from our static table. */ 442*ba07fd2fSBen Hoff int hws_vidioc_enum_dv_timings(struct file *file, void *fh, 443*ba07fd2fSBen Hoff struct v4l2_enum_dv_timings *edv) 444*ba07fd2fSBen Hoff { 445*ba07fd2fSBen Hoff struct hws_video *vid = video_drvdata(file); 446*ba07fd2fSBen Hoff const struct hws_dv_mode *m; 447*ba07fd2fSBen Hoff u32 w, h; 448*ba07fd2fSBen Hoff u32 fps; 449*ba07fd2fSBen Hoff bool interlace; 450*ba07fd2fSBen Hoff 451*ba07fd2fSBen Hoff if (!edv) 452*ba07fd2fSBen Hoff return -EINVAL; 453*ba07fd2fSBen Hoff 454*ba07fd2fSBen Hoff if (edv->pad) 455*ba07fd2fSBen Hoff return -EINVAL; 456*ba07fd2fSBen Hoff 457*ba07fd2fSBen Hoff w = 0; 458*ba07fd2fSBen Hoff h = 0; 459*ba07fd2fSBen Hoff interlace = false; 460*ba07fd2fSBen Hoff if (hws_get_live_dv_geometry(vid, &w, &h, &interlace)) { 461*ba07fd2fSBen Hoff fps = hws_get_live_fps(vid); 462*ba07fd2fSBen Hoff if (!fps) 463*ba07fd2fSBen Hoff fps = vid->current_fps ? vid->current_fps : 464*ba07fd2fSBen Hoff hws_pick_fps_from_mode(w, h, interlace); 465*ba07fd2fSBen Hoff m = fps ? hws_find_dv_by_wh_fps(w, h, interlace, fps) : NULL; 466*ba07fd2fSBen Hoff if (!m) 467*ba07fd2fSBen Hoff m = hws_find_dv_by_wh(w, h, interlace); 468*ba07fd2fSBen Hoff if (m) { 469*ba07fd2fSBen Hoff if (edv->index) 470*ba07fd2fSBen Hoff return -EINVAL; 471*ba07fd2fSBen Hoff edv->timings = m->timings; 472*ba07fd2fSBen Hoff return 0; 473*ba07fd2fSBen Hoff } 474*ba07fd2fSBen Hoff } 475*ba07fd2fSBen Hoff 476*ba07fd2fSBen Hoff if (edv->index >= hws_dv_modes_cnt) 477*ba07fd2fSBen Hoff return -EINVAL; 478*ba07fd2fSBen Hoff 479*ba07fd2fSBen Hoff edv->timings = hws_dv_modes[edv->index].timings; 480*ba07fd2fSBen Hoff return 0; 481*ba07fd2fSBen Hoff } 482*ba07fd2fSBen Hoff 483*ba07fd2fSBen Hoff /* Get the *currently configured* DV timings. */ 484*ba07fd2fSBen Hoff int hws_vidioc_g_dv_timings(struct file *file, void *fh, 485*ba07fd2fSBen Hoff struct v4l2_dv_timings *timings) 486*ba07fd2fSBen Hoff { 487*ba07fd2fSBen Hoff struct hws_video *vid = video_drvdata(file); 488*ba07fd2fSBen Hoff u32 w, h; 489*ba07fd2fSBen Hoff u32 fps; 490*ba07fd2fSBen Hoff bool interlace; 491*ba07fd2fSBen Hoff 492*ba07fd2fSBen Hoff if (!timings) 493*ba07fd2fSBen Hoff return -EINVAL; 494*ba07fd2fSBen Hoff 495*ba07fd2fSBen Hoff w = vid->pix.width; 496*ba07fd2fSBen Hoff h = vid->pix.height; 497*ba07fd2fSBen Hoff interlace = vid->pix.interlaced; 498*ba07fd2fSBen Hoff if (hws_get_live_dv_geometry(vid, &w, &h, &interlace)) { 499*ba07fd2fSBen Hoff fps = hws_get_live_fps(vid); 500*ba07fd2fSBen Hoff if (!fps) 501*ba07fd2fSBen Hoff fps = vid->current_fps ? vid->current_fps : 502*ba07fd2fSBen Hoff hws_pick_fps_from_mode(w, h, interlace); 503*ba07fd2fSBen Hoff return hws_fill_dv_timings(w, h, interlace, fps, timings); 504*ba07fd2fSBen Hoff } 505*ba07fd2fSBen Hoff 506*ba07fd2fSBen Hoff *timings = vid->cur_dv_timings; 507*ba07fd2fSBen Hoff return 0; 508*ba07fd2fSBen Hoff } 509*ba07fd2fSBen Hoff 510*ba07fd2fSBen Hoff static inline void hws_set_colorimetry_state(struct hws_pix_state *p) 511*ba07fd2fSBen Hoff { 512*ba07fd2fSBen Hoff bool sd = p->height <= 576; 513*ba07fd2fSBen Hoff 514*ba07fd2fSBen Hoff p->colorspace = sd ? V4L2_COLORSPACE_SMPTE170M : V4L2_COLORSPACE_REC709; 515*ba07fd2fSBen Hoff p->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; 516*ba07fd2fSBen Hoff p->quantization = V4L2_QUANTIZATION_FULL_RANGE; 517*ba07fd2fSBen Hoff p->xfer_func = V4L2_XFER_FUNC_DEFAULT; 518*ba07fd2fSBen Hoff } 519*ba07fd2fSBen Hoff 520*ba07fd2fSBen Hoff /* Set DV timings: must match one of our supported modes. 521*ba07fd2fSBen Hoff * If buffers are queued and this implies a size change, we reject with -EBUSY. 522*ba07fd2fSBen Hoff * Otherwise we update pix state and (optionally) reprogram the HW. 523*ba07fd2fSBen Hoff */ 524*ba07fd2fSBen Hoff int hws_vidioc_s_dv_timings(struct file *file, void *fh, 525*ba07fd2fSBen Hoff struct v4l2_dv_timings *timings) 526*ba07fd2fSBen Hoff { 527*ba07fd2fSBen Hoff struct hws_video *vid = video_drvdata(file); 528*ba07fd2fSBen Hoff const struct hws_dv_mode *m; 529*ba07fd2fSBen Hoff const struct v4l2_bt_timings *bt; 530*ba07fd2fSBen Hoff u32 new_w, new_h; 531*ba07fd2fSBen Hoff bool interlaced; 532*ba07fd2fSBen Hoff int ret = 0; 533*ba07fd2fSBen Hoff unsigned long was_busy; 534*ba07fd2fSBen Hoff u32 live_w, live_h; 535*ba07fd2fSBen Hoff u32 live_fps; 536*ba07fd2fSBen Hoff bool live_interlaced; 537*ba07fd2fSBen Hoff bool live_present; 538*ba07fd2fSBen Hoff 539*ba07fd2fSBen Hoff if (!timings) 540*ba07fd2fSBen Hoff return -EINVAL; 541*ba07fd2fSBen Hoff 542*ba07fd2fSBen Hoff m = hws_match_supported_dv(timings); 543*ba07fd2fSBen Hoff if (!m) 544*ba07fd2fSBen Hoff return -EINVAL; 545*ba07fd2fSBen Hoff 546*ba07fd2fSBen Hoff bt = &m->timings.bt; 547*ba07fd2fSBen Hoff if (bt->interlaced) 548*ba07fd2fSBen Hoff return -EINVAL; /* only progressive modes are advertised */ 549*ba07fd2fSBen Hoff new_w = bt->width; 550*ba07fd2fSBen Hoff new_h = bt->height; 551*ba07fd2fSBen Hoff interlaced = false; 552*ba07fd2fSBen Hoff 553*ba07fd2fSBen Hoff lockdep_assert_held(&vid->state_lock); 554*ba07fd2fSBen Hoff live_present = hws_get_live_dv_geometry(vid, &live_w, &live_h, 555*ba07fd2fSBen Hoff &live_interlaced); 556*ba07fd2fSBen Hoff 557*ba07fd2fSBen Hoff /* If vb2 has active buffers and size would change, reject. */ 558*ba07fd2fSBen Hoff was_busy = vb2_is_busy(&vid->buffer_queue); 559*ba07fd2fSBen Hoff if (was_busy && 560*ba07fd2fSBen Hoff (new_w != vid->pix.width || new_h != vid->pix.height || 561*ba07fd2fSBen Hoff interlaced != vid->pix.interlaced)) { 562*ba07fd2fSBen Hoff ret = -EBUSY; 563*ba07fd2fSBen Hoff return ret; 564*ba07fd2fSBen Hoff } 565*ba07fd2fSBen Hoff 566*ba07fd2fSBen Hoff /* When a live input signal is present, the receiver owns the timing. 567*ba07fd2fSBen Hoff * Allow setting the already-active timings so v4l2-compliance can 568*ba07fd2fSBen Hoff * round-trip them, but reject attempts to retime the live source. 569*ba07fd2fSBen Hoff */ 570*ba07fd2fSBen Hoff if (live_present) { 571*ba07fd2fSBen Hoff live_fps = hws_get_live_fps(vid); 572*ba07fd2fSBen Hoff if (!live_fps) 573*ba07fd2fSBen Hoff live_fps = vid->current_fps ? vid->current_fps : 574*ba07fd2fSBen Hoff hws_pick_fps_from_mode(live_w, live_h, 575*ba07fd2fSBen Hoff live_interlaced); 576*ba07fd2fSBen Hoff if (live_w == new_w && live_h == new_h && 577*ba07fd2fSBen Hoff live_interlaced == interlaced && 578*ba07fd2fSBen Hoff m->refresh_hz == live_fps) 579*ba07fd2fSBen Hoff return 0; 580*ba07fd2fSBen Hoff return -EBUSY; 581*ba07fd2fSBen Hoff } 582*ba07fd2fSBen Hoff 583*ba07fd2fSBen Hoff /* Update software pixel state (and recalc sizes) */ 584*ba07fd2fSBen Hoff vid->pix.width = new_w; 585*ba07fd2fSBen Hoff vid->pix.height = new_h; 586*ba07fd2fSBen Hoff vid->pix.field = interlaced ? V4L2_FIELD_INTERLACED 587*ba07fd2fSBen Hoff : V4L2_FIELD_NONE; 588*ba07fd2fSBen Hoff vid->pix.interlaced = interlaced; 589*ba07fd2fSBen Hoff vid->pix.fourcc = V4L2_PIX_FMT_YUYV; 590*ba07fd2fSBen Hoff 591*ba07fd2fSBen Hoff hws_set_colorimetry_state(&vid->pix); 592*ba07fd2fSBen Hoff 593*ba07fd2fSBen Hoff /* Recompute stride, sizeimage, and half_size. */ 594*ba07fd2fSBen Hoff vid->pix.bytesperline = hws_calc_bpl_yuyv(new_w); 595*ba07fd2fSBen Hoff vid->pix.sizeimage = hws_calc_size_yuyv(new_w, new_h); 596*ba07fd2fSBen Hoff vid->pix.half_size = hws_calc_half_size(vid->pix.sizeimage); 597*ba07fd2fSBen Hoff vid->cur_dv_timings = m->timings; 598*ba07fd2fSBen Hoff vid->current_fps = m->refresh_hz; 599*ba07fd2fSBen Hoff return ret; 600*ba07fd2fSBen Hoff } 601*ba07fd2fSBen Hoff 602*ba07fd2fSBen Hoff /* Report DV timings capability: advertise BT.656/1120 with 603*ba07fd2fSBen Hoff * the min/max WxH derived from our table and basic progressive support. 604*ba07fd2fSBen Hoff */ 605*ba07fd2fSBen Hoff int hws_vidioc_dv_timings_cap(struct file *file, void *fh, 606*ba07fd2fSBen Hoff struct v4l2_dv_timings_cap *cap) 607*ba07fd2fSBen Hoff { 608*ba07fd2fSBen Hoff u32 min_w = ~0U, min_h = ~0U; 609*ba07fd2fSBen Hoff u32 max_w = 0, max_h = 0; 610*ba07fd2fSBen Hoff size_t i, n = 0; 611*ba07fd2fSBen Hoff 612*ba07fd2fSBen Hoff if (!cap) 613*ba07fd2fSBen Hoff return -EINVAL; 614*ba07fd2fSBen Hoff 615*ba07fd2fSBen Hoff memset(cap, 0, sizeof(*cap)); 616*ba07fd2fSBen Hoff cap->type = V4L2_DV_BT_656_1120; 617*ba07fd2fSBen Hoff 618*ba07fd2fSBen Hoff for (i = 0; i < ARRAY_SIZE(hws_dv_modes); i++) { 619*ba07fd2fSBen Hoff const struct v4l2_bt_timings *bt = &hws_dv_modes[i].timings.bt; 620*ba07fd2fSBen Hoff 621*ba07fd2fSBen Hoff if (hws_dv_modes[i].timings.type != V4L2_DV_BT_656_1120) 622*ba07fd2fSBen Hoff continue; 623*ba07fd2fSBen Hoff n++; 624*ba07fd2fSBen Hoff 625*ba07fd2fSBen Hoff if (bt->width < min_w) 626*ba07fd2fSBen Hoff min_w = bt->width; 627*ba07fd2fSBen Hoff if (bt->height < min_h) 628*ba07fd2fSBen Hoff min_h = bt->height; 629*ba07fd2fSBen Hoff if (bt->width > max_w) 630*ba07fd2fSBen Hoff max_w = bt->width; 631*ba07fd2fSBen Hoff if (bt->height > max_h) 632*ba07fd2fSBen Hoff max_h = bt->height; 633*ba07fd2fSBen Hoff } 634*ba07fd2fSBen Hoff 635*ba07fd2fSBen Hoff /* If the table was empty, fail gracefully. */ 636*ba07fd2fSBen Hoff if (!n || min_w == U32_MAX) 637*ba07fd2fSBen Hoff return -ENODATA; 638*ba07fd2fSBen Hoff 639*ba07fd2fSBen Hoff cap->bt.min_width = min_w; 640*ba07fd2fSBen Hoff cap->bt.max_width = max_w; 641*ba07fd2fSBen Hoff cap->bt.min_height = min_h; 642*ba07fd2fSBen Hoff cap->bt.max_height = max_h; 643*ba07fd2fSBen Hoff 644*ba07fd2fSBen Hoff /* We support both CEA-861- and VESA-style modes in the list. */ 645*ba07fd2fSBen Hoff cap->bt.standards = 646*ba07fd2fSBen Hoff V4L2_DV_BT_STD_CEA861 | V4L2_DV_BT_STD_DMT | V4L2_DV_BT_STD_CVT; 647*ba07fd2fSBen Hoff 648*ba07fd2fSBen Hoff /* Only progressive modes are advertised. */ 649*ba07fd2fSBen Hoff cap->bt.capabilities = V4L2_DV_BT_CAP_PROGRESSIVE; 650*ba07fd2fSBen Hoff 651*ba07fd2fSBen Hoff /* Leave pixelclock/porch limits unconstrained (0) for now. */ 652*ba07fd2fSBen Hoff return 0; 653*ba07fd2fSBen Hoff } 654*ba07fd2fSBen Hoff 655*ba07fd2fSBen Hoff static int hws_s_ctrl(struct v4l2_ctrl *ctrl) 656*ba07fd2fSBen Hoff { 657*ba07fd2fSBen Hoff struct hws_video *vid = 658*ba07fd2fSBen Hoff container_of(ctrl->handler, struct hws_video, control_handler); 659*ba07fd2fSBen Hoff struct hws_pcie_dev *pdx = vid->parent; 660*ba07fd2fSBen Hoff bool program = false; 661*ba07fd2fSBen Hoff 662*ba07fd2fSBen Hoff switch (ctrl->id) { 663*ba07fd2fSBen Hoff case V4L2_CID_BRIGHTNESS: 664*ba07fd2fSBen Hoff vid->current_brightness = ctrl->val; 665*ba07fd2fSBen Hoff program = true; 666*ba07fd2fSBen Hoff break; 667*ba07fd2fSBen Hoff case V4L2_CID_CONTRAST: 668*ba07fd2fSBen Hoff vid->current_contrast = ctrl->val; 669*ba07fd2fSBen Hoff program = true; 670*ba07fd2fSBen Hoff break; 671*ba07fd2fSBen Hoff case V4L2_CID_SATURATION: 672*ba07fd2fSBen Hoff vid->current_saturation = ctrl->val; 673*ba07fd2fSBen Hoff program = true; 674*ba07fd2fSBen Hoff break; 675*ba07fd2fSBen Hoff case V4L2_CID_HUE: 676*ba07fd2fSBen Hoff vid->current_hue = ctrl->val; 677*ba07fd2fSBen Hoff program = true; 678*ba07fd2fSBen Hoff break; 679*ba07fd2fSBen Hoff default: 680*ba07fd2fSBen Hoff return -EINVAL; 681*ba07fd2fSBen Hoff } 682*ba07fd2fSBen Hoff 683*ba07fd2fSBen Hoff if (program) { 684*ba07fd2fSBen Hoff hws_hw_write_bchs(pdx, vid->channel_index, 685*ba07fd2fSBen Hoff (u8)vid->current_brightness, 686*ba07fd2fSBen Hoff (u8)vid->current_contrast, 687*ba07fd2fSBen Hoff (u8)vid->current_hue, 688*ba07fd2fSBen Hoff (u8)vid->current_saturation); 689*ba07fd2fSBen Hoff } 690*ba07fd2fSBen Hoff return 0; 691*ba07fd2fSBen Hoff } 692*ba07fd2fSBen Hoff 693*ba07fd2fSBen Hoff const struct v4l2_ctrl_ops hws_ctrl_ops = { 694*ba07fd2fSBen Hoff .s_ctrl = hws_s_ctrl, 695*ba07fd2fSBen Hoff }; 696*ba07fd2fSBen Hoff 697*ba07fd2fSBen Hoff int hws_vidioc_querycap(struct file *file, void *priv, struct v4l2_capability *cap) 698*ba07fd2fSBen Hoff { 699*ba07fd2fSBen Hoff struct hws_video *vid = video_drvdata(file); 700*ba07fd2fSBen Hoff int vi_index = vid->channel_index + 1; /* keep it simple */ 701*ba07fd2fSBen Hoff 702*ba07fd2fSBen Hoff strscpy(cap->driver, KBUILD_MODNAME, sizeof(cap->driver)); 703*ba07fd2fSBen Hoff snprintf(cap->card, sizeof(cap->card), 704*ba07fd2fSBen Hoff "AVMatrix HWS Capture %d", vi_index); 705*ba07fd2fSBen Hoff return 0; 706*ba07fd2fSBen Hoff } 707*ba07fd2fSBen Hoff 708*ba07fd2fSBen Hoff int hws_vidioc_enum_fmt_vid_cap(struct file *file, void *priv_fh, struct v4l2_fmtdesc *f) 709*ba07fd2fSBen Hoff { 710*ba07fd2fSBen Hoff if (f->index != 0) 711*ba07fd2fSBen Hoff return -EINVAL; /* only one format */ 712*ba07fd2fSBen Hoff 713*ba07fd2fSBen Hoff f->pixelformat = V4L2_PIX_FMT_YUYV; 714*ba07fd2fSBen Hoff return 0; 715*ba07fd2fSBen Hoff } 716*ba07fd2fSBen Hoff 717*ba07fd2fSBen Hoff int hws_vidioc_g_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt) 718*ba07fd2fSBen Hoff { 719*ba07fd2fSBen Hoff struct hws_video *vid = video_drvdata(file); 720*ba07fd2fSBen Hoff 721*ba07fd2fSBen Hoff fmt->fmt.pix.width = vid->pix.width; 722*ba07fd2fSBen Hoff fmt->fmt.pix.height = vid->pix.height; 723*ba07fd2fSBen Hoff fmt->fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV; 724*ba07fd2fSBen Hoff fmt->fmt.pix.field = vid->pix.field; 725*ba07fd2fSBen Hoff fmt->fmt.pix.bytesperline = vid->pix.bytesperline; 726*ba07fd2fSBen Hoff fmt->fmt.pix.sizeimage = vid->pix.sizeimage; 727*ba07fd2fSBen Hoff fmt->fmt.pix.colorspace = vid->pix.colorspace; 728*ba07fd2fSBen Hoff fmt->fmt.pix.ycbcr_enc = vid->pix.ycbcr_enc; 729*ba07fd2fSBen Hoff fmt->fmt.pix.quantization = vid->pix.quantization; 730*ba07fd2fSBen Hoff fmt->fmt.pix.xfer_func = vid->pix.xfer_func; 731*ba07fd2fSBen Hoff return 0; 732*ba07fd2fSBen Hoff } 733*ba07fd2fSBen Hoff 734*ba07fd2fSBen Hoff static inline void hws_set_colorimetry_fmt(struct v4l2_pix_format *p) 735*ba07fd2fSBen Hoff { 736*ba07fd2fSBen Hoff bool sd = p->height <= 576; 737*ba07fd2fSBen Hoff 738*ba07fd2fSBen Hoff p->colorspace = sd ? V4L2_COLORSPACE_SMPTE170M : V4L2_COLORSPACE_REC709; 739*ba07fd2fSBen Hoff p->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; 740*ba07fd2fSBen Hoff p->quantization = V4L2_QUANTIZATION_FULL_RANGE; 741*ba07fd2fSBen Hoff p->xfer_func = V4L2_XFER_FUNC_DEFAULT; 742*ba07fd2fSBen Hoff } 743*ba07fd2fSBen Hoff 744*ba07fd2fSBen Hoff int hws_vidioc_try_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f) 745*ba07fd2fSBen Hoff { 746*ba07fd2fSBen Hoff struct hws_video *vid = file ? video_drvdata(file) : NULL; 747*ba07fd2fSBen Hoff struct hws_pcie_dev *pdev = vid ? vid->parent : NULL; 748*ba07fd2fSBen Hoff struct v4l2_pix_format *pix = &f->fmt.pix; 749*ba07fd2fSBen Hoff u32 req_w = pix->width, req_h = pix->height; 750*ba07fd2fSBen Hoff u32 w, h, min_bpl, bpl; 751*ba07fd2fSBen Hoff size_t size; /* wider than u32 for overflow check */ 752*ba07fd2fSBen Hoff size_t max_frame = pdev ? pdev->max_hw_video_buf_sz : MAX_MM_VIDEO_SIZE; 753*ba07fd2fSBen Hoff 754*ba07fd2fSBen Hoff /* Only YUYV */ 755*ba07fd2fSBen Hoff pix->pixelformat = V4L2_PIX_FMT_YUYV; 756*ba07fd2fSBen Hoff 757*ba07fd2fSBen Hoff /* Defaults then clamp */ 758*ba07fd2fSBen Hoff w = (req_w ? req_w : 640); 759*ba07fd2fSBen Hoff h = (req_h ? req_h : 480); 760*ba07fd2fSBen Hoff if (w > MAX_VIDEO_HW_W) 761*ba07fd2fSBen Hoff w = MAX_VIDEO_HW_W; 762*ba07fd2fSBen Hoff if (h > MAX_VIDEO_HW_H) 763*ba07fd2fSBen Hoff h = MAX_VIDEO_HW_H; 764*ba07fd2fSBen Hoff if (!w) 765*ba07fd2fSBen Hoff w = 640; /* hard fallback in case macros are odd */ 766*ba07fd2fSBen Hoff if (!h) 767*ba07fd2fSBen Hoff h = 480; 768*ba07fd2fSBen Hoff 769*ba07fd2fSBen Hoff /* Field policy */ 770*ba07fd2fSBen Hoff pix->field = V4L2_FIELD_NONE; 771*ba07fd2fSBen Hoff 772*ba07fd2fSBen Hoff /* Stride policy for packed 16bpp, 64B align */ 773*ba07fd2fSBen Hoff min_bpl = ALIGN(w * 2, 64); 774*ba07fd2fSBen Hoff 775*ba07fd2fSBen Hoff /* Bound requested bpl to something sane, then align */ 776*ba07fd2fSBen Hoff bpl = pix->bytesperline; 777*ba07fd2fSBen Hoff if (bpl < min_bpl) { 778*ba07fd2fSBen Hoff bpl = min_bpl; 779*ba07fd2fSBen Hoff } else { 780*ba07fd2fSBen Hoff /* Cap at 16x width to avoid silly values that overflow sizeimage */ 781*ba07fd2fSBen Hoff u32 max_bpl = ALIGN(w * 2 * 16, 64); 782*ba07fd2fSBen Hoff 783*ba07fd2fSBen Hoff if (bpl > max_bpl) 784*ba07fd2fSBen Hoff bpl = max_bpl; 785*ba07fd2fSBen Hoff bpl = ALIGN(bpl, 64); 786*ba07fd2fSBen Hoff } 787*ba07fd2fSBen Hoff if (h && max_frame) { 788*ba07fd2fSBen Hoff size_t max_bpl_hw = max_frame / h; 789*ba07fd2fSBen Hoff 790*ba07fd2fSBen Hoff if (max_bpl_hw < min_bpl) 791*ba07fd2fSBen Hoff return -ERANGE; 792*ba07fd2fSBen Hoff max_bpl_hw = rounddown(max_bpl_hw, 64); 793*ba07fd2fSBen Hoff if (!max_bpl_hw) 794*ba07fd2fSBen Hoff return -ERANGE; 795*ba07fd2fSBen Hoff if (bpl > max_bpl_hw) { 796*ba07fd2fSBen Hoff if (pdev) 797*ba07fd2fSBen Hoff dev_dbg(&pdev->pdev->dev, 798*ba07fd2fSBen Hoff "try_fmt: clamp bpl %u -> %zu due to hw buf cap %zu\n", 799*ba07fd2fSBen Hoff bpl, max_bpl_hw, max_frame); 800*ba07fd2fSBen Hoff bpl = (u32)max_bpl_hw; 801*ba07fd2fSBen Hoff } 802*ba07fd2fSBen Hoff } 803*ba07fd2fSBen Hoff size = (size_t)bpl * (size_t)h; 804*ba07fd2fSBen Hoff if (size > max_frame) 805*ba07fd2fSBen Hoff return -ERANGE; 806*ba07fd2fSBen Hoff 807*ba07fd2fSBen Hoff pix->width = w; 808*ba07fd2fSBen Hoff pix->height = h; 809*ba07fd2fSBen Hoff pix->bytesperline = bpl; 810*ba07fd2fSBen Hoff pix->sizeimage = (u32)size; /* logical size, not page-aligned */ 811*ba07fd2fSBen Hoff 812*ba07fd2fSBen Hoff hws_set_colorimetry_fmt(pix); 813*ba07fd2fSBen Hoff if (pdev) 814*ba07fd2fSBen Hoff dev_dbg(&pdev->pdev->dev, 815*ba07fd2fSBen Hoff "try_fmt: w=%u h=%u bpl=%u size=%u field=%u\n", 816*ba07fd2fSBen Hoff pix->width, pix->height, pix->bytesperline, 817*ba07fd2fSBen Hoff pix->sizeimage, pix->field); 818*ba07fd2fSBen Hoff return 0; 819*ba07fd2fSBen Hoff } 820*ba07fd2fSBen Hoff 821*ba07fd2fSBen Hoff int hws_vidioc_s_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) 822*ba07fd2fSBen Hoff { 823*ba07fd2fSBen Hoff struct hws_video *vid = video_drvdata(file); 824*ba07fd2fSBen Hoff int ret; 825*ba07fd2fSBen Hoff 826*ba07fd2fSBen Hoff if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) 827*ba07fd2fSBen Hoff return -EINVAL; 828*ba07fd2fSBen Hoff 829*ba07fd2fSBen Hoff /* Normalize the request */ 830*ba07fd2fSBen Hoff ret = hws_vidioc_try_fmt_vid_cap(file, priv, f); 831*ba07fd2fSBen Hoff if (ret) 832*ba07fd2fSBen Hoff return ret; 833*ba07fd2fSBen Hoff 834*ba07fd2fSBen Hoff /* Don't allow buffer layout changes while buffers are queued. */ 835*ba07fd2fSBen Hoff if (vb2_is_busy(&vid->buffer_queue)) { 836*ba07fd2fSBen Hoff if (f->fmt.pix.width != vid->pix.width || 837*ba07fd2fSBen Hoff f->fmt.pix.height != vid->pix.height || 838*ba07fd2fSBen Hoff f->fmt.pix.bytesperline != vid->pix.bytesperline) 839*ba07fd2fSBen Hoff return -EBUSY; 840*ba07fd2fSBen Hoff } 841*ba07fd2fSBen Hoff 842*ba07fd2fSBen Hoff /* Apply to driver state */ 843*ba07fd2fSBen Hoff vid->pix.width = f->fmt.pix.width; 844*ba07fd2fSBen Hoff vid->pix.height = f->fmt.pix.height; 845*ba07fd2fSBen Hoff vid->pix.fourcc = V4L2_PIX_FMT_YUYV; 846*ba07fd2fSBen Hoff vid->pix.field = f->fmt.pix.field; 847*ba07fd2fSBen Hoff vid->pix.colorspace = f->fmt.pix.colorspace; 848*ba07fd2fSBen Hoff vid->pix.ycbcr_enc = f->fmt.pix.ycbcr_enc; 849*ba07fd2fSBen Hoff vid->pix.quantization = f->fmt.pix.quantization; 850*ba07fd2fSBen Hoff vid->pix.xfer_func = f->fmt.pix.xfer_func; 851*ba07fd2fSBen Hoff 852*ba07fd2fSBen Hoff /* Update negotiated buffer sizes. */ 853*ba07fd2fSBen Hoff vid->pix.bytesperline = f->fmt.pix.bytesperline; /* aligned */ 854*ba07fd2fSBen Hoff vid->pix.sizeimage = f->fmt.pix.sizeimage; /* logical */ 855*ba07fd2fSBen Hoff vid->pix.half_size = hws_calc_half_size(vid->pix.sizeimage); 856*ba07fd2fSBen Hoff vid->pix.interlaced = false; 857*ba07fd2fSBen Hoff /* S_FMT negotiates buffer layout only. Keep detector-owned DV timing 858*ba07fd2fSBen Hoff * state unchanged so a harmless restart cannot clobber the live FPS. 859*ba07fd2fSBen Hoff */ 860*ba07fd2fSBen Hoff /* Or: 861*ba07fd2fSBen Hoff * hws_calc_sizeimage(vid, vid->pix.width, vid->pix.height, false); 862*ba07fd2fSBen Hoff */ 863*ba07fd2fSBen Hoff 864*ba07fd2fSBen Hoff dev_dbg(&vid->parent->pdev->dev, 865*ba07fd2fSBen Hoff "s_fmt: w=%u h=%u bpl=%u size=%u\n", 866*ba07fd2fSBen Hoff vid->pix.width, vid->pix.height, vid->pix.bytesperline, 867*ba07fd2fSBen Hoff vid->pix.sizeimage); 868*ba07fd2fSBen Hoff 869*ba07fd2fSBen Hoff return 0; 870*ba07fd2fSBen Hoff } 871*ba07fd2fSBen Hoff 872*ba07fd2fSBen Hoff int hws_vidioc_g_parm(struct file *file, void *fh, struct v4l2_streamparm *param) 873*ba07fd2fSBen Hoff { 874*ba07fd2fSBen Hoff struct hws_video *vid = video_drvdata(file); 875*ba07fd2fSBen Hoff u32 fps; 876*ba07fd2fSBen Hoff 877*ba07fd2fSBen Hoff if (param->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) 878*ba07fd2fSBen Hoff return -EINVAL; 879*ba07fd2fSBen Hoff 880*ba07fd2fSBen Hoff fps = hws_get_live_fps(vid); 881*ba07fd2fSBen Hoff if (!fps) 882*ba07fd2fSBen Hoff fps = vid->current_fps ? vid->current_fps : 60; 883*ba07fd2fSBen Hoff 884*ba07fd2fSBen Hoff /* HDMI receivers report the detected frame period, they don't set it. */ 885*ba07fd2fSBen Hoff param->parm.capture.capability = 0; 886*ba07fd2fSBen Hoff param->parm.capture.capturemode = 0; 887*ba07fd2fSBen Hoff param->parm.capture.timeperframe.numerator = 1; 888*ba07fd2fSBen Hoff param->parm.capture.timeperframe.denominator = fps; 889*ba07fd2fSBen Hoff param->parm.capture.extendedmode = 0; 890*ba07fd2fSBen Hoff param->parm.capture.readbuffers = 0; 891*ba07fd2fSBen Hoff 892*ba07fd2fSBen Hoff return 0; 893*ba07fd2fSBen Hoff } 894*ba07fd2fSBen Hoff 895*ba07fd2fSBen Hoff int hws_vidioc_enum_input(struct file *file, void *priv, 896*ba07fd2fSBen Hoff struct v4l2_input *input) 897*ba07fd2fSBen Hoff { 898*ba07fd2fSBen Hoff struct hws_video *vid = video_drvdata(file); 899*ba07fd2fSBen Hoff 900*ba07fd2fSBen Hoff if (input->index) 901*ba07fd2fSBen Hoff return -EINVAL; 902*ba07fd2fSBen Hoff input->type = V4L2_INPUT_TYPE_CAMERA; 903*ba07fd2fSBen Hoff strscpy(input->name, KBUILD_MODNAME, sizeof(input->name)); 904*ba07fd2fSBen Hoff input->capabilities = V4L2_IN_CAP_DV_TIMINGS; 905*ba07fd2fSBen Hoff input->status = hws_input_status(vid); 906*ba07fd2fSBen Hoff 907*ba07fd2fSBen Hoff return 0; 908*ba07fd2fSBen Hoff } 909*ba07fd2fSBen Hoff 910*ba07fd2fSBen Hoff int hws_vidioc_g_input(struct file *file, void *priv, unsigned int *index) 911*ba07fd2fSBen Hoff { 912*ba07fd2fSBen Hoff *index = 0; 913*ba07fd2fSBen Hoff return 0; 914*ba07fd2fSBen Hoff } 915*ba07fd2fSBen Hoff 916*ba07fd2fSBen Hoff int hws_vidioc_s_input(struct file *file, void *priv, unsigned int i) 917*ba07fd2fSBen Hoff { 918*ba07fd2fSBen Hoff return i ? -EINVAL : 0; 919*ba07fd2fSBen Hoff } 920