/*
* Samsung HDMI interface driver
*
* Copyright (c) 2010-2011 Samsung Electronics Co., Ltd.
*
* Tomasz Stanislawski, <t.stanislaws@samsung.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published
* by the Free Software Foundiation. either version 2 of the License,
* or (at your option) any later version
*/
#define pr_fmt(fmt) "s5p-tv (hdmi_drv): " fmt
#ifdef CONFIG_VIDEO_SAMSUNG_S5P_HDMI_DEBUG
#define DEBUG
#endif
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/io.h>
#include <linux/i2c.h>
#include <linux/platform_device.h>
#include <media/v4l2-subdev.h>
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/delay.h>
#include <linux/bug.h>
#include <linux/pm_runtime.h>
#include <linux/clk.h>
#include <linux/regulator/consumer.h>
#include <media/s5p_hdmi.h>
#include <media/v4l2-common.h>
#include <media/v4l2-dev.h>
#include <media/v4l2-device.h>
#include "regs-hdmi.h"
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;
};
struct hdmi_timings {
struct hdmi_pulse hact;
u32 hsyn_pol; /* 0 - high, 1 - low */
struct hdmi_pulse hsyn;
u32 interlaced;
struct hdmi_pulse vact[2];
u32 vsyn_pol; /* 0 - high, 1 - low */
u32 vsyn_off;
struct hdmi_pulse vsyn[2];
};
struct hdmi_resources {
struct clk *hdmi;
struct clk *sclk_hdmi;
struct clk *sclk_pixel;
struct clk *sclk_hdmiphy;
struct clk *hdmiphy;
struct regulator_bulk_data *regul_bulk;
int regul_count;
};
struct hdmi_device {
/** base address of HDMI registers */
void __iomem *regs;
/** HDMI interrupt */
unsigned int irq;
/** pointer to device parent */
struct device *dev;
/** subdev generated by HDMI device */
struct v4l2_subdev sd;
/** V4L2 device structure */
struct v4l2_device v4l2_dev;
/** subdev of HDMIPHY interface */
struct v4l2_subdev *phy_sd;
/** subdev of MHL interface */
struct v4l2_subdev *mhl_sd;
/** configuration of current graphic mode */
const struct hdmi_timings *cur_conf;
/** flag indicating that timings are dirty */
int cur_conf_dirty;
/** current preset */
u32 cur_preset;
/** other resources */
struct hdmi_resources res;
};
static struct platform_device_id hdmi_driver_types[] = {
{
.name = "s5pv210-hdmi",
}, {
.name = "exynos4-hdmi",
}, {
/* end node */
}
};
static const struct v4l2_subdev_ops hdmi_sd_ops;
static struct hdmi_device *sd_to_hdmi_dev(struct v4l2_subdev *sd)
{
return container_of(sd, struct hdmi_device, sd);
}
static inline
void hdmi_write(struct hdmi_device *hdev, u32 reg_id, u32 value)
{
writel(value, hdev->regs + reg_id);
}
static inline
void hdmi_write_mask(struct hdmi_device *hdev, u32 reg_id, u32 value, u32 mask)
{
u32 old = readl(hdev->regs + reg_id);
value = (value & mask) | (old & ~mask);
writel(value, hdev->regs + reg_id);
}
static inline
void hdmi_writeb(struct hdmi_device *hdev, u32 reg_id, u8 value)
{
writeb(value, hdev->regs + reg_id);
}
static inline
void hdmi_writebn(struct hdmi_device *hdev, u32 reg_id, int n, u32 value)
{
switch (n) {
default:
writeb(value >> 24, hdev->regs + reg_id + 12);
case 3:
writeb(value >> 16, hdev->regs + reg_id + 8);
case 2:
writeb(value >> 8, hdev->regs + reg_id + 4);
case 1:
writeb(value >> 0, hdev->regs + reg_id + 0);
}
}
static inline u32 hdmi_read(struct hdmi_device *hdev, u32 reg_id)
{
return readl(hdev->regs + reg_id);
}
static irqreturn_t hdmi_irq_handler(int irq, void *dev_data)
{
struct hdmi_device *hdev = dev_data;
u32