1*70b19398SWenmeng Liu // SPDX-License-Identifier: GPL-2.0 2*70b19398SWenmeng Liu /* 3*70b19398SWenmeng Liu * 4*70b19398SWenmeng Liu * Qualcomm MSM Camera Subsystem - TPG Module 5*70b19398SWenmeng Liu * 6*70b19398SWenmeng Liu * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. 7*70b19398SWenmeng Liu */ 8*70b19398SWenmeng Liu #include <linux/clk.h> 9*70b19398SWenmeng Liu #include <linux/io.h> 10*70b19398SWenmeng Liu #include <linux/kernel.h> 11*70b19398SWenmeng Liu #include <linux/of.h> 12*70b19398SWenmeng Liu #include <linux/platform_device.h> 13*70b19398SWenmeng Liu #include <linux/pm_runtime.h> 14*70b19398SWenmeng Liu #include <media/media-entity.h> 15*70b19398SWenmeng Liu #include <media/v4l2-device.h> 16*70b19398SWenmeng Liu #include <media/v4l2-subdev.h> 17*70b19398SWenmeng Liu #include <media/mipi-csi2.h> 18*70b19398SWenmeng Liu 19*70b19398SWenmeng Liu #include "camss-tpg.h" 20*70b19398SWenmeng Liu #include "camss.h" 21*70b19398SWenmeng Liu 22*70b19398SWenmeng Liu static const struct tpg_format_info formats_gen1[] = { 23*70b19398SWenmeng Liu { 24*70b19398SWenmeng Liu MEDIA_BUS_FMT_SBGGR8_1X8, 25*70b19398SWenmeng Liu MIPI_CSI2_DT_RAW8, 26*70b19398SWenmeng Liu ENCODE_FORMAT_UNCOMPRESSED_8_BIT, 27*70b19398SWenmeng Liu 8, 28*70b19398SWenmeng Liu }, 29*70b19398SWenmeng Liu { 30*70b19398SWenmeng Liu MEDIA_BUS_FMT_SGBRG8_1X8, 31*70b19398SWenmeng Liu MIPI_CSI2_DT_RAW8, 32*70b19398SWenmeng Liu ENCODE_FORMAT_UNCOMPRESSED_8_BIT, 33*70b19398SWenmeng Liu 8, 34*70b19398SWenmeng Liu }, 35*70b19398SWenmeng Liu { 36*70b19398SWenmeng Liu MEDIA_BUS_FMT_SGRBG8_1X8, 37*70b19398SWenmeng Liu MIPI_CSI2_DT_RAW8, 38*70b19398SWenmeng Liu ENCODE_FORMAT_UNCOMPRESSED_8_BIT, 39*70b19398SWenmeng Liu 8, 40*70b19398SWenmeng Liu }, 41*70b19398SWenmeng Liu { 42*70b19398SWenmeng Liu MEDIA_BUS_FMT_SRGGB8_1X8, 43*70b19398SWenmeng Liu MIPI_CSI2_DT_RAW8, 44*70b19398SWenmeng Liu ENCODE_FORMAT_UNCOMPRESSED_8_BIT, 45*70b19398SWenmeng Liu 8, 46*70b19398SWenmeng Liu }, 47*70b19398SWenmeng Liu { 48*70b19398SWenmeng Liu MEDIA_BUS_FMT_SBGGR10_1X10, 49*70b19398SWenmeng Liu MIPI_CSI2_DT_RAW10, 50*70b19398SWenmeng Liu ENCODE_FORMAT_UNCOMPRESSED_10_BIT, 51*70b19398SWenmeng Liu 10, 52*70b19398SWenmeng Liu }, 53*70b19398SWenmeng Liu { 54*70b19398SWenmeng Liu MEDIA_BUS_FMT_SGBRG10_1X10, 55*70b19398SWenmeng Liu MIPI_CSI2_DT_RAW10, 56*70b19398SWenmeng Liu ENCODE_FORMAT_UNCOMPRESSED_10_BIT, 57*70b19398SWenmeng Liu 10, 58*70b19398SWenmeng Liu }, 59*70b19398SWenmeng Liu { 60*70b19398SWenmeng Liu MEDIA_BUS_FMT_SGRBG10_1X10, 61*70b19398SWenmeng Liu MIPI_CSI2_DT_RAW10, 62*70b19398SWenmeng Liu ENCODE_FORMAT_UNCOMPRESSED_10_BIT, 63*70b19398SWenmeng Liu 10, 64*70b19398SWenmeng Liu }, 65*70b19398SWenmeng Liu { 66*70b19398SWenmeng Liu MEDIA_BUS_FMT_SRGGB10_1X10, 67*70b19398SWenmeng Liu MIPI_CSI2_DT_RAW10, 68*70b19398SWenmeng Liu ENCODE_FORMAT_UNCOMPRESSED_10_BIT, 69*70b19398SWenmeng Liu 10, 70*70b19398SWenmeng Liu }, 71*70b19398SWenmeng Liu { 72*70b19398SWenmeng Liu MEDIA_BUS_FMT_SBGGR12_1X12, 73*70b19398SWenmeng Liu MIPI_CSI2_DT_RAW12, 74*70b19398SWenmeng Liu ENCODE_FORMAT_UNCOMPRESSED_12_BIT, 75*70b19398SWenmeng Liu 12, 76*70b19398SWenmeng Liu }, 77*70b19398SWenmeng Liu { 78*70b19398SWenmeng Liu MEDIA_BUS_FMT_SGBRG12_1X12, 79*70b19398SWenmeng Liu MIPI_CSI2_DT_RAW12, 80*70b19398SWenmeng Liu ENCODE_FORMAT_UNCOMPRESSED_12_BIT, 81*70b19398SWenmeng Liu 12, 82*70b19398SWenmeng Liu }, 83*70b19398SWenmeng Liu { 84*70b19398SWenmeng Liu MEDIA_BUS_FMT_SGRBG12_1X12, 85*70b19398SWenmeng Liu MIPI_CSI2_DT_RAW12, 86*70b19398SWenmeng Liu ENCODE_FORMAT_UNCOMPRESSED_12_BIT, 87*70b19398SWenmeng Liu 12, 88*70b19398SWenmeng Liu }, 89*70b19398SWenmeng Liu { 90*70b19398SWenmeng Liu MEDIA_BUS_FMT_SRGGB12_1X12, 91*70b19398SWenmeng Liu MIPI_CSI2_DT_RAW12, 92*70b19398SWenmeng Liu ENCODE_FORMAT_UNCOMPRESSED_12_BIT, 93*70b19398SWenmeng Liu 12, 94*70b19398SWenmeng Liu }, 95*70b19398SWenmeng Liu { 96*70b19398SWenmeng Liu MEDIA_BUS_FMT_Y8_1X8, 97*70b19398SWenmeng Liu MIPI_CSI2_DT_RAW8, 98*70b19398SWenmeng Liu ENCODE_FORMAT_UNCOMPRESSED_8_BIT, 99*70b19398SWenmeng Liu 8, 100*70b19398SWenmeng Liu }, 101*70b19398SWenmeng Liu { 102*70b19398SWenmeng Liu MEDIA_BUS_FMT_Y10_1X10, 103*70b19398SWenmeng Liu MIPI_CSI2_DT_RAW10, 104*70b19398SWenmeng Liu ENCODE_FORMAT_UNCOMPRESSED_10_BIT, 105*70b19398SWenmeng Liu 10, 106*70b19398SWenmeng Liu }, 107*70b19398SWenmeng Liu }; 108*70b19398SWenmeng Liu 109*70b19398SWenmeng Liu const struct tpg_formats tpg_formats_gen1 = { 110*70b19398SWenmeng Liu .nformats = ARRAY_SIZE(formats_gen1), 111*70b19398SWenmeng Liu .formats = formats_gen1 112*70b19398SWenmeng Liu }; 113*70b19398SWenmeng Liu 114*70b19398SWenmeng Liu const struct tpg_format_info *tpg_get_fmt_entry(const struct tpg_format_info *formats, 115*70b19398SWenmeng Liu unsigned int nformats, 116*70b19398SWenmeng Liu u32 code) 117*70b19398SWenmeng Liu { 118*70b19398SWenmeng Liu unsigned int i; 119*70b19398SWenmeng Liu 120*70b19398SWenmeng Liu for (i = 0; i < nformats; i++) 121*70b19398SWenmeng Liu if (code == formats[i].code) 122*70b19398SWenmeng Liu return &formats[i]; 123*70b19398SWenmeng Liu 124*70b19398SWenmeng Liu return ERR_PTR(-EINVAL); 125*70b19398SWenmeng Liu } 126*70b19398SWenmeng Liu 127*70b19398SWenmeng Liu static int tpg_set_clock_rates(struct tpg_device *tpg) 128*70b19398SWenmeng Liu { 129*70b19398SWenmeng Liu struct device *dev = tpg->camss->dev; 130*70b19398SWenmeng Liu int i, ret; 131*70b19398SWenmeng Liu 132*70b19398SWenmeng Liu for (i = 0; i < tpg->nclocks; i++) { 133*70b19398SWenmeng Liu struct camss_clock *clock = &tpg->clock[i]; 134*70b19398SWenmeng Liu long round_rate; 135*70b19398SWenmeng Liu 136*70b19398SWenmeng Liu if (clock->freq) { 137*70b19398SWenmeng Liu round_rate = clk_round_rate(clock->clk, clock->freq[0]); 138*70b19398SWenmeng Liu if (round_rate < 0) { 139*70b19398SWenmeng Liu dev_err(dev, "clk round rate failed: %ld\n", 140*70b19398SWenmeng Liu round_rate); 141*70b19398SWenmeng Liu return -EINVAL; 142*70b19398SWenmeng Liu } 143*70b19398SWenmeng Liu 144*70b19398SWenmeng Liu ret = clk_set_rate(clock->clk, round_rate); 145*70b19398SWenmeng Liu if (ret < 0) { 146*70b19398SWenmeng Liu dev_err(dev, "clk set rate failed: %d\n", ret); 147*70b19398SWenmeng Liu return ret; 148*70b19398SWenmeng Liu } 149*70b19398SWenmeng Liu } 150*70b19398SWenmeng Liu } 151*70b19398SWenmeng Liu 152*70b19398SWenmeng Liu return 0; 153*70b19398SWenmeng Liu } 154*70b19398SWenmeng Liu 155*70b19398SWenmeng Liu static int tpg_set_power(struct v4l2_subdev *sd, int on) 156*70b19398SWenmeng Liu { 157*70b19398SWenmeng Liu struct tpg_device *tpg = v4l2_get_subdevdata(sd); 158*70b19398SWenmeng Liu struct device *dev = tpg->camss->dev; 159*70b19398SWenmeng Liu 160*70b19398SWenmeng Liu if (on) { 161*70b19398SWenmeng Liu int ret; 162*70b19398SWenmeng Liu 163*70b19398SWenmeng Liu ret = pm_runtime_resume_and_get(dev); 164*70b19398SWenmeng Liu if (ret < 0) 165*70b19398SWenmeng Liu return ret; 166*70b19398SWenmeng Liu 167*70b19398SWenmeng Liu ret = tpg_set_clock_rates(tpg); 168*70b19398SWenmeng Liu if (ret < 0) { 169*70b19398SWenmeng Liu pm_runtime_put_sync(dev); 170*70b19398SWenmeng Liu return ret; 171*70b19398SWenmeng Liu } 172*70b19398SWenmeng Liu 173*70b19398SWenmeng Liu ret = camss_enable_clocks(tpg->nclocks, tpg->clock, dev); 174*70b19398SWenmeng Liu if (ret < 0) { 175*70b19398SWenmeng Liu pm_runtime_put_sync(dev); 176*70b19398SWenmeng Liu return ret; 177*70b19398SWenmeng Liu } 178*70b19398SWenmeng Liu 179*70b19398SWenmeng Liu tpg->res->hw_ops->reset(tpg); 180*70b19398SWenmeng Liu 181*70b19398SWenmeng Liu tpg->res->hw_ops->hw_version(tpg); 182*70b19398SWenmeng Liu } else { 183*70b19398SWenmeng Liu camss_disable_clocks(tpg->nclocks, tpg->clock); 184*70b19398SWenmeng Liu 185*70b19398SWenmeng Liu pm_runtime_put_sync(dev); 186*70b19398SWenmeng Liu } 187*70b19398SWenmeng Liu 188*70b19398SWenmeng Liu return 0; 189*70b19398SWenmeng Liu } 190*70b19398SWenmeng Liu 191*70b19398SWenmeng Liu static int tpg_set_stream(struct v4l2_subdev *sd, int enable) 192*70b19398SWenmeng Liu { 193*70b19398SWenmeng Liu struct tpg_device *tpg = v4l2_get_subdevdata(sd); 194*70b19398SWenmeng Liu int ret; 195*70b19398SWenmeng Liu 196*70b19398SWenmeng Liu if (enable) { 197*70b19398SWenmeng Liu ret = v4l2_ctrl_handler_setup(&tpg->ctrls); 198*70b19398SWenmeng Liu if (ret < 0) { 199*70b19398SWenmeng Liu dev_err(tpg->camss->dev, 200*70b19398SWenmeng Liu "could not sync v4l2 controls: %d\n", ret); 201*70b19398SWenmeng Liu return ret; 202*70b19398SWenmeng Liu } 203*70b19398SWenmeng Liu } 204*70b19398SWenmeng Liu 205*70b19398SWenmeng Liu return tpg->res->hw_ops->configure_stream(tpg, enable); 206*70b19398SWenmeng Liu } 207*70b19398SWenmeng Liu 208*70b19398SWenmeng Liu static struct v4l2_mbus_framefmt * 209*70b19398SWenmeng Liu __tpg_get_format(struct tpg_device *tpg, 210*70b19398SWenmeng Liu struct v4l2_subdev_state *sd_state, 211*70b19398SWenmeng Liu unsigned int pad, 212*70b19398SWenmeng Liu enum v4l2_subdev_format_whence which) 213*70b19398SWenmeng Liu { 214*70b19398SWenmeng Liu if (which == V4L2_SUBDEV_FORMAT_TRY) 215*70b19398SWenmeng Liu return v4l2_subdev_state_get_format(sd_state, 216*70b19398SWenmeng Liu pad); 217*70b19398SWenmeng Liu 218*70b19398SWenmeng Liu return &tpg->fmt; 219*70b19398SWenmeng Liu } 220*70b19398SWenmeng Liu 221*70b19398SWenmeng Liu static void tpg_try_format(struct tpg_device *tpg, 222*70b19398SWenmeng Liu struct v4l2_mbus_framefmt *fmt) 223*70b19398SWenmeng Liu { 224*70b19398SWenmeng Liu unsigned int i; 225*70b19398SWenmeng Liu 226*70b19398SWenmeng Liu for (i = 0; i < tpg->res->formats->nformats; i++) 227*70b19398SWenmeng Liu if (tpg->res->formats->formats[i].code == fmt->code) 228*70b19398SWenmeng Liu break; 229*70b19398SWenmeng Liu 230*70b19398SWenmeng Liu if (i >= tpg->res->formats->nformats) 231*70b19398SWenmeng Liu fmt->code = MEDIA_BUS_FMT_SBGGR8_1X8; 232*70b19398SWenmeng Liu 233*70b19398SWenmeng Liu fmt->width = clamp_t(u32, fmt->width, TPG_MIN_WIDTH, TPG_MAX_WIDTH); 234*70b19398SWenmeng Liu fmt->height = clamp_t(u32, fmt->height, TPG_MIN_HEIGHT, TPG_MAX_HEIGHT); 235*70b19398SWenmeng Liu fmt->field = V4L2_FIELD_NONE; 236*70b19398SWenmeng Liu fmt->colorspace = V4L2_COLORSPACE_SRGB; 237*70b19398SWenmeng Liu } 238*70b19398SWenmeng Liu 239*70b19398SWenmeng Liu static int tpg_enum_mbus_code(struct v4l2_subdev *sd, 240*70b19398SWenmeng Liu struct v4l2_subdev_state *sd_state, 241*70b19398SWenmeng Liu struct v4l2_subdev_mbus_code_enum *code) 242*70b19398SWenmeng Liu { 243*70b19398SWenmeng Liu struct tpg_device *tpg = v4l2_get_subdevdata(sd); 244*70b19398SWenmeng Liu 245*70b19398SWenmeng Liu if (code->index >= tpg->res->formats->nformats) 246*70b19398SWenmeng Liu return -EINVAL; 247*70b19398SWenmeng Liu 248*70b19398SWenmeng Liu code->code = tpg->res->formats->formats[code->index].code; 249*70b19398SWenmeng Liu 250*70b19398SWenmeng Liu return 0; 251*70b19398SWenmeng Liu } 252*70b19398SWenmeng Liu 253*70b19398SWenmeng Liu static int tpg_enum_frame_size(struct v4l2_subdev *sd, 254*70b19398SWenmeng Liu struct v4l2_subdev_state *sd_state, 255*70b19398SWenmeng Liu struct v4l2_subdev_frame_size_enum *fse) 256*70b19398SWenmeng Liu { 257*70b19398SWenmeng Liu struct tpg_device *tpg = v4l2_get_subdevdata(sd); 258*70b19398SWenmeng Liu unsigned int i; 259*70b19398SWenmeng Liu 260*70b19398SWenmeng Liu if (fse->index != 0) 261*70b19398SWenmeng Liu return -EINVAL; 262*70b19398SWenmeng Liu 263*70b19398SWenmeng Liu for (i = 0; i < tpg->res->formats->nformats; i++) 264*70b19398SWenmeng Liu if (tpg->res->formats->formats[i].code == fse->code) 265*70b19398SWenmeng Liu break; 266*70b19398SWenmeng Liu 267*70b19398SWenmeng Liu if (i >= tpg->res->formats->nformats) 268*70b19398SWenmeng Liu return -EINVAL; 269*70b19398SWenmeng Liu 270*70b19398SWenmeng Liu fse->min_width = TPG_MIN_WIDTH; 271*70b19398SWenmeng Liu fse->min_height = TPG_MIN_HEIGHT; 272*70b19398SWenmeng Liu fse->max_width = TPG_MAX_WIDTH; 273*70b19398SWenmeng Liu fse->max_height = TPG_MAX_HEIGHT; 274*70b19398SWenmeng Liu 275*70b19398SWenmeng Liu return 0; 276*70b19398SWenmeng Liu } 277*70b19398SWenmeng Liu 278*70b19398SWenmeng Liu static int tpg_get_format(struct v4l2_subdev *sd, 279*70b19398SWenmeng Liu struct v4l2_subdev_state *sd_state, 280*70b19398SWenmeng Liu struct v4l2_subdev_format *fmt) 281*70b19398SWenmeng Liu { 282*70b19398SWenmeng Liu struct tpg_device *tpg = v4l2_get_subdevdata(sd); 283*70b19398SWenmeng Liu struct v4l2_mbus_framefmt *format; 284*70b19398SWenmeng Liu 285*70b19398SWenmeng Liu format = __tpg_get_format(tpg, sd_state, fmt->pad, fmt->which); 286*70b19398SWenmeng Liu if (!format) 287*70b19398SWenmeng Liu return -EINVAL; 288*70b19398SWenmeng Liu 289*70b19398SWenmeng Liu fmt->format = *format; 290*70b19398SWenmeng Liu 291*70b19398SWenmeng Liu return 0; 292*70b19398SWenmeng Liu } 293*70b19398SWenmeng Liu 294*70b19398SWenmeng Liu static int tpg_set_format(struct v4l2_subdev *sd, 295*70b19398SWenmeng Liu struct v4l2_subdev_state *sd_state, 296*70b19398SWenmeng Liu struct v4l2_subdev_format *fmt) 297*70b19398SWenmeng Liu { 298*70b19398SWenmeng Liu struct tpg_device *tpg = v4l2_get_subdevdata(sd); 299*70b19398SWenmeng Liu struct v4l2_mbus_framefmt *format; 300*70b19398SWenmeng Liu 301*70b19398SWenmeng Liu format = __tpg_get_format(tpg, sd_state, fmt->pad, fmt->which); 302*70b19398SWenmeng Liu if (!format) 303*70b19398SWenmeng Liu return -EINVAL; 304*70b19398SWenmeng Liu 305*70b19398SWenmeng Liu tpg_try_format(tpg, &fmt->format); 306*70b19398SWenmeng Liu *format = fmt->format; 307*70b19398SWenmeng Liu 308*70b19398SWenmeng Liu return 0; 309*70b19398SWenmeng Liu } 310*70b19398SWenmeng Liu 311*70b19398SWenmeng Liu static int tpg_init_formats(struct v4l2_subdev *sd, 312*70b19398SWenmeng Liu struct v4l2_subdev_fh *fh) 313*70b19398SWenmeng Liu { 314*70b19398SWenmeng Liu struct v4l2_subdev_format format = { 315*70b19398SWenmeng Liu .pad = MSM_TPG_PAD_SRC, 316*70b19398SWenmeng Liu .which = fh ? V4L2_SUBDEV_FORMAT_TRY : 317*70b19398SWenmeng Liu V4L2_SUBDEV_FORMAT_ACTIVE, 318*70b19398SWenmeng Liu .format = { 319*70b19398SWenmeng Liu .code = MEDIA_BUS_FMT_SBGGR8_1X8, 320*70b19398SWenmeng Liu .width = 1920, 321*70b19398SWenmeng Liu .height = 1080, 322*70b19398SWenmeng Liu } 323*70b19398SWenmeng Liu }; 324*70b19398SWenmeng Liu 325*70b19398SWenmeng Liu return tpg_set_format(sd, fh ? fh->state : NULL, &format); 326*70b19398SWenmeng Liu } 327*70b19398SWenmeng Liu 328*70b19398SWenmeng Liu static int tpg_s_ctrl(struct v4l2_ctrl *ctrl) 329*70b19398SWenmeng Liu { 330*70b19398SWenmeng Liu struct tpg_device *tpg = container_of(ctrl->handler, 331*70b19398SWenmeng Liu struct tpg_device, ctrls); 332*70b19398SWenmeng Liu int ret = -EINVAL; 333*70b19398SWenmeng Liu 334*70b19398SWenmeng Liu switch (ctrl->id) { 335*70b19398SWenmeng Liu case V4L2_CID_TEST_PATTERN: 336*70b19398SWenmeng Liu ret = tpg->res->hw_ops->configure_testgen_pattern(tpg, ctrl->val); 337*70b19398SWenmeng Liu break; 338*70b19398SWenmeng Liu } 339*70b19398SWenmeng Liu 340*70b19398SWenmeng Liu return ret; 341*70b19398SWenmeng Liu } 342*70b19398SWenmeng Liu 343*70b19398SWenmeng Liu static const struct v4l2_ctrl_ops tpg_ctrl_ops = { 344*70b19398SWenmeng Liu .s_ctrl = tpg_s_ctrl, 345*70b19398SWenmeng Liu }; 346*70b19398SWenmeng Liu 347*70b19398SWenmeng Liu int msm_tpg_subdev_init(struct camss *camss, 348*70b19398SWenmeng Liu struct tpg_device *tpg, 349*70b19398SWenmeng Liu const struct camss_subdev_resources *res, u8 id) 350*70b19398SWenmeng Liu { 351*70b19398SWenmeng Liu struct platform_device *pdev; 352*70b19398SWenmeng Liu struct device *dev; 353*70b19398SWenmeng Liu int i, j; 354*70b19398SWenmeng Liu 355*70b19398SWenmeng Liu dev = camss->dev; 356*70b19398SWenmeng Liu pdev = to_platform_device(dev); 357*70b19398SWenmeng Liu 358*70b19398SWenmeng Liu tpg->camss = camss; 359*70b19398SWenmeng Liu tpg->id = id; 360*70b19398SWenmeng Liu tpg->res = &res->tpg; 361*70b19398SWenmeng Liu tpg->res->hw_ops->subdev_init(tpg); 362*70b19398SWenmeng Liu 363*70b19398SWenmeng Liu tpg->base = devm_platform_ioremap_resource_byname(pdev, res->reg[0]); 364*70b19398SWenmeng Liu if (IS_ERR(tpg->base)) 365*70b19398SWenmeng Liu return PTR_ERR(tpg->base); 366*70b19398SWenmeng Liu 367*70b19398SWenmeng Liu tpg->nclocks = 0; 368*70b19398SWenmeng Liu while (res->clock[tpg->nclocks]) 369*70b19398SWenmeng Liu tpg->nclocks++; 370*70b19398SWenmeng Liu 371*70b19398SWenmeng Liu if (!tpg->nclocks) 372*70b19398SWenmeng Liu return 0; 373*70b19398SWenmeng Liu 374*70b19398SWenmeng Liu tpg->clock = devm_kcalloc(dev, tpg->nclocks, 375*70b19398SWenmeng Liu sizeof(*tpg->clock), GFP_KERNEL); 376*70b19398SWenmeng Liu if (!tpg->clock) 377*70b19398SWenmeng Liu return -ENOMEM; 378*70b19398SWenmeng Liu 379*70b19398SWenmeng Liu for (i = 0; i < tpg->nclocks; i++) { 380*70b19398SWenmeng Liu struct camss_clock *clock = &tpg->clock[i]; 381*70b19398SWenmeng Liu 382*70b19398SWenmeng Liu clock->clk = devm_clk_get(dev, res->clock[i]); 383*70b19398SWenmeng Liu if (IS_ERR(clock->clk)) 384*70b19398SWenmeng Liu return PTR_ERR(clock->clk); 385*70b19398SWenmeng Liu 386*70b19398SWenmeng Liu clock->name = res->clock[i]; 387*70b19398SWenmeng Liu 388*70b19398SWenmeng Liu clock->nfreqs = 0; 389*70b19398SWenmeng Liu while (res->clock_rate[i][clock->nfreqs]) 390*70b19398SWenmeng Liu clock->nfreqs++; 391*70b19398SWenmeng Liu 392*70b19398SWenmeng Liu if (!clock->nfreqs) { 393*70b19398SWenmeng Liu clock->freq = NULL; 394*70b19398SWenmeng Liu continue; 395*70b19398SWenmeng Liu } 396*70b19398SWenmeng Liu 397*70b19398SWenmeng Liu clock->freq = devm_kcalloc(dev, clock->nfreqs, 398*70b19398SWenmeng Liu sizeof(*clock->freq), GFP_KERNEL); 399*70b19398SWenmeng Liu if (!clock->freq) 400*70b19398SWenmeng Liu return -ENOMEM; 401*70b19398SWenmeng Liu 402*70b19398SWenmeng Liu for (j = 0; j < clock->nfreqs; j++) 403*70b19398SWenmeng Liu clock->freq[j] = res->clock_rate[i][j]; 404*70b19398SWenmeng Liu } 405*70b19398SWenmeng Liu 406*70b19398SWenmeng Liu return 0; 407*70b19398SWenmeng Liu } 408*70b19398SWenmeng Liu 409*70b19398SWenmeng Liu static int tpg_link_setup(struct media_entity *entity, 410*70b19398SWenmeng Liu const struct media_pad *local, 411*70b19398SWenmeng Liu const struct media_pad *remote, u32 flags) 412*70b19398SWenmeng Liu { 413*70b19398SWenmeng Liu if (flags & MEDIA_LNK_FL_ENABLED) 414*70b19398SWenmeng Liu if (media_pad_remote_pad_first(local)) 415*70b19398SWenmeng Liu return -EBUSY; 416*70b19398SWenmeng Liu 417*70b19398SWenmeng Liu return 0; 418*70b19398SWenmeng Liu } 419*70b19398SWenmeng Liu 420*70b19398SWenmeng Liu static const struct v4l2_subdev_core_ops tpg_core_ops = { 421*70b19398SWenmeng Liu .s_power = tpg_set_power, 422*70b19398SWenmeng Liu }; 423*70b19398SWenmeng Liu 424*70b19398SWenmeng Liu static const struct v4l2_subdev_video_ops tpg_video_ops = { 425*70b19398SWenmeng Liu .s_stream = tpg_set_stream, 426*70b19398SWenmeng Liu }; 427*70b19398SWenmeng Liu 428*70b19398SWenmeng Liu static const struct v4l2_subdev_pad_ops tpg_pad_ops = { 429*70b19398SWenmeng Liu .enum_mbus_code = tpg_enum_mbus_code, 430*70b19398SWenmeng Liu .enum_frame_size = tpg_enum_frame_size, 431*70b19398SWenmeng Liu .get_fmt = tpg_get_format, 432*70b19398SWenmeng Liu .set_fmt = tpg_set_format, 433*70b19398SWenmeng Liu }; 434*70b19398SWenmeng Liu 435*70b19398SWenmeng Liu static const struct v4l2_subdev_ops tpg_v4l2_ops = { 436*70b19398SWenmeng Liu .core = &tpg_core_ops, 437*70b19398SWenmeng Liu .video = &tpg_video_ops, 438*70b19398SWenmeng Liu .pad = &tpg_pad_ops, 439*70b19398SWenmeng Liu }; 440*70b19398SWenmeng Liu 441*70b19398SWenmeng Liu static const struct v4l2_subdev_internal_ops tpg_v4l2_internal_ops = { 442*70b19398SWenmeng Liu .open = tpg_init_formats, 443*70b19398SWenmeng Liu }; 444*70b19398SWenmeng Liu 445*70b19398SWenmeng Liu static const struct media_entity_operations tpg_media_ops = { 446*70b19398SWenmeng Liu .link_setup = tpg_link_setup, 447*70b19398SWenmeng Liu .link_validate = v4l2_subdev_link_validate, 448*70b19398SWenmeng Liu }; 449*70b19398SWenmeng Liu 450*70b19398SWenmeng Liu int msm_tpg_register_entity(struct tpg_device *tpg, 451*70b19398SWenmeng Liu struct v4l2_device *v4l2_dev) 452*70b19398SWenmeng Liu { 453*70b19398SWenmeng Liu struct v4l2_subdev *sd = &tpg->subdev; 454*70b19398SWenmeng Liu struct device *dev = tpg->camss->dev; 455*70b19398SWenmeng Liu int ret; 456*70b19398SWenmeng Liu 457*70b19398SWenmeng Liu v4l2_subdev_init(sd, &tpg_v4l2_ops); 458*70b19398SWenmeng Liu sd->internal_ops = &tpg_v4l2_internal_ops; 459*70b19398SWenmeng Liu sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | 460*70b19398SWenmeng Liu V4L2_SUBDEV_FL_HAS_EVENTS; 461*70b19398SWenmeng Liu snprintf(sd->name, ARRAY_SIZE(sd->name), "%s%d", 462*70b19398SWenmeng Liu "msm_tpg", tpg->id); 463*70b19398SWenmeng Liu sd->grp_id = TPG_GRP_ID; 464*70b19398SWenmeng Liu v4l2_set_subdevdata(sd, tpg); 465*70b19398SWenmeng Liu 466*70b19398SWenmeng Liu ret = v4l2_ctrl_handler_init(&tpg->ctrls, 1); 467*70b19398SWenmeng Liu if (ret < 0) { 468*70b19398SWenmeng Liu dev_err(dev, "Failed to init ctrl handler: %d\n", ret); 469*70b19398SWenmeng Liu return ret; 470*70b19398SWenmeng Liu } 471*70b19398SWenmeng Liu 472*70b19398SWenmeng Liu tpg->testgen_mode = v4l2_ctrl_new_std_menu_items(&tpg->ctrls, 473*70b19398SWenmeng Liu &tpg_ctrl_ops, V4L2_CID_TEST_PATTERN, 474*70b19398SWenmeng Liu tpg->testgen.nmodes, 0, 0, 475*70b19398SWenmeng Liu tpg->testgen.modes); 476*70b19398SWenmeng Liu if (tpg->ctrls.error) { 477*70b19398SWenmeng Liu dev_err(dev, "Failed to init ctrl: %d\n", tpg->ctrls.error); 478*70b19398SWenmeng Liu ret = tpg->ctrls.error; 479*70b19398SWenmeng Liu goto free_ctrl; 480*70b19398SWenmeng Liu } 481*70b19398SWenmeng Liu 482*70b19398SWenmeng Liu tpg->subdev.ctrl_handler = &tpg->ctrls; 483*70b19398SWenmeng Liu 484*70b19398SWenmeng Liu ret = tpg_init_formats(sd, NULL); 485*70b19398SWenmeng Liu if (ret < 0) { 486*70b19398SWenmeng Liu dev_err(dev, "Failed to init format: %d\n", ret); 487*70b19398SWenmeng Liu goto free_ctrl; 488*70b19398SWenmeng Liu } 489*70b19398SWenmeng Liu 490*70b19398SWenmeng Liu tpg->pad.flags = MEDIA_PAD_FL_SOURCE; 491*70b19398SWenmeng Liu 492*70b19398SWenmeng Liu sd->entity.ops = &tpg_media_ops; 493*70b19398SWenmeng Liu ret = media_entity_pads_init(&sd->entity, 1, &tpg->pad); 494*70b19398SWenmeng Liu if (ret < 0) { 495*70b19398SWenmeng Liu dev_err(dev, "Failed to init media entity: %d\n", ret); 496*70b19398SWenmeng Liu goto free_ctrl; 497*70b19398SWenmeng Liu } 498*70b19398SWenmeng Liu 499*70b19398SWenmeng Liu ret = v4l2_device_register_subdev(v4l2_dev, sd); 500*70b19398SWenmeng Liu if (ret < 0) { 501*70b19398SWenmeng Liu dev_err(dev, "Failed to register subdev: %d\n", ret); 502*70b19398SWenmeng Liu media_entity_cleanup(&sd->entity); 503*70b19398SWenmeng Liu goto free_ctrl; 504*70b19398SWenmeng Liu } 505*70b19398SWenmeng Liu 506*70b19398SWenmeng Liu return 0; 507*70b19398SWenmeng Liu 508*70b19398SWenmeng Liu free_ctrl: 509*70b19398SWenmeng Liu v4l2_ctrl_handler_free(&tpg->ctrls); 510*70b19398SWenmeng Liu 511*70b19398SWenmeng Liu return ret; 512*70b19398SWenmeng Liu } 513*70b19398SWenmeng Liu 514*70b19398SWenmeng Liu void msm_tpg_unregister_entity(struct tpg_device *tpg) 515*70b19398SWenmeng Liu { 516*70b19398SWenmeng Liu v4l2_device_unregister_subdev(&tpg->subdev); 517*70b19398SWenmeng Liu media_entity_cleanup(&tpg->subdev.entity); 518*70b19398SWenmeng Liu v4l2_ctrl_handler_free(&tpg->ctrls); 519*70b19398SWenmeng Liu } 520