static int mxr_runtime_suspend(struct device *dev) { struct mxr_device *mdev = to_mdev(dev); struct mxr_resources *res = &mdev->res; struct s5p_mxr_platdata *pdata = mdev->pdata; mxr_dbg(mdev, "suspend - start\n"); mutex_lock(&mdev->mutex); /* disable system mmu for tv. It must be disabled before disabling * mixer's clock. Because of system mmu limitation. */ mdev->vb2->suspend(mdev->alloc_ctx); /* turn clocks off */ #if defined(CONFIG_ARCH_EXYNOS4) clk_disable_unprepare(res->vp); #endif clk_disable_unprepare(res->mixer); if (is_ip_ver_5a || is_ip_ver_5s || is_ip_ver_5s2) clk_disable_unprepare(res->axi_disp1); mutex_unlock(&mdev->mutex); mxr_dbg(mdev, "suspend - finished\n"); return 0; }
void mxr_output_put(struct mxr_device *mdev) { mutex_lock(&mdev->mutex); --mdev->n_output; mxr_dbg(mdev, "%s(%d)\n", __func__, mdev->n_output); /* turn on auxiliary driver */ if (mdev->n_output == 0) v4l2_subdev_call(to_outsd(mdev), core, s_power, 0); WARN(mdev->n_output < 0, "negative number of output users (%d)\n", mdev->n_output); mutex_unlock(&mdev->mutex); }
static int mxr_runtime_suspend(struct device *dev) { struct mxr_device *mdev = to_mdev(dev); struct mxr_resources *res = &mdev->res; mxr_dbg(mdev, "suspend - start\n"); mutex_lock(&mdev->mutex); /* disable system mmu for tv. It must be disabled before disabling * mixer's clock. Because of system mmu limitation. */ mdev->vb2->suspend(mdev->alloc_ctx); /* turn clocks off */ #if defined(CONFIG_CPU_EXYNOS4210) clk_disable(res->sclk_mixer); #endif #if defined(CONFIG_ARCH_EXYNOS4) clk_disable(res->vp); #endif clk_disable(res->mixer); mutex_unlock(&mdev->mutex); mxr_dbg(mdev, "suspend - finished\n"); return 0; }
static int mxr_runtime_resume(struct device *dev) { struct mxr_device *mdev = to_mdev(dev); struct mxr_resources *res = &mdev->res; int ret; mxr_dbg(mdev, "resume - start\n"); mutex_lock(&mdev->mutex); /* turn clocks on */ ret = clk_prepare_enable(res->mixer); if (ret < 0) { dev_err(mdev->dev, "clk_prepare_enable(mixer) failed\n"); goto fail; } ret = clk_prepare_enable(res->vp); if (ret < 0) { dev_err(mdev->dev, "clk_prepare_enable(vp) failed\n"); goto fail_mixer; } ret = clk_prepare_enable(res->sclk_mixer); if (ret < 0) { dev_err(mdev->dev, "clk_prepare_enable(sclk_mixer) failed\n"); goto fail_vp; } /* apply default configuration */ mxr_reg_reset(mdev); mxr_dbg(mdev, "resume - finished\n"); mutex_unlock(&mdev->mutex); return 0; fail_vp: clk_disable_unprepare(res->vp); fail_mixer: clk_disable_unprepare(res->mixer); fail: mutex_unlock(&mdev->mutex); dev_err(mdev->dev, "resume failed\n"); return ret; }
/* When mixer is connected to gscaler through local path, only gscaler's * video device can command alpha blending functionality for mixer */ static int mxr_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) { struct mxr_device *mdev = sd_to_mdev(sd); int v = ctrl->value; int num = 0; mxr_dbg(mdev, "%s start\n", __func__); mxr_dbg(mdev, "id = %d, value = %d\n", ctrl->id, ctrl->value); if (!strcmp(sd->name, "s5p-mixer0")) num = MXR_SUB_MIXER0; else if (!strcmp(sd->name, "s5p-mixer1")) num = MXR_SUB_MIXER1; switch (ctrl->id) { case V4L2_CID_TV_LAYER_BLEND_ENABLE: mdev->sub_mxr[num].layer[MXR_LAYER_VIDEO]->layer_blend_en = v; break; case V4L2_CID_TV_LAYER_BLEND_ALPHA: mdev->sub_mxr[num].layer[MXR_LAYER_VIDEO]->layer_alpha = (u32)v; break; case V4L2_CID_TV_PIXEL_BLEND_ENABLE: mdev->sub_mxr[num].layer[MXR_LAYER_VIDEO]->pixel_blend_en = v; break; case V4L2_CID_TV_CHROMA_ENABLE: mdev->sub_mxr[num].layer[MXR_LAYER_VIDEO]->chroma_en = v; break; case V4L2_CID_TV_CHROMA_VALUE: mdev->sub_mxr[num].layer[MXR_LAYER_VIDEO]->chroma_val = (u32)v; break; default: mxr_err(mdev, "invalid control id\n"); return -EINVAL; } return 0; }
void mxr_power_put(struct mxr_device *mdev) { --mdev->n_power; mxr_dbg(mdev, "%s(%d)\n", __func__, mdev->n_power); /* If runtime PM is not implemented, mxr_runtime_suspend * function is directly called. */ if (mdev->n_power == 0) { #ifdef CONFIG_PM_RUNTIME pm_runtime_put_sync(mdev->dev); #else mxr_runtime_suspend(mdev->dev); #endif } }
static int mxr_s_crop(struct file *file, void *fh, struct v4l2_crop *a) { struct mxr_layer *layer = video_drvdata(file); struct mxr_crop *crop; mxr_dbg(layer->mdev, "%s:%d\n", __func__, __LINE__); crop = choose_crop_by_type(&layer->geo, a->type); if (crop == NULL) return -EINVAL; crop->x_offset = a->c.left; crop->y_offset = a->c.top; crop->width = a->c.width; crop->height = a->c.height; mxr_layer_geo_fix(layer); return 0; }
static void mxr_video_fix_geometry(struct mxr_layer *layer) { struct mxr_geometry *geo = &layer->geo; mxr_dbg(layer->mdev, "%s start\n", __func__); geo->dst.x_offset = clamp_val(geo->dst.x_offset, 0, geo->dst.full_width - 1); geo->dst.y_offset = clamp_val(geo->dst.y_offset, 0, geo->dst.full_height - 1); /* mixer scale-up is unuseful. so no use it */ geo->dst.width = clamp_val(geo->dst.width, 1, geo->dst.full_width - geo->dst.x_offset); geo->dst.height = clamp_val(geo->dst.height, 1, geo->dst.full_height - geo->dst.y_offset); }
static int mxr_querycap(struct file *file, void *priv, struct v4l2_capability *cap) { struct mxr_layer *layer = video_drvdata(file); mxr_dbg(layer->mdev, "%s:%d\n", __func__, __LINE__); strlcpy(cap->driver, MXR_DRIVER_NAME, sizeof cap->driver); strlcpy(cap->card, layer->vfd.name, sizeof cap->card); snprintf(cap->bus_info, sizeof(cap->bus_info), "%d", layer->idx); cap->version = KERNEL_VERSION(0, 1, 0); cap->capabilities = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_VIDEO_OUTPUT_MPLANE; return 0; }
static int mxr_enum_fmt(struct file *file, void *priv, struct v4l2_fmtdesc *f) { struct mxr_layer *layer = video_drvdata(file); struct mxr_device *mdev = layer->mdev; const struct mxr_format *fmt; mxr_dbg(mdev, "%s\n", __func__); fmt = find_format_by_index(layer, f->index); if (fmt == NULL) return -EINVAL; strlcpy(f->description, fmt->name, sizeof(f->description)); f->pixelformat = fmt->fourcc; return 0; }
static int mxr_g_fmt(struct file *file, void *priv, struct v4l2_format *f) { struct mxr_layer *layer = video_drvdata(file); struct v4l2_pix_format_mplane *pix = &f->fmt.pix_mp; mxr_dbg(layer->mdev, "%s:%d\n", __func__, __LINE__); pix->width = layer->geo.src.full_width; pix->height = layer->geo.src.full_height; pix->field = V4L2_FIELD_NONE; pix->pixelformat = layer->fmt->fourcc; pix->colorspace = layer->fmt->colorspace; mxr_mplane_fill(pix->plane_fmt, layer->fmt, pix->width, pix->height); return 0; }
int mxr_power_get(struct mxr_device *mdev) { int ret = 0; ++mdev->n_power; mxr_dbg(mdev, "%s(%d)\n", __func__, mdev->n_power); /* If runtime PM is not implemented, mxr_runtime_resume * function is directly called. */ if (mdev->n_power == 1) { #ifdef CONFIG_PM_RUNTIME ret = pm_runtime_get_sync(mdev->dev); #else mxr_runtime_resume(mdev->dev); #endif } return ret; }
static void mxr_geometry_dump(struct mxr_device *mdev, struct mxr_geometry *geo) { mxr_dbg(mdev, "src.full_size = (%u, %u)\n", geo->src.full_width, geo->src.full_height); mxr_dbg(mdev, "src.size = (%u, %u)\n", geo->src.width, geo->src.height); mxr_dbg(mdev, "src.offset = (%u, %u)\n", geo->src.x_offset, geo->src.y_offset); mxr_dbg(mdev, "dst.full_size = (%u, %u)\n", geo->dst.full_width, geo->dst.full_height); mxr_dbg(mdev, "dst.size = (%u, %u)\n", geo->dst.width, geo->dst.height); mxr_dbg(mdev, "dst.offset = (%u, %u)\n", geo->dst.x_offset, geo->dst.y_offset); mxr_dbg(mdev, "ratio = (%u, %u)\n", geo->x_ratio, geo->y_ratio); }
static int mxr_s_dv_preset(struct file *file, void *fh, struct v4l2_dv_preset *preset) { struct mxr_layer *layer = video_drvdata(file); struct mxr_device *mdev = layer->mdev; int ret; mxr_dbg(mdev, "%s start\n", __func__); /* lock protects from changing sd_out */ mutex_lock(&mdev->mutex); ret = v4l2_subdev_call(to_outsd(mdev), video, s_dv_preset, preset); mutex_unlock(&mdev->mutex); /* any failure should return EINVAL according to V4L2 doc */ return ret ? -EINVAL : 0; }
static int mxr_video_release(struct file *file) { struct mxr_layer *layer = video_drvdata(file); mxr_dbg(layer->mdev, "%s:%d\n", __func__, __LINE__); /* initialize alpha blending variables */ layer->layer_blend_en = 0; layer->layer_alpha = 0; layer->pixel_blend_en = 0; layer->chroma_en = 0; layer->chroma_val = 0; if (v4l2_fh_is_singular_file(file)) vb2_queue_release(&layer->vb_queue); v4l2_fh_release(file); return 0; }
static void mxr_vp_fix_geometry(struct mxr_layer *layer) { struct mxr_geometry *geo = &layer->geo; mxr_dbg(layer->mdev, "%s start\n", __func__); /* align horizontal size to 8 pixels */ geo->src.full_width = ALIGN(geo->src.full_width, 8); /* limit to boundary size */ geo->src.full_width = clamp_val(geo->src.full_width, 8, 8192); geo->src.full_height = clamp_val(geo->src.full_height, 1, 8192); geo->src.width = clamp_val(geo->src.width, 32, geo->src.full_width); geo->src.width = min(geo->src.width, 2047U); geo->src.height = clamp_val(geo->src.height, 4, geo->src.full_height); geo->src.height = min(geo->src.height, 2047U); /* setting size of output window */ geo->dst.width = clamp_val(geo->dst.width, 8, geo->dst.full_width); geo->dst.height = clamp_val(geo->dst.height, 1, geo->dst.full_height); /* ensure that scaling is in range 1/4x to 16x */ if (geo->src.width >= 4 * geo->dst.width) geo->src.width = 4 * geo->dst.width; if (geo->dst.width >= 16 * geo->src.width) geo->dst.width = 16 * geo->src.width; if (geo->src.height >= 4 * geo->dst.height) geo->src.height = 4 * geo->dst.height; if (geo->dst.height >= 16 * geo->src.height) geo->dst.height = 16 * geo->src.height; /* setting scaling ratio */ geo->x_ratio = (geo->src.width << 16) / geo->dst.width; geo->y_ratio = (geo->src.height << 16) / geo->dst.height; /* adjust offsets */ geo->src.x_offset = min(geo->src.x_offset, geo->src.full_width - geo->src.width); geo->src.y_offset = min(geo->src.y_offset, geo->src.full_height - geo->src.height); geo->dst.x_offset = min(geo->dst.x_offset, geo->dst.full_width - geo->dst.width); geo->dst.y_offset = min(geo->dst.y_offset, geo->dst.full_height - geo->dst.height); }
static int mxr_cropcap(struct file *file, void *fh, struct v4l2_cropcap *a) { struct mxr_layer *layer = video_drvdata(file); struct mxr_crop *crop; mxr_dbg(layer->mdev, "%s:%d\n", __func__, __LINE__); crop = choose_crop_by_type(&layer->geo, a->type); if (crop == NULL) return -EINVAL; mxr_layer_geo_fix(layer); a->bounds.left = 0; a->bounds.top = 0; a->bounds.width = crop->full_width; a->bounds.top = crop->full_height; a->defrect = a->bounds; /* setting pixel aspect to 1/1 */ a->pixelaspect.numerator = 1; a->pixelaspect.denominator = 1; return 0; }
static int mxr_s_output(struct file *file, void *fh, unsigned int i) { struct video_device *vfd = video_devdata(file); struct mxr_layer *layer = video_drvdata(file); struct mxr_device *mdev = layer->mdev; int ret = 0; if (i >= mdev->output_cnt || mdev->output[i] == NULL) return -EINVAL; mutex_lock(&mdev->mutex); mdev->current_output = i; vfd->tvnorms = 0; v4l2_subdev_call(to_outsd(mdev), video, g_tvnorms_output, &vfd->tvnorms); mutex_unlock(&mdev->mutex); mxr_dbg(mdev, "tvnorms = %08llx\n", vfd->tvnorms); return ret; }
void mxr_streamer_put(struct mxr_device *mdev) { mutex_lock(&mdev->mutex); --mdev->n_streamer; mxr_dbg(mdev, "%s(%d)\n", __func__, mdev->n_streamer); if (mdev->n_streamer == 0) { int ret; struct v4l2_subdev *sd = to_outsd(mdev); mxr_reg_streamoff(mdev); /* vsync applies Mixer setup */ ret = mxr_reg_wait4vsync(mdev); WARN(ret, "failed to get vsync (%d) from output\n", ret); ret = v4l2_subdev_call(sd, video, s_stream, 0); WARN(ret, "stopping stream failed for output %s\n", sd->name); } WARN(mdev->n_streamer < 0, "negative number of streamers (%d)\n", mdev->n_streamer); mutex_unlock(&mdev->mutex); mxr_reg_dump(mdev); }
void mxr_layer_default_geo(struct mxr_layer *layer) { struct mxr_device *mdev = layer->mdev; struct v4l2_mbus_framefmt mbus_fmt; mxr_dbg(layer->mdev, "%s start\n", __func__); memset(&layer->geo, 0, sizeof layer->geo); mxr_get_mbus_fmt(mdev, &mbus_fmt); layer->geo.dst.full_width = mbus_fmt.width; layer->geo.dst.full_height = mbus_fmt.height; layer->geo.dst.width = layer->geo.dst.full_width; layer->geo.dst.height = layer->geo.dst.full_height; layer->geo.dst.field = mbus_fmt.field; layer->geo.src.full_width = mbus_fmt.width; layer->geo.src.full_height = mbus_fmt.height; layer->geo.src.width = layer->geo.src.full_width; layer->geo.src.height = layer->geo.src.full_height; layer->ops.fix_geometry(layer); }
static int mxr_video_open(struct file *file) { struct mxr_layer *layer = video_drvdata(file); struct mxr_device *mdev = layer->mdev; int ret = 0; mxr_dbg(mdev, "%s:%d\n", __func__, __LINE__); /* assure device probe is finished */ wait_for_device_probe(); /* creating context for file descriptor */ ret = v4l2_fh_open(file); if (ret) { mxr_err(mdev, "v4l2_fh_open failed\n"); return ret; } /* leaving if layer is already initialized */ if (!v4l2_fh_is_singular_file(file)) return 0; ret = vb2_queue_init(&layer->vb_queue); if (ret != 0) { mxr_err(mdev, "failed to initialize vb2 queue\n"); goto fail_fh_open; } /* set default format, first on the list */ layer->fmt = layer->fmt_array[0]; /* setup default geometry */ mxr_layer_default_geo(layer); return 0; fail_fh_open: v4l2_fh_release(file); return ret; }
static int mxr_streamer_put(struct mxr_device *mdev, struct v4l2_subdev *sd) { int i; int ret = 0; int local = 1; struct media_pad *pad; struct sub_mxr_device *sub_mxr; struct mxr_layer *layer; struct v4l2_subdev *hdmi_sd; struct v4l2_subdev *gsc_sd; struct exynos_entity_data *md_data; mutex_lock(&mdev->s_mutex); --mdev->n_streamer; mxr_dbg(mdev, "%s(%d)\n", __func__, mdev->n_streamer); /* distinction number of local path */ if (mdev->mxr_data_from == FROM_GSC_SD) { local = 0; for (i = 0; i < MXR_MAX_SUB_MIXERS; ++i) { sub_mxr = &mdev->sub_mxr[i]; if (sub_mxr->local) local += sub_mxr->local; } if (local == 2) mxr_layer_sync(mdev, MXR_DISABLE); } if ((mdev->n_streamer == 0 && local == 1) || (mdev->n_streamer == 1 && local == 2)) { for (i = MXR_PAD_SOURCE_GSCALER; i < MXR_PADS_NUM; ++i) { pad = &sd->entity.pads[i]; /* find sink pad of output via enabled link*/ pad = media_entity_remote_source(pad); if (pad) if (media_entity_type(pad->entity) == MEDIA_ENT_T_V4L2_SUBDEV) break; if (i == MXR_PAD_SOURCE_GRP1) { ret = -ENODEV; goto out; } } hdmi_sd = media_entity_to_v4l2_subdev(pad->entity); mxr_reg_streamoff(mdev); /* vsync applies Mixer setup */ ret = mxr_reg_wait4update(mdev); if (ret) { mxr_err(mdev, "failed to get vsync (%d) from output\n", ret); goto out; } } /* When using local path between gscaler and mixer, below stop sequence * must be processed */ if (mdev->mxr_data_from == FROM_GSC_SD) { pad = &sd->entity.pads[MXR_PAD_SINK_GSCALER]; pad = media_entity_remote_source(pad); if (pad) { gsc_sd = media_entity_to_v4l2_subdev( pad->entity); mxr_dbg(mdev, "stop from %s\n", gsc_sd->name); md_data = (struct exynos_entity_data *) gsc_sd->dev_priv; md_data->media_ops->power_off(gsc_sd); } } if ((mdev->n_streamer == 0 && local == 1) || (mdev->n_streamer == 1 && local == 2)) { ret = v4l2_subdev_call(hdmi_sd, video, s_stream, 0); if (ret) { mxr_err(mdev, "stopping stream failed for output %s\n", hdmi_sd->name); goto out; } } /* turn off connected output device through link * with mixer */ if (mdev->mxr_data_from == FROM_GSC_SD) { for (i = 0; i < MXR_MAX_SUB_MIXERS; ++i) { sub_mxr = &mdev->sub_mxr[i]; if (sub_mxr->local) { layer = sub_mxr->layer[MXR_LAYER_VIDEO]; layer->ops.stream_set(layer, 0); layer->pipe.state = MXR_PIPELINE_IDLE; } } mxr_reg_local_path_clear(mdev); mxr_output_put(mdev); /* disable mixer clock */ mxr_power_put(mdev); } WARN(mdev->n_streamer < 0, "negative number of streamers (%d)\n", mdev->n_streamer); out: mutex_unlock(&mdev->s_mutex); mxr_reg_dump(mdev); return ret; }
static int mxr_streamer_get(struct mxr_device *mdev, struct v4l2_subdev *sd) { int i; int ret = 0; int local = 1; struct sub_mxr_device *sub_mxr; struct mxr_layer *layer; struct media_pad *pad; struct s5p_mxr_platdata *pdata = mdev->pdata; struct v4l2_mbus_framefmt mbus_fmt; struct v4l2_control ctrl; mutex_lock(&mdev->s_mutex); ++mdev->n_streamer; mxr_dbg(mdev, "%s(%d)\n", __func__, mdev->n_streamer); /* If pipeline is started from Gscaler input video device, * TV basic configuration must be set before running mixer */ if (mdev->mxr_data_from == FROM_GSC_SD) { mxr_dbg(mdev, "%s: from gscaler\n", __func__); local = 0; /* enable mixer clock */ ret = mxr_power_get(mdev); if (ret < 0) { mxr_err(mdev, "power on failed for video layer\n"); ret = -ENODEV; goto out; } for (i = 0; i < MXR_MAX_SUB_MIXERS; ++i) { sub_mxr = &mdev->sub_mxr[i]; if (sub_mxr->local) { layer = sub_mxr->layer[MXR_LAYER_VIDEO]; layer->pipe.state = MXR_PIPELINE_STREAMING; mxr_layer_geo_fix(layer); layer->ops.format_set(layer, layer->fmt, &layer->geo); layer->ops.stream_set(layer, 1); local += sub_mxr->local; } } if (local == 2) mxr_layer_sync(mdev, MXR_ENABLE); /* Set the TVOUT register about gsc-mixer local path */ mxr_reg_local_path_set(mdev); } /* Alpha blending configuration always can be changed * whenever streaming */ mxr_set_alpha_blend(mdev); mxr_reg_set_color_range(mdev); mxr_reg_set_layer_prio(mdev); if (is_ip_ver_5s || is_ip_ver_5s2) mxr_reg_set_resolution(mdev); if ((mdev->n_streamer == 1 && local == 1) || (mdev->n_streamer == 2 && local == 2)) { #if defined(CONFIG_TV_USE_BUS_DEVFREQ) if (is_ip_ver_5a) pm_qos_add_request(&exynos5_tv_mif_qos, PM_QOS_BUS_THROUGHPUT, 800000); pm_qos_add_request(&exynos5_tv_int_qos, PM_QOS_DEVICE_THROUGHPUT, 400000); #endif for (i = MXR_PAD_SOURCE_GSCALER; i < MXR_PADS_NUM; ++i) { pad = &sd->entity.pads[i]; /* find sink pad of output via enabled link*/ pad = media_entity_remote_source(pad); if (pad) if (media_entity_type(pad->entity) == MEDIA_ENT_T_V4L2_SUBDEV) break; if (i == MXR_PAD_SOURCE_GRP1) { ret = -ENODEV; goto out; } } sd = media_entity_to_v4l2_subdev(pad->entity); mxr_dbg(mdev, "cookie of current output = (%d)\n", to_output(mdev)->cookie); mxr_reg_s_output(mdev, to_output(mdev)->cookie); ret = v4l2_subdev_call(sd, video, g_mbus_fmt, &mbus_fmt); if (ret) { mxr_err(mdev, "failed to get mbus_fmt for output %s\n", sd->name); goto out; } ctrl.id = V4L2_CID_TV_GET_DVI_MODE; ret = v4l2_subdev_call(sd, core, g_ctrl, &ctrl); if (ret) { mxr_err(mdev, "failed to get DVI or HDMI mode %s\n", sd->name); goto out; } mxr_reg_set_mbus_fmt(mdev, &mbus_fmt, ctrl.value); ret = v4l2_subdev_call(sd, video, s_mbus_fmt, &mbus_fmt); if (ret) { mxr_err(mdev, "failed to set mbus_fmt for output %s\n", sd->name); goto out; } mxr_reg_streamon(mdev); /* start hdmi */ ctrl.id = V4L2_CID_TV_HDMI_STATUS; ret = v4l2_subdev_call(sd, core, g_ctrl, &ctrl); if (ret) { mxr_err(mdev, "failed to get output %s status for start\n", sd->name); goto out; } if (ctrl.value == (HDMI_STOP | HPD_HIGH)) { ret = v4l2_subdev_call(sd, core, s_power, 1); if (ret) { mxr_err(mdev, "failed to get power for output %s\n", sd->name); goto out; } ret = v4l2_subdev_call(sd, video, s_stream, 1); if (ret) { mxr_err(mdev, "starting stream failed for output %s\n", sd->name); goto out; } } ret = mxr_reg_wait4update(mdev); if (ret) { mxr_err(mdev, "failed to get vsync (%d) from output\n", ret); goto out; } } out: mutex_unlock(&mdev->s_mutex); mxr_reg_dump(mdev); return ret; }
static int mxr_streamer_get(struct mxr_device *mdev, struct v4l2_subdev *sd) { int i; int ret = 0; int local = 1; struct sub_mxr_device *sub_mxr; struct mxr_layer *layer; struct media_pad *pad; struct v4l2_mbus_framefmt mbus_fmt; #if defined(CONFIG_CPU_EXYNOS4210) struct mxr_resources *res = &mdev->res; #endif struct v4l2_control ctrl; mutex_lock(&mdev->s_mutex); ++mdev->n_streamer; mxr_dbg(mdev, "%s(%d)\n", __func__, mdev->n_streamer); /* If pipeline is started from Gscaler input video device, * TV basic configuration must be set before running mixer */ if (mdev->mxr_data_from == FROM_GSC_SD) { mxr_dbg(mdev, "%s: from gscaler\n", __func__); local = 0; /* enable mixer clock */ ret = mxr_power_get(mdev); if (ret) { mxr_err(mdev, "power on failed\n"); ret = -ENODEV; goto out; } /* turn on connected output device through link * with mixer */ mxr_output_get(mdev); for (i = 0; i < MXR_MAX_SUB_MIXERS; ++i) { sub_mxr = &mdev->sub_mxr[i]; if (sub_mxr->local) { layer = sub_mxr->layer[MXR_LAYER_VIDEO]; layer->pipe.state = MXR_PIPELINE_STREAMING; mxr_layer_geo_fix(layer); layer->ops.format_set(layer, layer->fmt, &layer->geo); layer->ops.stream_set(layer, 1); local += sub_mxr->local; } } if (local == 2) mxr_layer_sync(mdev, MXR_ENABLE); /* Set the TVOUT register about gsc-mixer local path */ mxr_reg_local_path_set(mdev, mdev->mxr0_gsc, mdev->mxr1_gsc, mdev->flags); } /* Alpha blending configuration always can be changed * whenever streaming */ mxr_set_alpha_blend(mdev); mxr_reg_set_color_range(mdev); mxr_reg_set_layer_prio(mdev); if ((mdev->n_streamer == 1 && local == 1) || (mdev->n_streamer == 2 && local == 2)) { for (i = MXR_PAD_SOURCE_GSCALER; i < MXR_PADS_NUM; ++i) { pad = &sd->entity.pads[i]; /* find sink pad of output via enabled link*/ pad = media_entity_remote_source(pad); if (pad) if (media_entity_type(pad->entity) == MEDIA_ENT_T_V4L2_SUBDEV) break; if (i == MXR_PAD_SOURCE_GRP1) { ret = -ENODEV; goto out; } } sd = media_entity_to_v4l2_subdev(pad->entity); mxr_dbg(mdev, "cookie of current output = (%d)\n", to_output(mdev)->cookie); #if defined(CONFIG_CPU_EXYNOS4210) if (to_output(mdev)->cookie == 0) clk_set_parent(res->sclk_mixer, res->sclk_dac); else clk_set_parent(res->sclk_mixer, res->sclk_hdmi); #endif mxr_reg_s_output(mdev, to_output(mdev)->cookie); ret = v4l2_subdev_call(sd, video, g_mbus_fmt, &mbus_fmt); if (ret) { mxr_err(mdev, "failed to get mbus_fmt for output %s\n", sd->name); goto out; } ctrl.id = V4L2_CID_TV_GET_DVI_MODE; ret = v4l2_subdev_call(sd, core, g_ctrl, &ctrl); if (ret) { mxr_err(mdev, "failed to get DVI or HDMI mode %s\n", sd->name); goto out; } mxr_reg_set_mbus_fmt(mdev, &mbus_fmt, ctrl.value); ret = v4l2_subdev_call(sd, video, s_mbus_fmt, &mbus_fmt); if (ret) { mxr_err(mdev, "failed to set mbus_fmt for output %s\n", sd->name); goto out; } mxr_reg_streamon(mdev); ret = v4l2_subdev_call(sd, video, s_stream, 1); if (ret) { mxr_err(mdev, "starting stream failed for output %s\n", sd->name); goto out; } ret = mxr_reg_wait4update(mdev); if (ret) { mxr_err(mdev, "failed to get vsync (%d) from output\n", ret); goto out; } } out: mutex_unlock(&mdev->s_mutex); mxr_reg_dump(mdev); return ret; }
static int mxr_streamer_put(struct mxr_device *mdev, struct v4l2_subdev *sd) { int i; int ret = 0; int local = 1; struct media_pad *pad; struct sub_mxr_device *sub_mxr; struct mxr_layer *layer; struct v4l2_subdev *hdmi_sd; struct v4l2_subdev *gsc_sd; struct exynos_entity_data *md_data; struct s5p_mxr_platdata *pdata = mdev->pdata; struct v4l2_control ctrl; mutex_lock(&mdev->s_mutex); --mdev->n_streamer; mxr_dbg(mdev, "%s(%d)\n", __func__, mdev->n_streamer); /* distinction number of local path */ if (mdev->mxr_data_from == FROM_GSC_SD) { local = 0; for (i = 0; i < MXR_MAX_SUB_MIXERS; ++i) { sub_mxr = &mdev->sub_mxr[i]; if (sub_mxr->local) local += sub_mxr->local; } if (local == 2) mxr_layer_sync(mdev, MXR_DISABLE); /* stop gscaler --> waiting for frame done */ pad = &sd->entity.pads[MXR_PAD_SINK_GSCALER]; pad = media_entity_remote_source(pad); if (pad) { gsc_sd = media_entity_to_v4l2_subdev( pad->entity); mxr_dbg(mdev, "stop from %s\n", gsc_sd->name); md_data = (struct exynos_entity_data *) gsc_sd->dev_priv; if (is_ip_ver_5g_1 || is_ip_ver_5a_0) md_data->media_ops->power_off(gsc_sd); } /* disable video layer */ for (i = 0; i < MXR_MAX_SUB_MIXERS; ++i) { sub_mxr = &mdev->sub_mxr[i]; if (sub_mxr->local) { layer = sub_mxr->layer[MXR_LAYER_VIDEO]; layer->ops.stream_set(layer, 0); layer->pipe.state = MXR_PIPELINE_IDLE; } } } if ((mdev->n_streamer == 0 && local == 1) || (mdev->n_streamer == 1 && local == 2)) { for (i = MXR_PAD_SOURCE_GSCALER; i < MXR_PADS_NUM; ++i) { pad = &sd->entity.pads[i]; /* find sink pad of output via enabled link*/ pad = media_entity_remote_source(pad); if (pad) if (media_entity_type(pad->entity) == MEDIA_ENT_T_V4L2_SUBDEV) break; if (i == MXR_PAD_SOURCE_GRP1) { ret = -ENODEV; goto out; } } hdmi_sd = media_entity_to_v4l2_subdev(pad->entity); mxr_reg_streamoff(mdev); /* vsync applies Mixer setup */ ret = mxr_reg_wait4update(mdev); if (ret) { mxr_err(mdev, "failed to get vsync (%d) from output\n", ret); goto out; } /* stop hdmi */ ctrl.id = V4L2_CID_TV_HDMI_STATUS; ret = v4l2_subdev_call(hdmi_sd, core, g_ctrl, &ctrl); if (ret) { mxr_err(mdev, "failed to get output %s status for stop\n", hdmi_sd->name); goto out; } /* * HDMI should be turn off only when not in use. * 1. cable out * 2. suspend (blank is called at suspend) */ if (ctrl.value == (HDMI_STREAMING | HPD_LOW) || mdev->blank) { ret = v4l2_subdev_call(hdmi_sd, video, s_stream, 0); if (ret) { mxr_err(mdev, "stopping stream failed for output %s\n", hdmi_sd->name); goto out; } ret = v4l2_subdev_call(hdmi_sd, core, s_power, 0); if (ret) { mxr_err(mdev, "failed to put power for output %s\n", hdmi_sd->name); goto out; } mdev->blank = 0; } } /* disable mixer clock */ if (mdev->mxr_data_from == FROM_GSC_SD) mxr_power_put(mdev); WARN(mdev->n_streamer < 0, "negative number of streamers (%d)\n", mdev->n_streamer); out: #if defined(CONFIG_TV_USE_BUS_DEVFREQ) if ((mdev->n_streamer == 0 && local == 1) || (mdev->n_streamer == 1 && local == 2)) { if (is_ip_ver_5a) pm_qos_remove_request(&exynos5_tv_mif_qos); pm_qos_remove_request(&exynos5_tv_int_qos); } #endif mutex_unlock(&mdev->s_mutex); mxr_reg_dump(mdev); return ret; }
static int mxr_streamer_put(struct mxr_device *mdev, struct v4l2_subdev *sd) { int i; int ret = 0; int local = 1; struct media_pad *pad; struct sub_mxr_device *sub_mxr; struct mxr_layer *layer; struct v4l2_subdev *gsc_sd; struct exynos_entity_data *md_data; struct s5p_mxr_platdata *pdata = mdev->pdata; mutex_lock(&mdev->s_mutex); --mdev->n_streamer; mxr_dbg(mdev, "%s(%d)\n", __func__, mdev->n_streamer); /* distinction number of local path */ if (mdev->mxr_data_from == FROM_GSC_SD) { local = 0; for (i = 0; i < MXR_MAX_SUB_MIXERS; ++i) { sub_mxr = &mdev->sub_mxr[i]; if (sub_mxr->local) local += sub_mxr->local; } if (local == 2) mxr_layer_sync(mdev, MXR_DISABLE); /* stop gscaler --> waiting for frame done */ pad = &sd->entity.pads[MXR_PAD_SINK_GSCALER]; pad = media_entity_remote_source(pad); if (pad) { gsc_sd = media_entity_to_v4l2_subdev( pad->entity); mxr_dbg(mdev, "stop from %s\n", gsc_sd->name); md_data = (struct exynos_entity_data *) gsc_sd->dev_priv; if (is_ip_ver_5g_1 || is_ip_ver_5a_0) md_data->media_ops->power_off(gsc_sd); } /* disable video layer */ for (i = 0; i < MXR_MAX_SUB_MIXERS; ++i) { sub_mxr = &mdev->sub_mxr[i]; if (sub_mxr->local) { layer = sub_mxr->layer[MXR_LAYER_VIDEO]; layer->ops.stream_set(layer, 0); layer->pipe.state = MXR_PIPELINE_IDLE; } } } if ((mdev->n_streamer == 0 && local == 1) || (mdev->n_streamer == 1 && local == 2)) { mxr_reg_streamoff(mdev); /* vsync applies Mixer setup */ ret = mxr_reg_wait4update(mdev); if (ret) { mxr_err(mdev, "failed to get vsync (%d) from output\n", ret); goto out; } } /* disable mixer clock */ if (mdev->mxr_data_from == FROM_GSC_SD) mxr_power_put(mdev); WARN(mdev->n_streamer < 0, "negative number of streamers (%d)\n", mdev->n_streamer); out: #if defined(CONFIG_ARM_EXYNOS5410_BUS_DEVFREQ) if ((mdev->n_streamer == 0 && local == 1) || (mdev->n_streamer == 1 && local == 2)) { pm_qos_remove_request(&exynos5_tv_mif_qos); pm_qos_remove_request(&exynos5_tv_int_qos); } #endif mutex_unlock(&mdev->s_mutex); mxr_reg_dump(mdev); return ret; }
static int mxr_s_ctrl(struct file *file, void *fh, struct v4l2_control *ctrl) { struct mxr_layer *layer = video_drvdata(file); struct mxr_device *mdev = layer->mdev; int v = ctrl->value; int ret; mxr_dbg(mdev, "%s start\n", __func__); ret = mxr_check_ctrl_val(ctrl); if (ret) { mxr_err(mdev, "alpha value is out of range\n"); return ret; } switch (ctrl->id) { case V4L2_CID_TV_LAYER_BLEND_ENABLE: layer->layer_blend_en = v; break; case V4L2_CID_TV_LAYER_BLEND_ALPHA: layer->layer_alpha = (u32)v; break; case V4L2_CID_TV_PIXEL_BLEND_ENABLE: layer->pixel_blend_en = v; break; case V4L2_CID_TV_CHROMA_ENABLE: layer->chroma_en = v; break; case V4L2_CID_TV_CHROMA_VALUE: layer->chroma_val = (u32)v; break; case V4L2_CID_TV_SET_COLOR_RANGE: mdev->color_range = v; v4l2_subdev_call(to_outsd(mdev), core, s_ctrl, ctrl); break; case V4L2_CID_TV_BLANK: mdev->blank = v; break; case V4L2_CID_TV_ENABLE_HDMI_AUDIO: case V4L2_CID_TV_SET_NUM_CHANNELS: case V4L2_CID_TV_HPD_STATUS: case V4L2_CID_TV_SET_DVI_MODE: case V4L2_CID_TV_SET_ASPECT_RATIO: case V4L2_CID_TV_HDCP_ENABLE: v4l2_subdev_call(to_outsd(mdev), core, s_ctrl, ctrl); break; case V4L2_CID_TV_LAYER_PRIO: layer->prio = (u8)v; /* This can be turned on/off each layer while streaming */ if (layer->pipe.state == MXR_PIPELINE_STREAMING) mxr_reg_set_layer_prio(mdev); break; case V4L2_CID_TV_UPDATE: ret = mxr_update(mdev); break; default: mxr_err(mdev, "invalid control id\n"); ret = -EINVAL; break; } return ret; }