linux/drivers/media/platform/s5p-tv/hdmiphy_drv.c
<<
>>
Prefs
   1/*
   2 * Samsung HDMI Physical interface driver
   3 *
   4 * Copyright (C) 2010-2011 Samsung Electronics Co.Ltd
   5 * Author: Tomasz Stanislawski <t.stanislaws@samsung.com>
   6 *
   7 * This program is free software; you can redistribute  it and/or modify it
   8 * under  the terms of  the GNU General  Public License as published by the
   9 * Free Software Foundation;  either version 2 of the  License, or (at your
  10 * option) any later version.
  11 */
  12
  13#include <linux/module.h>
  14#include <linux/i2c.h>
  15#include <linux/slab.h>
  16#include <linux/clk.h>
  17#include <linux/io.h>
  18#include <linux/interrupt.h>
  19#include <linux/irq.h>
  20#include <linux/err.h>
  21
  22#include <media/v4l2-subdev.h>
  23
  24MODULE_AUTHOR("Tomasz Stanislawski <t.stanislaws@samsung.com>");
  25MODULE_DESCRIPTION("Samsung HDMI Physical interface driver");
  26MODULE_LICENSE("GPL");
  27
  28struct hdmiphy_conf {
  29        unsigned long pixclk;
  30        const u8 *data;
  31};
  32
  33struct hdmiphy_ctx {
  34        struct v4l2_subdev sd;
  35        const struct hdmiphy_conf *conf_tab;
  36};
  37
  38static const struct hdmiphy_conf hdmiphy_conf_s5pv210[] = {
  39        { .pixclk = 27000000, .data = (u8 [32]) {
  40                0x01, 0x05, 0x00, 0xD8, 0x10, 0x1C, 0x30, 0x40,
  41                0x6B, 0x10, 0x02, 0x52, 0xDF, 0xF2, 0x54, 0x87,
  42                0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0,
  43                0x22, 0x40, 0xE3, 0x26, 0x00, 0x00, 0x00, 0x00, }
  44        },
  45        { .pixclk = 27027000, .data = (u8 [32]) {
  46                0x01, 0x05, 0x00, 0xD4, 0x10, 0x9C, 0x09, 0x64,
  47                0x6B, 0x10, 0x02, 0x52, 0xDF, 0xF2, 0x54, 0x87,
  48                0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0,
  49                0x22, 0x40, 0xE2, 0x26, 0x00, 0x00, 0x00, 0x00, }
  50        },
  51        { .pixclk = 74176000, .data = (u8 [32]) {
  52                0x01, 0x05, 0x00, 0xD8, 0x10, 0x9C, 0xEF, 0x5B,
  53                0x6D, 0x10, 0x01, 0x52, 0xEF, 0xF3, 0x54, 0xB9,
  54                0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0,
  55                0x22, 0x40, 0xA5, 0x26, 0x01, 0x00, 0x00, 0x00, }
  56        },
  57        { .pixclk = 74250000, .data = (u8 [32]) {
  58                0x01, 0x05, 0x00, 0xD8, 0x10, 0x9C, 0xF8, 0x40,
  59                0x6A, 0x10, 0x01, 0x52, 0xFF, 0xF1, 0x54, 0xBA,
  60                0x84, 0x00, 0x10, 0x38, 0x00, 0x08, 0x10, 0xE0,
  61                0x22, 0x40, 0xA4, 0x26, 0x01, 0x00, 0x00, 0x00, }
  62        },
  63        { /* end marker */ }
  64};
  65
  66static const struct hdmiphy_conf hdmiphy_conf_exynos4210[] = {
  67        { .pixclk = 27000000, .data = (u8 [32]) {
  68                0x01, 0x05, 0x00, 0xD8, 0x10, 0x1C, 0x30, 0x40,
  69                0x6B, 0x10, 0x02, 0x51, 0xDF, 0xF2, 0x54, 0x87,
  70                0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0,
  71                0x22, 0x40, 0xE3, 0x26, 0x00, 0x00, 0x00, 0x00, }
  72        },
  73        { .pixclk = 27027000, .data = (u8 [32]) {
  74                0x01, 0x05, 0x00, 0xD4, 0x10, 0x9C, 0x09, 0x64,
  75                0x6B, 0x10, 0x02, 0x51, 0xDF, 0xF2, 0x54, 0x87,
  76                0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0,
  77                0x22, 0x40, 0xE2, 0x26, 0x00, 0x00, 0x00, 0x00, }
  78        },
  79        { .pixclk = 74176000, .data = (u8 [32]) {
  80                0x01, 0x05, 0x00, 0xD8, 0x10, 0x9C, 0xEF, 0x5B,
  81                0x6D, 0x10, 0x01, 0x51, 0xEF, 0xF3, 0x54, 0xB9,
  82                0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0,
  83                0x22, 0x40, 0xA5, 0x26, 0x01, 0x00, 0x00, 0x00, }
  84        },
  85        { .pixclk = 74250000, .data = (u8 [32]) {
  86                0x01, 0x05, 0x00, 0xD8, 0x10, 0x9C, 0xF8, 0x40,
  87                0x6A, 0x10, 0x01, 0x51, 0xFF, 0xF1, 0x54, 0xBA,
  88                0x84, 0x00, 0x10, 0x38, 0x00, 0x08, 0x10, 0xE0,
  89                0x22, 0x40, 0xA4, 0x26, 0x01, 0x00, 0x00, 0x00, }
  90        },
  91        { .pixclk = 148352000, .data = (u8 [32]) {
  92                0x01, 0x05, 0x00, 0xD8, 0x10, 0x9C, 0xEF, 0x5B,
  93                0x6D, 0x18, 0x00, 0x51, 0xEF, 0xF3, 0x54, 0xB9,
  94                0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0,
  95                0x11, 0x40, 0xA5, 0x26, 0x02, 0x00, 0x00, 0x00, }
  96        },
  97        { .pixclk = 148500000, .data = (u8 [32]) {
  98                0x01, 0x05, 0x00, 0xD8, 0x10, 0x9C, 0xF8, 0x40,
  99                0x6A, 0x18, 0x00, 0x51, 0xFF, 0xF1, 0x54, 0xBA,
 100                0x84, 0x00, 0x10, 0x38, 0x00, 0x08, 0x10, 0xE0,
 101                0x11, 0x40, 0xA4, 0x26, 0x02, 0x00, 0x00, 0x00, }
 102        },
 103        { /* end marker */ }
 104};
 105
 106static const struct hdmiphy_conf hdmiphy_conf_exynos4212[] = {
 107        { .pixclk = 27000000, .data = (u8 [32]) {
 108                0x01, 0x11, 0x2D, 0x75, 0x00, 0x01, 0x00, 0x08,
 109                0x82, 0x00, 0x0E, 0xD9, 0x45, 0xA0, 0x34, 0xC0,
 110                0x0B, 0x80, 0x12, 0x87, 0x08, 0x24, 0x24, 0x71,
 111                0x54, 0xE3, 0x24, 0x00, 0x00, 0x00, 0x01, 0x00, }
 112        },
 113        { .pixclk = 27027000, .data = (u8 [32]) {
 114                0x01, 0x91, 0x2D, 0x72, 0x00, 0x64, 0x12, 0x08,
 115                0x43, 0x20, 0x0E, 0xD9, 0x45, 0xA0, 0x34, 0xC0,
 116                0x0B, 0x80, 0x12, 0x87, 0x08, 0x24, 0x24, 0x71,
 117                0x54, 0xE2, 0x24, 0x00, 0x00, 0x00, 0x01, 0x00, }
 118        },
 119        { .pixclk = 74176000, .data = (u8 [32]) {
 120                0x01, 0x91, 0x3E, 0x35, 0x00, 0x5B, 0xDE, 0x08,
 121                0x82, 0x20, 0x73, 0xD9, 0x45, 0xA0, 0x34, 0xC0,
 122                0x0B, 0x80, 0x12, 0x87, 0x08, 0x24, 0x24, 0x52,
 123                0x54, 0xA5, 0x24, 0x01, 0x00, 0x00, 0x01, 0x00, }
 124        },
 125        { .pixclk = 74250000, .data = (u8 [32]) {
 126                0x01, 0x91, 0x3E, 0x35, 0x00, 0x40, 0xF0, 0x08,
 127                0x82, 0x20, 0x73, 0xD9, 0x45, 0xA0, 0x34, 0xC0,
 128                0x0B, 0x80, 0x12, 0x87, 0x08, 0x24, 0x24, 0x52,
 129                0x54, 0xA4, 0x24, 0x01, 0x00, 0x00, 0x01, 0x00, }
 130        },
 131        { .pixclk = 148500000, .data = (u8 [32]) {
 132                0x01, 0x91, 0x3E, 0x15, 0x00, 0x40, 0xF0, 0x08,
 133                0x82, 0x20, 0x73, 0xD9, 0x45, 0xA0, 0x34, 0xC0,
 134                0x0B, 0x80, 0x12, 0x87, 0x08, 0x24, 0x24, 0xA4,
 135                0x54, 0x4A, 0x25, 0x03, 0x00, 0x00, 0x01, 0x00, }
 136        },
 137        { /* end marker */ }
 138};
 139
 140static const struct hdmiphy_conf hdmiphy_conf_exynos4412[] = {
 141        { .pixclk = 27000000, .data = (u8 [32]) {
 142                0x01, 0x11, 0x2D, 0x75, 0x40, 0x01, 0x00, 0x08,
 143                0x82, 0x00, 0x0E, 0xD9, 0x45, 0xA0, 0xAC, 0x80,
 144                0x08, 0x80, 0x11, 0x84, 0x02, 0x22, 0x44, 0x86,
 145                0x54, 0xE4, 0x24, 0x00, 0x00, 0x00, 0x01, 0x00, }
 146        },
 147        { .pixclk = 27027000, .data = (u8 [32]) {
 148                0x01, 0x91, 0x2D, 0x72, 0x40, 0x64, 0x12, 0x08,
 149                0x43, 0x20, 0x0E, 0xD9, 0x45, 0xA0, 0xAC, 0x80,
 150                0x08, 0x80, 0x11, 0x84, 0x02, 0x22, 0x44, 0x86,
 151                0x54, 0xE3, 0x24, 0x00, 0x00, 0x00, 0x01, 0x00, }
 152        },
 153        { .pixclk = 74176000, .data = (u8 [32]) {
 154                0x01, 0x91, 0x1F, 0x10, 0x40, 0x5B, 0xEF, 0x08,
 155                0x81, 0x20, 0xB9, 0xD8, 0x45, 0xA0, 0xAC, 0x80,
 156                0x08, 0x80, 0x11, 0x84, 0x02, 0x22, 0x44, 0x86,
 157                0x54, 0xA6, 0x24, 0x01, 0x00, 0x00, 0x01, 0x00, }
 158        },
 159        { .pixclk = 74250000, .data = (u8 [32]) {
 160                0x01, 0x91, 0x1F, 0x10, 0x40, 0x40, 0xF8, 0x08,
 161                0x81, 0x20, 0xBA, 0xD8, 0x45, 0xA0, 0xAC, 0x80,
 162                0x08, 0x80, 0x11, 0x84, 0x02, 0x22, 0x44, 0x86,
 163                0x54, 0xA5, 0x24, 0x01, 0x00, 0x00, 0x01, 0x00, }
 164        },
 165        { .pixclk = 148500000, .data = (u8 [32]) {
 166                0x01, 0x91, 0x1F, 0x00, 0x40, 0x40, 0xF8, 0x08,
 167                0x81, 0x20, 0xBA, 0xD8, 0x45, 0xA0, 0xAC, 0x80,
 168                0x08, 0x80, 0x11, 0x84, 0x02, 0x22, 0x44, 0x86,
 169                0x54, 0x4B, 0x25, 0x03, 0x00, 0x00, 0x01, 0x00, }
 170        },
 171        { /* end marker */ }
 172};
 173
 174static inline struct hdmiphy_ctx *sd_to_ctx(struct v4l2_subdev *sd)
 175{
 176        return container_of(sd, struct hdmiphy_ctx, sd);
 177}
 178
 179static const u8 *hdmiphy_find_conf(unsigned long pixclk,
 180                const struct hdmiphy_conf *conf)
 181{
 182        for (; conf->pixclk; ++conf)
 183                if (conf->pixclk == pixclk)
 184                        return conf->data;
 185        return NULL;
 186}
 187
 188static int hdmiphy_s_power(struct v4l2_subdev *sd, int on)
 189{
 190        /* to be implemented */
 191        return 0;
 192}
 193
 194static int hdmiphy_s_dv_timings(struct v4l2_subdev *sd,
 195        struct v4l2_dv_timings *timings)
 196{
 197        const u8 *data;
 198        u8 buffer[32];
 199        int ret;
 200        struct hdmiphy_ctx *ctx = sd_to_ctx(sd);
 201        struct i2c_client *client = v4l2_get_subdevdata(sd);
 202        struct device *dev = &client->dev;
 203        unsigned long pixclk = timings->bt.pixelclock;
 204
 205        dev_info(dev, "s_dv_timings\n");
 206        if ((timings->bt.flags & V4L2_DV_FL_REDUCED_FPS) && pixclk == 74250000)
 207                pixclk = 74176000;
 208        data = hdmiphy_find_conf(pixclk, ctx->conf_tab);
 209        if (!data) {
 210                dev_err(dev, "format not supported\n");
 211                return -EINVAL;
 212        }
 213
 214        /* storing configuration to the device */
 215        memcpy(buffer, data, 32);
 216        ret = i2c_master_send(client, buffer, 32);
 217        if (ret != 32) {
 218                dev_err(dev, "failed to configure HDMIPHY via I2C\n");
 219                return -EIO;
 220        }
 221
 222        return 0;
 223}
 224
 225static int hdmiphy_dv_timings_cap(struct v4l2_subdev *sd,
 226        struct v4l2_dv_timings_cap *cap)
 227{
 228        cap->type = V4L2_DV_BT_656_1120;
 229        /* The phy only determines the pixelclock, leave the other values
 230         * at 0 to signify that we have no information for them. */
 231        cap->bt.min_pixelclock = 27000000;
 232        cap->bt.max_pixelclock = 148500000;
 233        return 0;
 234}
 235
 236static int hdmiphy_s_stream(struct v4l2_subdev *sd, int enable)
 237{
 238        struct i2c_client *client = v4l2_get_subdevdata(sd);
 239        struct device *dev = &client->dev;
 240        u8 buffer[2];
 241        int ret;
 242
 243        dev_info(dev, "s_stream(%d)\n", enable);
 244        /* going to/from configuration from/to operation mode */
 245        buffer[0] = 0x1f;
 246        buffer[1] = enable ? 0x80 : 0x00;
 247
 248        ret = i2c_master_send(client, buffer, 2);
 249        if (ret != 2) {
 250                dev_err(dev, "stream (%d) failed\n", enable);
 251                return -EIO;
 252        }
 253        return 0;
 254}
 255
 256static const struct v4l2_subdev_core_ops hdmiphy_core_ops = {
 257        .s_power =  hdmiphy_s_power,
 258};
 259
 260static const struct v4l2_subdev_video_ops hdmiphy_video_ops = {
 261        .s_dv_timings = hdmiphy_s_dv_timings,
 262        .dv_timings_cap = hdmiphy_dv_timings_cap,
 263        .s_stream =  hdmiphy_s_stream,
 264};
 265
 266static const struct v4l2_subdev_ops hdmiphy_ops = {
 267        .core = &hdmiphy_core_ops,
 268        .video = &hdmiphy_video_ops,
 269};
 270
 271static int hdmiphy_probe(struct i2c_client *client,
 272                         const struct i2c_device_id *id)
 273{
 274        struct hdmiphy_ctx *ctx;
 275
 276        ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
 277        if (!ctx)
 278                return -ENOMEM;
 279
 280        ctx->conf_tab = (struct hdmiphy_conf *)id->driver_data;
 281        v4l2_i2c_subdev_init(&ctx->sd, client, &hdmiphy_ops);
 282
 283        dev_info(&client->dev, "probe successful\n");
 284        return 0;
 285}
 286
 287static int hdmiphy_remove(struct i2c_client *client)
 288{
 289        struct v4l2_subdev *sd = i2c_get_clientdata(client);
 290        struct hdmiphy_ctx *ctx = sd_to_ctx(sd);
 291
 292        kfree(ctx);
 293        dev_info(&client->dev, "remove successful\n");
 294
 295        return 0;
 296}
 297
 298static const struct i2c_device_id hdmiphy_id[] = {
 299        { "hdmiphy", (unsigned long)hdmiphy_conf_exynos4210 },
 300        { "hdmiphy-s5pv210", (unsigned long)hdmiphy_conf_s5pv210 },
 301        { "hdmiphy-exynos4210", (unsigned long)hdmiphy_conf_exynos4210 },
 302        { "hdmiphy-exynos4212", (unsigned long)hdmiphy_conf_exynos4212 },
 303        { "hdmiphy-exynos4412", (unsigned long)hdmiphy_conf_exynos4412 },
 304        { },
 305};
 306MODULE_DEVICE_TABLE(i2c, hdmiphy_id);
 307
 308static struct i2c_driver hdmiphy_driver = {
 309        .driver = {
 310                .name   = "s5p-hdmiphy",
 311                .owner  = THIS_MODULE,
 312        },
 313        .probe          = hdmiphy_probe,
 314        .remove         = hdmiphy_remove,
 315        .id_table = hdmiphy_id,
 316};
 317
 318module_i2c_driver(hdmiphy_driver);
 319