diff options
Diffstat (limited to 'drivers/media/platform/s5p-tv/hdmi_drv.c')
| -rw-r--r-- | drivers/media/platform/s5p-tv/hdmi_drv.c | 201 |
1 files changed, 123 insertions, 78 deletions
diff --git a/drivers/media/platform/s5p-tv/hdmi_drv.c b/drivers/media/platform/s5p-tv/hdmi_drv.c index 8a9cf43018f..754740f4b67 100644 --- a/drivers/media/platform/s5p-tv/hdmi_drv.c +++ b/drivers/media/platform/s5p-tv/hdmi_drv.c @@ -31,11 +31,13 @@ #include <linux/pm_runtime.h> #include <linux/clk.h> #include <linux/regulator/consumer.h> +#include <linux/v4l2-dv-timings.h> #include <media/s5p_hdmi.h> #include <media/v4l2-common.h> #include <media/v4l2-dev.h> #include <media/v4l2-device.h> +#include <media/v4l2-dv-timings.h> #include "regs-hdmi.h" @@ -43,9 +45,6 @@ MODULE_AUTHOR("Tomasz Stanislawski, <t.stanislaws@samsung.com>"); MODULE_DESCRIPTION("Samsung HDMI"); MODULE_LICENSE("GPL"); -/* default preset configured on probe */ -#define HDMI_DEFAULT_PRESET V4L2_DV_480P59_94 - struct hdmi_pulse { u32 beg; u32 end; @@ -91,8 +90,8 @@ struct hdmi_device { const struct hdmi_timings *cur_conf; /** flag indicating that timings are dirty */ int cur_conf_dirty; - /** current preset */ - u32 cur_preset; + /** current timings */ + struct v4l2_dv_timings cur_timings; /** other resources */ struct hdmi_resources res; }; @@ -252,7 +251,6 @@ static int hdmi_conf_apply(struct hdmi_device *hdmi_dev) { struct device *dev = hdmi_dev->dev; const struct hdmi_timings *conf = hdmi_dev->cur_conf; - struct v4l2_dv_preset preset; int ret; dev_dbg(dev, "%s\n", __func__); @@ -267,11 +265,11 @@ static int hdmi_conf_apply(struct hdmi_device *hdmi_dev) hdmi_write_mask(hdmi_dev, HDMI_PHY_RSTOUT, 0, HDMI_PHY_SW_RSTOUT); mdelay(10); - /* configure presets */ - preset.preset = hdmi_dev->cur_preset; - ret = v4l2_subdev_call(hdmi_dev->phy_sd, video, s_dv_preset, &preset); + /* configure timings */ + ret = v4l2_subdev_call(hdmi_dev->phy_sd, video, s_dv_timings, + &hdmi_dev->cur_timings); if (ret) { - dev_err(dev, "failed to set preset (%u)\n", preset.preset); + dev_err(dev, "failed to set timings\n"); return ret; } @@ -475,33 +473,26 @@ static const struct hdmi_timings hdmi_timings_1080p50 = { .vsyn[0] = { .beg = 0 + 4, .end = 5 + 4}, }; +/* default hdmi_timings index of the timings configured on probe */ +#define HDMI_DEFAULT_TIMINGS_IDX (0) + static const struct { - u32 preset; - const struct hdmi_timings *timings; + bool reduced_fps; + const struct v4l2_dv_timings dv_timings; + const struct hdmi_timings *hdmi_timings; } hdmi_timings[] = { - { V4L2_DV_480P59_94, &hdmi_timings_480p }, - { V4L2_DV_576P50, &hdmi_timings_576p50 }, - { V4L2_DV_720P50, &hdmi_timings_720p50 }, - { V4L2_DV_720P59_94, &hdmi_timings_720p60 }, - { V4L2_DV_720P60, &hdmi_timings_720p60 }, - { V4L2_DV_1080P24, &hdmi_timings_1080p24 }, - { V4L2_DV_1080P30, &hdmi_timings_1080p60 }, - { V4L2_DV_1080P50, &hdmi_timings_1080p50 }, - { V4L2_DV_1080I50, &hdmi_timings_1080i50 }, - { V4L2_DV_1080I60, &hdmi_timings_1080i60 }, - { V4L2_DV_1080P60, &hdmi_timings_1080p60 }, + { false, V4L2_DV_BT_CEA_720X480P59_94, &hdmi_timings_480p }, + { false, V4L2_DV_BT_CEA_720X576P50, &hdmi_timings_576p50 }, + { false, V4L2_DV_BT_CEA_1280X720P50, &hdmi_timings_720p50 }, + { true, V4L2_DV_BT_CEA_1280X720P60, &hdmi_timings_720p60 }, + { false, V4L2_DV_BT_CEA_1920X1080P24, &hdmi_timings_1080p24 }, + { false, V4L2_DV_BT_CEA_1920X1080P30, &hdmi_timings_1080p60 }, + { false, V4L2_DV_BT_CEA_1920X1080P50, &hdmi_timings_1080p50 }, + { false, V4L2_DV_BT_CEA_1920X1080I50, &hdmi_timings_1080i50 }, + { false, V4L2_DV_BT_CEA_1920X1080I60, &hdmi_timings_1080i60 }, + { false, V4L2_DV_BT_CEA_1920X1080P60, &hdmi_timings_1080p60 }, }; -static const struct hdmi_timings *hdmi_preset2timings(u32 preset) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(hdmi_timings); ++i) - if (hdmi_timings[i].preset == preset) - return hdmi_timings[i].timings; - return NULL; -} - static int hdmi_streamon(struct hdmi_device *hdev) { struct device *dev = hdev->dev; @@ -586,16 +577,22 @@ static int hdmi_s_stream(struct v4l2_subdev *sd, int enable) return hdmi_streamoff(hdev); } -static void hdmi_resource_poweron(struct hdmi_resources *res) +static int hdmi_resource_poweron(struct hdmi_resources *res) { + int ret; + /* turn HDMI power on */ - regulator_bulk_enable(res->regul_count, res->regul_bulk); + ret = regulator_bulk_enable(res->regul_count, res->regul_bulk); + if (ret < 0) + return ret; /* power-on hdmi physical interface */ clk_enable(res->hdmiphy); /* use VPP as parent clock; HDMIPHY is not working yet */ clk_set_parent(res->sclk_hdmi, res->sclk_pixel); /* turn clocks on */ clk_enable(res->sclk_hdmi); + + return 0; } static void hdmi_resource_poweroff(struct hdmi_resources *res) @@ -621,29 +618,33 @@ static int hdmi_s_power(struct v4l2_subdev *sd, int on) return IS_ERR_VALUE(ret) ? ret : 0; } -static int hdmi_s_dv_preset(struct v4l2_subdev *sd, - struct v4l2_dv_preset *preset) +static int hdmi_s_dv_timings(struct v4l2_subdev *sd, + struct v4l2_dv_timings *timings) { struct hdmi_device *hdev = sd_to_hdmi_dev(sd); struct device *dev = hdev->dev; - const struct hdmi_timings *conf; + int i; - conf = hdmi_preset2timings(preset->preset); - if (conf == NULL) { - dev_err(dev, "preset (%u) not supported\n", preset->preset); + for (i = 0; i < ARRAY_SIZE(hdmi_timings); i++) + if (v4l2_match_dv_timings(&hdmi_timings[i].dv_timings, + timings, 0)) + break; + if (i == ARRAY_SIZE(hdmi_timings)) { + dev_err(dev, "timings not supported\n"); return -EINVAL; } - hdev->cur_conf = conf; + hdev->cur_conf = hdmi_timings[i].hdmi_timings; hdev->cur_conf_dirty = 1; - hdev->cur_preset = preset->preset; + hdev->cur_timings = *timings; + if (!hdmi_timings[i].reduced_fps) + hdev->cur_timings.bt.flags &= ~V4L2_DV_FL_CAN_REDUCE_FPS; return 0; } -static int hdmi_g_dv_preset(struct v4l2_subdev *sd, - struct v4l2_dv_preset *preset) +static int hdmi_g_dv_timings(struct v4l2_subdev *sd, + struct v4l2_dv_timings *timings) { - memset(preset, 0, sizeof(*preset)); - preset->preset = sd_to_hdmi_dev(sd)->cur_preset; + *timings = sd_to_hdmi_dev(sd)->cur_timings; return 0; } @@ -656,7 +657,7 @@ static int hdmi_g_mbus_fmt(struct v4l2_subdev *sd, dev_dbg(hdev->dev, "%s\n", __func__); if (!hdev->cur_conf) return -EINVAL; - memset(fmt, 0, sizeof *fmt); + memset(fmt, 0, sizeof(*fmt)); fmt->width = t->hact.end - t->hact.beg; fmt->height = t->vact[0].end - t->vact[0].beg; fmt->code = V4L2_MBUS_FMT_FIXED; /* means RGB888 */ @@ -670,13 +671,38 @@ static int hdmi_g_mbus_fmt(struct v4l2_subdev *sd, return 0; } -static int hdmi_enum_dv_presets(struct v4l2_subdev *sd, - struct v4l2_dv_enum_preset *preset) +static int hdmi_enum_dv_timings(struct v4l2_subdev *sd, + struct v4l2_enum_dv_timings *timings) { - if (preset->index >= ARRAY_SIZE(hdmi_timings)) + if (timings->pad != 0) + return -EINVAL; + if (timings->index >= ARRAY_SIZE(hdmi_timings)) return -EINVAL; - return v4l_fill_dv_preset_info(hdmi_timings[preset->index].preset, - preset); + timings->timings = hdmi_timings[timings->index].dv_timings; + if (!hdmi_timings[timings->index].reduced_fps) + timings->timings.bt.flags &= ~V4L2_DV_FL_CAN_REDUCE_FPS; + return 0; +} + +static int hdmi_dv_timings_cap(struct v4l2_subdev *sd, + struct v4l2_dv_timings_cap *cap) +{ + struct hdmi_device *hdev = sd_to_hdmi_dev(sd); + + if (cap->pad != 0) + return -EINVAL; + + /* Let the phy fill in the pixelclock range */ + v4l2_subdev_call(hdev->phy_sd, pad, dv_timings_cap, cap); + cap->type = V4L2_DV_BT_656_1120; + cap->bt.min_width = 720; + cap->bt.max_width = 1920; + cap->bt.min_height = 480; + cap->bt.max_height = 1080; + cap->bt.standards = V4L2_DV_BT_STD_CEA861; + cap->bt.capabilities = V4L2_DV_BT_CAP_INTERLACED | + V4L2_DV_BT_CAP_PROGRESSIVE; + return 0; } static const struct v4l2_subdev_core_ops hdmi_sd_core_ops = { @@ -684,13 +710,17 @@ static const struct v4l2_subdev_core_ops hdmi_sd_core_ops = { }; static const struct v4l2_subdev_video_ops hdmi_sd_video_ops = { - .s_dv_preset = hdmi_s_dv_preset, - .g_dv_preset = hdmi_g_dv_preset, - .enum_dv_presets = hdmi_enum_dv_presets, + .s_dv_timings = hdmi_s_dv_timings, + .g_dv_timings = hdmi_g_dv_timings, .g_mbus_fmt = hdmi_g_mbus_fmt, .s_stream = hdmi_s_stream, }; +static const struct v4l2_subdev_pad_ops hdmi_sd_pad_ops = { + .enum_dv_timings = hdmi_enum_dv_timings, + .dv_timings_cap = hdmi_dv_timings_cap, +}; + static const struct v4l2_subdev_ops hdmi_sd_ops = { .core = &hdmi_sd_core_ops, .video = &hdmi_sd_video_ops, @@ -713,11 +743,13 @@ static int hdmi_runtime_resume(struct device *dev) { struct v4l2_subdev *sd = dev_get_drvdata(dev); struct hdmi_device *hdev = sd_to_hdmi_dev(sd); - int ret = 0; + int ret; dev_dbg(dev, "%s\n", __func__); - hdmi_resource_poweron(&hdev->res); + ret = hdmi_resource_poweron(&hdev->res); + if (ret < 0) + return ret; /* starting MHL */ ret = v4l2_subdev_call(hdev->mhl_sd, core, s_power, 1); @@ -740,6 +772,15 @@ static const struct dev_pm_ops hdmi_pm_ops = { .runtime_resume = hdmi_runtime_resume, }; +static void hdmi_resource_clear_clocks(struct hdmi_resources *res) +{ + res->hdmi = ERR_PTR(-EINVAL); + res->sclk_hdmi = ERR_PTR(-EINVAL); + res->sclk_pixel = ERR_PTR(-EINVAL); + res->sclk_hdmiphy = ERR_PTR(-EINVAL); + res->hdmiphy = ERR_PTR(-EINVAL); +} + static void hdmi_resources_cleanup(struct hdmi_device *hdev) { struct hdmi_resources *res = &hdev->res; @@ -750,17 +791,18 @@ static void hdmi_resources_cleanup(struct hdmi_device *hdev) regulator_bulk_free(res->regul_count, res->regul_bulk); /* kfree is NULL-safe */ kfree(res->regul_bulk); - if (!IS_ERR_OR_NULL(res->hdmiphy)) + if (!IS_ERR(res->hdmiphy)) clk_put(res->hdmiphy); - if (!IS_ERR_OR_NULL(res->sclk_hdmiphy)) + if (!IS_ERR(res->sclk_hdmiphy)) clk_put(res->sclk_hdmiphy); - if (!IS_ERR_OR_NULL(res->sclk_pixel)) + if (!IS_ERR(res->sclk_pixel)) clk_put(res->sclk_pixel); - if (!IS_ERR_OR_NULL(res->sclk_hdmi)) + if (!IS_ERR(res->sclk_hdmi)) clk_put(res->sclk_hdmi); - if (!IS_ERR_OR_NULL(res->hdmi)) + if (!IS_ERR(res->hdmi)) clk_put(res->hdmi); - memset(res, 0, sizeof *res); + memset(res, 0, sizeof(*res)); + hdmi_resource_clear_clocks(res); } static int hdmi_resources_init(struct hdmi_device *hdev) @@ -777,31 +819,32 @@ static int hdmi_resources_init(struct hdmi_device *hdev) dev_dbg(dev, "HDMI resource init\n"); - memset(res, 0, sizeof *res); - /* get clocks, power */ + memset(res, 0, sizeof(*res)); + hdmi_resource_clear_clocks(res); + /* get clocks, power */ res->hdmi = clk_get(dev, "hdmi"); - if (IS_ERR_OR_NULL(res->hdmi)) { + if (IS_ERR(res->hdmi)) { dev_err(dev, "failed to get clock 'hdmi'\n"); goto fail; } res->sclk_hdmi = clk_get(dev, "sclk_hdmi"); - if (IS_ERR_OR_NULL(res->sclk_hdmi)) { + if (IS_ERR(res->sclk_hdmi)) { dev_err(dev, "failed to get clock 'sclk_hdmi'\n"); goto fail; } res->sclk_pixel = clk_get(dev, "sclk_pixel"); - if (IS_ERR_OR_NULL(res->sclk_pixel)) { + if (IS_ERR(res->sclk_pixel)) { dev_err(dev, "failed to get clock 'sclk_pixel'\n"); goto fail; } res->sclk_hdmiphy = clk_get(dev, "sclk_hdmiphy"); - if (IS_ERR_OR_NULL(res->sclk_hdmiphy)) { + if (IS_ERR(res->sclk_hdmiphy)) { dev_err(dev, "failed to get clock 'sclk_hdmiphy'\n"); goto fail; } res->hdmiphy = clk_get(dev, "hdmiphy"); - if (IS_ERR_OR_NULL(res->hdmiphy)) { + if (IS_ERR(res->hdmiphy)) { dev_err(dev, "failed to get clock 'hdmiphy'\n"); goto fail; } @@ -830,7 +873,7 @@ fail: return -ENODEV; } -static int __devinit hdmi_probe(struct platform_device *pdev) +static int hdmi_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct resource *res; @@ -955,10 +998,12 @@ static int __devinit hdmi_probe(struct platform_device *pdev) v4l2_subdev_init(sd, &hdmi_sd_ops); sd->owner = THIS_MODULE; - strlcpy(sd->name, "s5p-hdmi", sizeof sd->name); - hdmi_dev->cur_preset = HDMI_DEFAULT_PRESET; - /* FIXME: missing fail preset is not supported */ - hdmi_dev->cur_conf = hdmi_preset2timings(hdmi_dev->cur_preset); + strlcpy(sd->name, "s5p-hdmi", sizeof(sd->name)); + hdmi_dev->cur_timings = + hdmi_timings[HDMI_DEFAULT_TIMINGS_IDX].dv_timings; + /* FIXME: missing fail timings is not supported */ + hdmi_dev->cur_conf = + hdmi_timings[HDMI_DEFAULT_TIMINGS_IDX].hdmi_timings; hdmi_dev->cur_conf_dirty = 1; /* storing subdev for call that have only access to struct device */ @@ -979,7 +1024,7 @@ fail: return ret; } -static int __devexit hdmi_remove(struct platform_device *pdev) +static int hdmi_remove(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct v4l2_subdev *sd = dev_get_drvdata(dev); @@ -997,7 +1042,7 @@ static int __devexit hdmi_remove(struct platform_device *pdev) static struct platform_driver hdmi_driver __refdata = { .probe = hdmi_probe, - .remove = __devexit_p(hdmi_remove), + .remove = hdmi_remove, .id_table = hdmi_driver_types, .driver = { .name = "s5p-hdmi", |
