Commit 1fae6dfe authored by Dave Airlie's avatar Dave Airlie

Merge branch 'exynos-drm-next' of...

Merge branch 'exynos-drm-next' of git://git.kernel.org/pub/scm/linux/kernel/git/daeinki/drm-exynos into drm-next

   This pull request includes i80 interface support, module auto-loading
   ipp consolidation, and trivail fixups and cleanups.

Summary:
- Add i80 interface support. For this, we added some features to
  Exynos drm framework, which don't affect any other SoC and common
  framework because they are specific to Exynos drm.
- Add module auto-loading support. For this, sub drivers of Exynos drm
  exports their of match tables to userspace. This allows modules to be
  loaded automatically based on devicetree information
- Consolidate ipp driver. This patch just just includes cleanups and
  a littl bit refactoring codes.

If there is any problem, please kindly let me know.

* 'exynos-drm-next' of git://git.kernel.org/pub/scm/linux/kernel/git/daeinki/drm-exynos: (38 commits)
  drm/exynos: g2d: let exynos_g2d_get_ver_ioctl fail
  drm/exynos: g2d: make ioctls more robust
  drm/exynos: hdmi: add null check for hdmiphy_port
  drm/exynos: control blending of mixer graphic layer 0
  drm/exynos: Add MODULE_DEVICE_TABLE entries for various components
  Subject: Revert "drm/exynos: remove MODULE_DEVICE_TABLE definitions"
  Subject: Revert "drm/exynos: fix module build error"
  drm/exynos/ipp: simplify ipp_find_driver
  drm/exynos/ipp: simplify ipp_create_id
  drm/exynos/ipp: remove redundant messages
  drm/exynos/ipp: simplify ipp_find_obj
  drm/exynos/ipp: remove useless registration checks
  drm/exynos/ipp: simplify memory check function
  drm/exynos/ipp: remove incorrect checks of list_first_entry result
  drm/exynos/ipp: remove temporary variable
  drm/exynos/ipp: correct address type
  drm/exynos/ipp: remove struct exynos_drm_ipp_private
  drm/exynos/ipp: remove unused field from exynos_drm_ipp_private
  drm/exynos/ipp: remove type casting
  drm/exynos: g2d: add exynos4212 as a compatible device.
  ...
parents 08d645c1 ef7ce055
Exynos MIPI DSI Master Exynos MIPI DSI Master
Required properties: Required properties:
- compatible: "samsung,exynos4210-mipi-dsi" - compatible: value should be one of the following
"samsung,exynos4210-mipi-dsi" /* for Exynos4 SoCs */
"samsung,exynos5410-mipi-dsi" /* for Exynos5410/5420/5440 SoCs */
- reg: physical base address and length of the registers set for the device - reg: physical base address and length of the registers set for the device
- interrupts: should contain DSI interrupt - interrupts: should contain DSI interrupt
- clocks: list of clock specifiers, must contain an entry for each required - clocks: list of clock specifiers, must contain an entry for each required
......
...@@ -4,8 +4,9 @@ Required properties: ...@@ -4,8 +4,9 @@ Required properties:
- compatible: value should be one of the following: - compatible: value should be one of the following:
1) "samsung,exynos5-mixer" <DEPRECATED> 1) "samsung,exynos5-mixer" <DEPRECATED>
2) "samsung,exynos4210-mixer" 2) "samsung,exynos4210-mixer"
3) "samsung,exynos5250-mixer" 3) "samsung,exynos4212-mixer"
4) "samsung,exynos5420-mixer" 4) "samsung,exynos5250-mixer"
5) "samsung,exynos5420-mixer"
- reg: physical base address of the mixer and length of memory mapped - reg: physical base address of the mixer and length of memory mapped
region. region.
......
...@@ -44,6 +44,34 @@ Optional Properties: ...@@ -44,6 +44,34 @@ Optional Properties:
- display-timings: timing settings for FIMD, as described in document [1]. - display-timings: timing settings for FIMD, as described in document [1].
Can be used in case timings cannot be provided otherwise Can be used in case timings cannot be provided otherwise
or to override timings provided by the panel. or to override timings provided by the panel.
- samsung,sysreg: handle to syscon used to control the system registers
- i80-if-timings: timing configuration for lcd i80 interface support.
- cs-setup: clock cycles for the active period of address signal is enabled
until chip select is enabled.
If not specified, the default value(0) will be used.
- wr-setup: clock cycles for the active period of CS signal is enabled until
write signal is enabled.
If not specified, the default value(0) will be used.
- wr-active: clock cycles for the active period of CS is enabled.
If not specified, the default value(1) will be used.
- wr-hold: clock cycles for the active period of CS is disabled until write
signal is disabled.
If not specified, the default value(0) will be used.
The parameters are defined as:
VCLK(internal) __|??????|_____|??????|_____|??????|_____|??????|_____|??
: : : : :
Address Output --:<XXXXXXXXXXX:XXXXXXXXXXXX:XXXXXXXXXXXX:XXXXXXXXXXXX:XX
| cs-setup+1 | : : :
|<---------->| : : :
Chip Select ???????????????|____________:____________:____________|??
| wr-setup+1 | | wr-hold+1 |
|<---------->| |<---------->|
Write Enable ????????????????????????????|____________|???????????????
| wr-active+1|
|<---------->|
Video Data ----------------------------<XXXXXXXXXXXXXXXXXXXXXXXXX>--
The device node can contain 'port' child nodes according to the bindings defined The device node can contain 'port' child nodes according to the bindings defined
in [2]. The following are properties specific to those nodes: in [2]. The following are properties specific to those nodes:
......
...@@ -608,6 +608,7 @@ fimd: fimd@11c00000 { ...@@ -608,6 +608,7 @@ fimd: fimd@11c00000 {
clocks = <&clock CLK_SCLK_FIMD0>, <&clock CLK_FIMD0>; clocks = <&clock CLK_SCLK_FIMD0>, <&clock CLK_FIMD0>;
clock-names = "sclk_fimd", "fimd"; clock-names = "sclk_fimd", "fimd";
samsung,power-domain = <&pd_lcd0>; samsung,power-domain = <&pd_lcd0>;
samsung,sysreg = <&sys_reg>;
status = "disabled"; status = "disabled";
}; };
}; };
...@@ -87,6 +87,7 @@ fimd@14400000 { ...@@ -87,6 +87,7 @@ fimd@14400000 {
reg = <0x14400000 0x40000>; reg = <0x14400000 0x40000>;
interrupt-names = "fifo", "vsync", "lcd_sys"; interrupt-names = "fifo", "vsync", "lcd_sys";
interrupts = <18 4>, <18 5>, <18 6>; interrupts = <18 4>, <18 5>, <18 6>;
samsung,sysreg = <&sysreg_system_controller>;
status = "disabled"; status = "disabled";
}; };
......
...@@ -517,6 +517,26 @@ dp: dp-controller@145B0000 { ...@@ -517,6 +517,26 @@ dp: dp-controller@145B0000 {
phy-names = "dp"; phy-names = "dp";
}; };
mipi_phy: video-phy@10040714 {
compatible = "samsung,s5pv210-mipi-video-phy";
reg = <0x10040714 12>;
#phy-cells = <1>;
};
dsi@14500000 {
compatible = "samsung,exynos5410-mipi-dsi";
reg = <0x14500000 0x10000>;
interrupts = <0 82 0>;
samsung,power-domain = <&disp_pd>;
phys = <&mipi_phy 1>;
phy-names = "dsim";
clocks = <&clock CLK_DSIM1>, <&clock CLK_SCLK_MIPI1>;
clock-names = "bus_clk", "pll_clk";
#address-cells = <1>;
#size-cells = <0>;
status = "disabled";
};
fimd: fimd@14400000 { fimd: fimd@14400000 {
samsung,power-domain = <&disp_pd>; samsung,power-domain = <&disp_pd>;
clocks = <&clock CLK_SCLK_FIMD1>, <&clock CLK_FIMD1>; clocks = <&clock CLK_SCLK_FIMD1>, <&clock CLK_FIMD1>;
......
...@@ -28,6 +28,7 @@ config DRM_EXYNOS_FIMD ...@@ -28,6 +28,7 @@ config DRM_EXYNOS_FIMD
bool "Exynos DRM FIMD" bool "Exynos DRM FIMD"
depends on DRM_EXYNOS && !FB_S3C depends on DRM_EXYNOS && !FB_S3C
select FB_MODE_HELPERS select FB_MODE_HELPERS
select MFD_SYSCON
help help
Choose this option if you want to use Exynos FIMD for DRM. Choose this option if you want to use Exynos FIMD for DRM.
......
...@@ -1376,6 +1376,7 @@ static const struct of_device_id exynos_dp_match[] = { ...@@ -1376,6 +1376,7 @@ static const struct of_device_id exynos_dp_match[] = {
{ .compatible = "samsung,exynos5-dp" }, { .compatible = "samsung,exynos5-dp" },
{}, {},
}; };
MODULE_DEVICE_TABLE(of, exynos_dp_match);
struct platform_driver dp_driver = { struct platform_driver dp_driver = {
.probe = exynos_dp_probe, .probe = exynos_dp_probe,
...@@ -1390,4 +1391,4 @@ struct platform_driver dp_driver = { ...@@ -1390,4 +1391,4 @@ struct platform_driver dp_driver = {
MODULE_AUTHOR("Jingoo Han <jg1.han@samsung.com>"); MODULE_AUTHOR("Jingoo Han <jg1.han@samsung.com>");
MODULE_DESCRIPTION("Samsung SoC DP Driver"); MODULE_DESCRIPTION("Samsung SoC DP Driver");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL v2");
...@@ -69,8 +69,10 @@ static void exynos_drm_crtc_dpms(struct drm_crtc *crtc, int mode) ...@@ -69,8 +69,10 @@ static void exynos_drm_crtc_dpms(struct drm_crtc *crtc, int mode)
if (mode > DRM_MODE_DPMS_ON) { if (mode > DRM_MODE_DPMS_ON) {
/* wait for the completion of page flip. */ /* wait for the completion of page flip. */
wait_event(exynos_crtc->pending_flip_queue, if (!wait_event_timeout(exynos_crtc->pending_flip_queue,
atomic_read(&exynos_crtc->pending_flip) == 0); !atomic_read(&exynos_crtc->pending_flip),
HZ/20))
atomic_set(&exynos_crtc->pending_flip, 0);
drm_vblank_off(crtc->dev, exynos_crtc->pipe); drm_vblank_off(crtc->dev, exynos_crtc->pipe);
} }
...@@ -259,6 +261,7 @@ static int exynos_drm_crtc_page_flip(struct drm_crtc *crtc, ...@@ -259,6 +261,7 @@ static int exynos_drm_crtc_page_flip(struct drm_crtc *crtc,
spin_lock_irq(&dev->event_lock); spin_lock_irq(&dev->event_lock);
drm_vblank_put(dev, exynos_crtc->pipe); drm_vblank_put(dev, exynos_crtc->pipe);
list_del(&event->base.link); list_del(&event->base.link);
atomic_set(&exynos_crtc->pending_flip, 0);
spin_unlock_irq(&dev->event_lock); spin_unlock_irq(&dev->event_lock);
goto out; goto out;
...@@ -508,3 +511,11 @@ int exynos_drm_crtc_get_pipe_from_type(struct drm_device *drm_dev, ...@@ -508,3 +511,11 @@ int exynos_drm_crtc_get_pipe_from_type(struct drm_device *drm_dev,
return -EPERM; return -EPERM;
} }
void exynos_drm_crtc_te_handler(struct drm_crtc *crtc)
{
struct exynos_drm_manager *manager = to_exynos_crtc(crtc)->manager;
if (manager->ops->te_handler)
manager->ops->te_handler(manager);
}
...@@ -36,4 +36,11 @@ void exynos_drm_crtc_plane_disable(struct drm_crtc *crtc, int zpos); ...@@ -36,4 +36,11 @@ void exynos_drm_crtc_plane_disable(struct drm_crtc *crtc, int zpos);
int exynos_drm_crtc_get_pipe_from_type(struct drm_device *drm_dev, int exynos_drm_crtc_get_pipe_from_type(struct drm_device *drm_dev,
unsigned int out_type); unsigned int out_type);
/*
* This function calls the crtc device(manager)'s te_handler() callback
* to trigger to transfer video image at the tearing effect synchronization
* signal.
*/
void exynos_drm_crtc_te_handler(struct drm_crtc *crtc);
#endif #endif
...@@ -358,7 +358,7 @@ static int exynos_drm_sys_suspend(struct device *dev) ...@@ -358,7 +358,7 @@ static int exynos_drm_sys_suspend(struct device *dev)
struct drm_device *drm_dev = dev_get_drvdata(dev); struct drm_device *drm_dev = dev_get_drvdata(dev);
pm_message_t message; pm_message_t message;
if (pm_runtime_suspended(dev)) if (pm_runtime_suspended(dev) || !drm_dev)
return 0; return 0;
message.event = PM_EVENT_SUSPEND; message.event = PM_EVENT_SUSPEND;
...@@ -369,7 +369,7 @@ static int exynos_drm_sys_resume(struct device *dev) ...@@ -369,7 +369,7 @@ static int exynos_drm_sys_resume(struct device *dev)
{ {
struct drm_device *drm_dev = dev_get_drvdata(dev); struct drm_device *drm_dev = dev_get_drvdata(dev);
if (pm_runtime_suspended(dev)) if (pm_runtime_suspended(dev) || !drm_dev)
return 0; return 0;
return exynos_drm_resume(drm_dev); return exynos_drm_resume(drm_dev);
......
...@@ -186,6 +186,8 @@ struct exynos_drm_display { ...@@ -186,6 +186,8 @@ struct exynos_drm_display {
* @win_commit: apply hardware specific overlay data to registers. * @win_commit: apply hardware specific overlay data to registers.
* @win_enable: enable hardware specific overlay. * @win_enable: enable hardware specific overlay.
* @win_disable: disable hardware specific overlay. * @win_disable: disable hardware specific overlay.
* @te_handler: trigger to transfer video image at the tearing effect
* synchronization signal if there is a page flip request.
*/ */
struct exynos_drm_manager; struct exynos_drm_manager;
struct exynos_drm_manager_ops { struct exynos_drm_manager_ops {
...@@ -204,6 +206,7 @@ struct exynos_drm_manager_ops { ...@@ -204,6 +206,7 @@ struct exynos_drm_manager_ops {
void (*win_commit)(struct exynos_drm_manager *mgr, int zpos); void (*win_commit)(struct exynos_drm_manager *mgr, int zpos);
void (*win_enable)(struct exynos_drm_manager *mgr, int zpos); void (*win_enable)(struct exynos_drm_manager *mgr, int zpos);
void (*win_disable)(struct exynos_drm_manager *mgr, int zpos); void (*win_disable)(struct exynos_drm_manager *mgr, int zpos);
void (*te_handler)(struct exynos_drm_manager *mgr);
}; };
/* /*
...@@ -234,14 +237,9 @@ struct exynos_drm_g2d_private { ...@@ -234,14 +237,9 @@ struct exynos_drm_g2d_private {
struct list_head userptr_list; struct list_head userptr_list;
}; };
struct exynos_drm_ipp_private {
struct device *dev;
struct list_head event_list;
};
struct drm_exynos_file_private { struct drm_exynos_file_private {
struct exynos_drm_g2d_private *g2d_priv; struct exynos_drm_g2d_private *g2d_priv;
struct exynos_drm_ipp_private *ipp_priv; struct device *ipp_dev;
struct file *anon_filp; struct file *anon_filp;
}; };
......
...@@ -16,7 +16,10 @@ ...@@ -16,7 +16,10 @@
#include <drm/drm_panel.h> #include <drm/drm_panel.h>
#include <linux/clk.h> #include <linux/clk.h>
#include <linux/gpio/consumer.h>
#include <linux/irq.h> #include <linux/irq.h>
#include <linux/of_device.h>
#include <linux/of_gpio.h>
#include <linux/phy/phy.h> #include <linux/phy/phy.h>
#include <linux/regulator/consumer.h> #include <linux/regulator/consumer.h>
#include <linux/component.h> #include <linux/component.h>
...@@ -24,6 +27,7 @@ ...@@ -24,6 +27,7 @@
#include <video/mipi_display.h> #include <video/mipi_display.h>
#include <video/videomode.h> #include <video/videomode.h>
#include "exynos_drm_crtc.h"
#include "exynos_drm_drv.h" #include "exynos_drm_drv.h"
/* returns true iff both arguments logically differs */ /* returns true iff both arguments logically differs */
...@@ -54,9 +58,12 @@ ...@@ -54,9 +58,12 @@
/* FIFO memory AC characteristic register */ /* FIFO memory AC characteristic register */
#define DSIM_PLLCTRL_REG 0x4c /* PLL control register */ #define DSIM_PLLCTRL_REG 0x4c /* PLL control register */
#define DSIM_PLLTMR_REG 0x50 /* PLL timer register */
#define DSIM_PHYACCHR_REG 0x54 /* D-PHY AC characteristic register */ #define DSIM_PHYACCHR_REG 0x54 /* D-PHY AC characteristic register */
#define DSIM_PHYACCHR1_REG 0x58 /* D-PHY AC characteristic register1 */ #define DSIM_PHYACCHR1_REG 0x58 /* D-PHY AC characteristic register1 */
#define DSIM_PHYCTRL_REG 0x5c
#define DSIM_PHYTIMING_REG 0x64
#define DSIM_PHYTIMING1_REG 0x68
#define DSIM_PHYTIMING2_REG 0x6c
/* DSIM_STATUS */ /* DSIM_STATUS */
#define DSIM_STOP_STATE_DAT(x) (((x) & 0xf) << 0) #define DSIM_STOP_STATE_DAT(x) (((x) & 0xf) << 0)
...@@ -200,6 +207,24 @@ ...@@ -200,6 +207,24 @@
#define DSIM_PLL_M(x) ((x) << 4) #define DSIM_PLL_M(x) ((x) << 4)
#define DSIM_PLL_S(x) ((x) << 1) #define DSIM_PLL_S(x) ((x) << 1)
/* DSIM_PHYCTRL */
#define DSIM_PHYCTRL_ULPS_EXIT(x) (((x) & 0x1ff) << 0)
/* DSIM_PHYTIMING */
#define DSIM_PHYTIMING_LPX(x) ((x) << 8)
#define DSIM_PHYTIMING_HS_EXIT(x) ((x) << 0)
/* DSIM_PHYTIMING1 */
#define DSIM_PHYTIMING1_CLK_PREPARE(x) ((x) << 24)
#define DSIM_PHYTIMING1_CLK_ZERO(x) ((x) << 16)
#define DSIM_PHYTIMING1_CLK_POST(x) ((x) << 8)
#define DSIM_PHYTIMING1_CLK_TRAIL(x) ((x) << 0)
/* DSIM_PHYTIMING2 */
#define DSIM_PHYTIMING2_HS_PREPARE(x) ((x) << 16)
#define DSIM_PHYTIMING2_HS_ZERO(x) ((x) << 8)
#define DSIM_PHYTIMING2_HS_TRAIL(x) ((x) << 0)
#define DSI_MAX_BUS_WIDTH 4 #define DSI_MAX_BUS_WIDTH 4
#define DSI_NUM_VIRTUAL_CHANNELS 4 #define DSI_NUM_VIRTUAL_CHANNELS 4
#define DSI_TX_FIFO_SIZE 2048 #define DSI_TX_FIFO_SIZE 2048
...@@ -233,6 +258,12 @@ struct exynos_dsi_transfer { ...@@ -233,6 +258,12 @@ struct exynos_dsi_transfer {
#define DSIM_STATE_INITIALIZED BIT(1) #define DSIM_STATE_INITIALIZED BIT(1)
#define DSIM_STATE_CMD_LPM BIT(2) #define DSIM_STATE_CMD_LPM BIT(2)
struct exynos_dsi_driver_data {
unsigned int plltmr_reg;
unsigned int has_freqband:1;
};
struct exynos_dsi { struct exynos_dsi {
struct mipi_dsi_host dsi_host; struct mipi_dsi_host dsi_host;
struct drm_connector connector; struct drm_connector connector;
...@@ -247,6 +278,7 @@ struct exynos_dsi { ...@@ -247,6 +278,7 @@ struct exynos_dsi {
struct clk *bus_clk; struct clk *bus_clk;
struct regulator_bulk_data supplies[2]; struct regulator_bulk_data supplies[2];
int irq; int irq;
int te_gpio;
u32 pll_clk_rate; u32 pll_clk_rate;
u32 burst_clk_rate; u32 burst_clk_rate;
...@@ -262,11 +294,39 @@ struct exynos_dsi { ...@@ -262,11 +294,39 @@ struct exynos_dsi {
spinlock_t transfer_lock; /* protects transfer_list */ spinlock_t transfer_lock; /* protects transfer_list */
struct list_head transfer_list; struct list_head transfer_list;
struct exynos_dsi_driver_data *driver_data;
}; };
#define host_to_dsi(host) container_of(host, struct exynos_dsi, dsi_host) #define host_to_dsi(host) container_of(host, struct exynos_dsi, dsi_host)
#define connector_to_dsi(c) container_of(c, struct exynos_dsi, connector) #define connector_to_dsi(c) container_of(c, struct exynos_dsi, connector)
static struct exynos_dsi_driver_data exynos4_dsi_driver_data = {
.plltmr_reg = 0x50,
.has_freqband = 1,
};
static struct exynos_dsi_driver_data exynos5_dsi_driver_data = {
.plltmr_reg = 0x58,
};
static struct of_device_id exynos_dsi_of_match[] = {
{ .compatible = "samsung,exynos4210-mipi-dsi",
.data = &exynos4_dsi_driver_data },
{ .compatible = "samsung,exynos5410-mipi-dsi",
.data = &exynos5_dsi_driver_data },
{ }
};
static inline struct exynos_dsi_driver_data *exynos_dsi_get_driver_data(
struct platform_device *pdev)
{
const struct of_device_id *of_id =
of_match_device(exynos_dsi_of_match, &pdev->dev);
return (struct exynos_dsi_driver_data *)of_id->data;
}
static void exynos_dsi_wait_for_reset(struct exynos_dsi *dsi) static void exynos_dsi_wait_for_reset(struct exynos_dsi *dsi)
{ {
if (wait_for_completion_timeout(&dsi->completed, msecs_to_jiffies(300))) if (wait_for_completion_timeout(&dsi->completed, msecs_to_jiffies(300)))
...@@ -340,14 +400,9 @@ static unsigned long exynos_dsi_pll_find_pms(struct exynos_dsi *dsi, ...@@ -340,14 +400,9 @@ static unsigned long exynos_dsi_pll_find_pms(struct exynos_dsi *dsi,
static unsigned long exynos_dsi_set_pll(struct exynos_dsi *dsi, static unsigned long exynos_dsi_set_pll(struct exynos_dsi *dsi,
unsigned long freq) unsigned long freq)
{ {
static const unsigned long freq_bands[] = { struct exynos_dsi_driver_data *driver_data = dsi->driver_data;
100 * MHZ, 120 * MHZ, 160 * MHZ, 200 * MHZ,
270 * MHZ, 320 * MHZ, 390 * MHZ, 450 * MHZ,
510 * MHZ, 560 * MHZ, 640 * MHZ, 690 * MHZ,
770 * MHZ, 870 * MHZ, 950 * MHZ,
};
unsigned long fin, fout; unsigned long fin, fout;
int timeout, band; int timeout;
u8 p, s; u8 p, s;
u16 m; u16 m;
u32 reg; u32 reg;
...@@ -368,18 +423,30 @@ static unsigned long exynos_dsi_set_pll(struct exynos_dsi *dsi, ...@@ -368,18 +423,30 @@ static unsigned long exynos_dsi_set_pll(struct exynos_dsi *dsi,
"failed to find PLL PMS for requested frequency\n"); "failed to find PLL PMS for requested frequency\n");
return -EFAULT; return -EFAULT;
} }
dev_dbg(dsi->dev, "PLL freq %lu, (p %d, m %d, s %d)\n", fout, p, m, s);
for (band = 0; band < ARRAY_SIZE(freq_bands); ++band) writel(500, dsi->reg_base + driver_data->plltmr_reg);
if (fout < freq_bands[band])
break; reg = DSIM_PLL_EN | DSIM_PLL_P(p) | DSIM_PLL_M(m) | DSIM_PLL_S(s);
if (driver_data->has_freqband) {
static const unsigned long freq_bands[] = {
100 * MHZ, 120 * MHZ, 160 * MHZ, 200 * MHZ,
270 * MHZ, 320 * MHZ, 390 * MHZ, 450 * MHZ,
510 * MHZ, 560 * MHZ, 640 * MHZ, 690 * MHZ,
770 * MHZ, 870 * MHZ, 950 * MHZ,
};
int band;
dev_dbg(dsi->dev, "PLL freq %lu, (p %d, m %d, s %d), band %d\n", fout, for (band = 0; band < ARRAY_SIZE(freq_bands); ++band)
p, m, s, band); if (fout < freq_bands[band])
break;
writel(500, dsi->reg_base + DSIM_PLLTMR_REG); dev_dbg(dsi->dev, "band %d\n", band);
reg |= DSIM_FREQ_BAND(band);
}
reg = DSIM_FREQ_BAND(band) | DSIM_PLL_EN
| DSIM_PLL_P(p) | DSIM_PLL_M(m) | DSIM_PLL_S(s);
writel(reg, dsi->reg_base + DSIM_PLLCTRL_REG); writel(reg, dsi->reg_base + DSIM_PLLCTRL_REG);
timeout = 1000; timeout = 1000;
...@@ -433,6 +500,59 @@ static int exynos_dsi_enable_clock(struct exynos_dsi *dsi) ...@@ -433,6 +500,59 @@ static int exynos_dsi_enable_clock(struct exynos_dsi *dsi)
return 0; return 0;
} }
static void exynos_dsi_set_phy_ctrl(struct exynos_dsi *dsi)
{
struct exynos_dsi_driver_data *driver_data = dsi->driver_data;
u32 reg;
if (driver_data->has_freqband)
return;
/* B D-PHY: D-PHY Master & Slave Analog Block control */
reg = DSIM_PHYCTRL_ULPS_EXIT(0x0af);
writel(reg, dsi->reg_base + DSIM_PHYCTRL_REG);
/*
* T LPX: Transmitted length of any Low-Power state period
* T HS-EXIT: Time that the transmitter drives LP-11 following a HS
* burst
*/
reg = DSIM_PHYTIMING_LPX(0x06) | DSIM_PHYTIMING_HS_EXIT(0x0b);
writel(reg, dsi->reg_base + DSIM_PHYTIMING_REG);
/*
* T CLK-PREPARE: Time that the transmitter drives the Clock Lane LP-00
* Line state immediately before the HS-0 Line state starting the
* HS transmission
* T CLK-ZERO: Time that the transmitter drives the HS-0 state prior to
* transmitting the Clock.
* T CLK_POST: Time that the transmitter continues to send HS clock
* after the last associated Data Lane has transitioned to LP Mode
* Interval is defined as the period from the end of T HS-TRAIL to
* the beginning of T CLK-TRAIL
* T CLK-TRAIL: Time that the transmitter drives the HS-0 state after
* the last payload clock bit of a HS transmission burst
*/
reg = DSIM_PHYTIMING1_CLK_PREPARE(0x07) |
DSIM_PHYTIMING1_CLK_ZERO(0x27) |
DSIM_PHYTIMING1_CLK_POST(0x0d) |
DSIM_PHYTIMING1_CLK_TRAIL(0x08);
writel(reg, dsi->reg_base + DSIM_PHYTIMING1_REG);
/*
* T HS-PREPARE: Time that the transmitter drives the Data Lane LP-00
* Line state immediately before the HS-0 Line state starting the
* HS transmission
* T HS-ZERO: Time that the transmitter drives the HS-0 state prior to
* transmitting the Sync sequence.
* T HS-TRAIL: Time that the transmitter drives the flipped differential
* state after last payload data bit of a HS transmission burst
*/
reg = DSIM_PHYTIMING2_HS_PREPARE(0x09) | DSIM_PHYTIMING2_HS_ZERO(0x0d) |
DSIM_PHYTIMING2_HS_TRAIL(0x0b);
writel(reg, dsi->reg_base + DSIM_PHYTIMING2_REG);
}
static void exynos_dsi_disable_clock(struct exynos_dsi *dsi) static void exynos_dsi_disable_clock(struct exynos_dsi *dsi)
{ {
u32 reg; u32 reg;
...@@ -468,13 +588,20 @@ static int exynos_dsi_init_link(struct exynos_dsi *dsi) ...@@ -468,13 +588,20 @@ static int exynos_dsi_init_link(struct exynos_dsi *dsi)
/* DSI configuration */ /* DSI configuration */
reg = 0; reg = 0;
/*
* The first bit of mode_flags specifies display configuration.
* If this bit is set[= MIPI_DSI_MODE_VIDEO], dsi will support video
* mode, otherwise it will support command mode.
*/
if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO) { if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO) {
reg |= DSIM_VIDEO_MODE; reg |= DSIM_VIDEO_MODE;
/*
* The user manual describes that following bits are ignored in
* command mode.
*/
if (!(dsi->mode_flags & MIPI_DSI_MODE_VSYNC_FLUSH)) if (!(dsi->mode_flags & MIPI_DSI_MODE_VSYNC_FLUSH))
reg |= DSIM_MFLUSH_VS; reg |= DSIM_MFLUSH_VS;
if (!(dsi->mode_flags & MIPI_DSI_MODE_EOT_PACKET))
reg |= DSIM_EOT_DISABLE;
if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE) if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE)
reg |= DSIM_SYNC_INFORM; reg |= DSIM_SYNC_INFORM;
if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO_BURST) if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO_BURST)
...@@ -491,6 +618,9 @@ static int exynos_dsi_init_link(struct exynos_dsi *dsi) ...@@ -491,6 +618,9 @@ static int exynos_dsi_init_link(struct exynos_dsi *dsi)
reg |= DSIM_HSA_MODE; reg |= DSIM_HSA_MODE;
} }
if (!(dsi->mode_flags & MIPI_DSI_MODE_EOT_PACKET))
reg |= DSIM_EOT_DISABLE;
switch (dsi->format) { switch (dsi->format) {
case MIPI_DSI_FMT_RGB888: case MIPI_DSI_FMT_RGB888:
reg |= DSIM_MAIN_PIX_FORMAT_RGB888; reg |= DSIM_MAIN_PIX_FORMAT_RGB888;
...@@ -944,17 +1074,90 @@ static irqreturn_t exynos_dsi_irq(int irq, void *dev_id) ...@@ -944,17 +1074,90 @@ static irqreturn_t exynos_dsi_irq(int irq, void *dev_id)
return IRQ_HANDLED; return IRQ_HANDLED;
} }
static irqreturn_t exynos_dsi_te_irq_handler(int irq, void *dev_id)
{
struct exynos_dsi *dsi = (struct exynos_dsi *)dev_id;
struct drm_encoder *encoder = dsi->encoder;
if (dsi->state & DSIM_STATE_ENABLED)
exynos_drm_crtc_te_handler(encoder->crtc);
return IRQ_HANDLED;
}
static void exynos_dsi_enable_irq(struct exynos_dsi *dsi)
{
enable_irq(dsi->irq);
if (gpio_is_valid(dsi->te_gpio))
enable_irq(gpio_to_irq(dsi->te_gpio));
}
static void exynos_dsi_disable_irq(struct exynos_dsi *dsi)
{
if (gpio_is_valid(dsi->te_gpio))
disable_irq(gpio_to_irq(dsi->te_gpio));
disable_irq(dsi->irq);
}
static int exynos_dsi_init(struct exynos_dsi *dsi) static int exynos_dsi_init(struct exynos_dsi *dsi)
{ {
exynos_dsi_enable_clock(dsi);
exynos_dsi_reset(dsi); exynos_dsi_reset(dsi);
enable_irq(dsi->irq); exynos_dsi_enable_irq(dsi);
exynos_dsi_enable_clock(dsi);
exynos_dsi_wait_for_reset(dsi); exynos_dsi_wait_for_reset(dsi);
exynos_dsi_set_phy_ctrl(dsi);
exynos_dsi_init_link(dsi); exynos_dsi_init_link(dsi);
return 0; return 0;
} }
static int exynos_dsi_register_te_irq(struct exynos_dsi *dsi)
{
int ret;
dsi->te_gpio = of_get_named_gpio(dsi->panel_node, "te-gpios", 0);
if (!gpio_is_valid(dsi->te_gpio)) {
dev_err(dsi->dev, "no te-gpios specified\n");
ret = dsi->te_gpio;
goto out;
}
ret = gpio_request_one(dsi->te_gpio, GPIOF_IN, "te_gpio");
if (ret) {
dev_err(dsi->dev, "gpio request failed with %d\n", ret);
goto out;
}
/*
* This TE GPIO IRQ should not be set to IRQ_NOAUTOEN, because panel
* calls drm_panel_init() first then calls mipi_dsi_attach() in probe().
* It means that te_gpio is invalid when exynos_dsi_enable_irq() is
* called by drm_panel_init() before panel is attached.
*/
ret = request_threaded_irq(gpio_to_irq(dsi->te_gpio),
exynos_dsi_te_irq_handler, NULL,
IRQF_TRIGGER_RISING, "TE", dsi);
if (ret) {
dev_err(dsi->dev, "request interrupt failed with %d\n", ret);
gpio_free(dsi->te_gpio);
goto out;
}
out:
return ret;
}
static void exynos_dsi_unregister_te_irq(struct exynos_dsi *dsi)
{
if (gpio_is_valid(dsi->te_gpio)) {
free_irq(gpio_to_irq(dsi->te_gpio), dsi);
gpio_free(dsi->te_gpio);
dsi->te_gpio = -ENOENT;
}
}
static int exynos_dsi_host_attach(struct mipi_dsi_host *host, static int exynos_dsi_host_attach(struct mipi_dsi_host *host,
struct mipi_dsi_device *device) struct mipi_dsi_device *device)
{ {
...@@ -968,6 +1171,19 @@ static int exynos_dsi_host_attach(struct mipi_dsi_host *host, ...@@ -968,6 +1171,19 @@ static int exynos_dsi_host_attach(struct mipi_dsi_host *host,
if (dsi->connector.dev) if (dsi->connector.dev)
drm_helper_hpd_irq_event(dsi->connector.dev); drm_helper_hpd_irq_event(dsi->connector.dev);
/*
* This is a temporary solution and should be made by more generic way.
*
* If attached panel device is for command mode one, dsi should register
* TE interrupt handler.
*/
if (!(dsi->mode_flags & MIPI_DSI_MODE_VIDEO)) {
int ret = exynos_dsi_register_te_irq(dsi);
if (ret)
return ret;
}
return 0; return 0;
} }
...@@ -976,6 +1192,8 @@ static int exynos_dsi_host_detach(struct mipi_dsi_host *host, ...@@ -976,6 +1192,8 @@ static int exynos_dsi_host_detach(struct mipi_dsi_host *host,
{ {
struct exynos_dsi *dsi = host_to_dsi(host); struct exynos_dsi *dsi = host_to_dsi(host);
exynos_dsi_unregister_te_irq(dsi);
dsi->panel_node = NULL; dsi->panel_node = NULL;
if (dsi->connector.dev) if (dsi->connector.dev)
...@@ -1089,7 +1307,7 @@ static void exynos_dsi_poweroff(struct exynos_dsi *dsi) ...@@ -1089,7 +1307,7 @@ static void exynos_dsi_poweroff(struct exynos_dsi *dsi)
exynos_dsi_disable_clock(dsi); exynos_dsi_disable_clock(dsi);
disable_irq(dsi->irq); exynos_dsi_disable_irq(dsi);
} }
dsi->state &= ~DSIM_STATE_CMD_LPM; dsi->state &= ~DSIM_STATE_CMD_LPM;
...@@ -1278,6 +1496,7 @@ static struct exynos_drm_display exynos_dsi_display = { ...@@ -1278,6 +1496,7 @@ static struct exynos_drm_display exynos_dsi_display = {
.type = EXYNOS_DISPLAY_TYPE_LCD, .type = EXYNOS_DISPLAY_TYPE_LCD,
.ops = &exynos_dsi_display_ops, .ops = &exynos_dsi_display_ops,
}; };
MODULE_DEVICE_TABLE(of, exynos_dsi_of_match);
/* of_* functions will be removed after merge of of_graph patches */ /* of_* functions will be removed after merge of of_graph patches */
static struct device_node * static struct device_node *
...@@ -1435,6 +1654,9 @@ static int exynos_dsi_probe(struct platform_device *pdev) ...@@ -1435,6 +1654,9 @@ static int exynos_dsi_probe(struct platform_device *pdev)
goto err_del_component; goto err_del_component;
} }
/* To be checked as invalid one */
dsi->te_gpio = -ENOENT;
init_completion(&dsi->completed); init_completion(&dsi->completed);
spin_lock_init(&dsi->transfer_lock); spin_lock_init(&dsi->transfer_lock);
INIT_LIST_HEAD(&dsi->transfer_list); INIT_LIST_HEAD(&dsi->transfer_list);
...@@ -1443,6 +1665,7 @@ static int exynos_dsi_probe(struct platform_device *pdev) ...@@ -1443,6 +1665,7 @@ static int exynos_dsi_probe(struct platform_device *pdev)
dsi->dsi_host.dev = &pdev->dev; dsi->dsi_host.dev = &pdev->dev;
dsi->dev = &pdev->dev; dsi->dev = &pdev->dev;
dsi->driver_data = exynos_dsi_get_driver_data(pdev);
ret = exynos_dsi_parse_dt(dsi); ret = exynos_dsi_parse_dt(dsi);
if (ret) if (ret)
...@@ -1525,11 +1748,6 @@ static int exynos_dsi_remove(struct platform_device *pdev) ...@@ -1525,11 +1748,6 @@ static int exynos_dsi_remove(struct platform_device *pdev)
return 0; return 0;
} }
static struct of_device_id exynos_dsi_of_match[] = {
{ .compatible = "samsung,exynos4210-mipi-dsi" },
{ }
};
struct platform_driver dsi_driver = { struct platform_driver dsi_driver = {
.probe = exynos_dsi_probe, .probe = exynos_dsi_probe,
.remove = exynos_dsi_remove, .remove = exynos_dsi_remove,
......
...@@ -1887,6 +1887,7 @@ static const struct of_device_id fimc_of_match[] = { ...@@ -1887,6 +1887,7 @@ static const struct of_device_id fimc_of_match[] = {
{ .compatible = "samsung,exynos4212-fimc" }, { .compatible = "samsung,exynos4212-fimc" },
{ }, { },
}; };
MODULE_DEVICE_TABLE(of, fimc_of_match);
struct platform_driver fimc_driver = { struct platform_driver fimc_driver = {
.probe = fimc_probe, .probe = fimc_probe,
......
...@@ -20,6 +20,8 @@ ...@@ -20,6 +20,8 @@
#include <linux/of_device.h> #include <linux/of_device.h>
#include <linux/pm_runtime.h> #include <linux/pm_runtime.h>
#include <linux/component.h> #include <linux/component.h>
#include <linux/mfd/syscon.h>
#include <linux/regmap.h>
#include <video/of_display_timing.h> #include <video/of_display_timing.h>
#include <video/of_videomode.h> #include <video/of_videomode.h>
...@@ -61,6 +63,24 @@ ...@@ -61,6 +63,24 @@
/* color key value register for hardware window 1 ~ 4. */ /* color key value register for hardware window 1 ~ 4. */
#define WKEYCON1_BASE(x) ((WKEYCON1 + 0x140) + ((x - 1) * 8)) #define WKEYCON1_BASE(x) ((WKEYCON1 + 0x140) + ((x - 1) * 8))
/* I80 / RGB trigger control register */
#define TRIGCON 0x1A4
#define TRGMODE_I80_RGB_ENABLE_I80 (1 << 0)
#define SWTRGCMD_I80_RGB_ENABLE (1 << 1)
/* display mode change control register except exynos4 */
#define VIDOUT_CON 0x000
#define VIDOUT_CON_F_I80_LDI0 (0x2 << 8)
/* I80 interface control for main LDI register */
#define I80IFCONFAx(x) (0x1B0 + (x) * 4)
#define I80IFCONFBx(x) (0x1B8 + (x) * 4)
#define LCD_CS_SETUP(x) ((x) << 16)
#define LCD_WR_SETUP(x) ((x) << 12)
#define LCD_WR_ACTIVE(x) ((x) << 8)
#define LCD_WR_HOLD(x) ((x) << 4)
#define I80IFEN_ENABLE (1 << 0)
/* FIMD has totally five hardware windows. */ /* FIMD has totally five hardware windows. */
#define WINDOWS_NR 5 #define WINDOWS_NR 5
...@@ -68,10 +88,14 @@ ...@@ -68,10 +88,14 @@
struct fimd_driver_data { struct fimd_driver_data {
unsigned int timing_base; unsigned int timing_base;
unsigned int lcdblk_offset;
unsigned int lcdblk_vt_shift;
unsigned int lcdblk_bypass_shift;
unsigned int has_shadowcon:1; unsigned int has_shadowcon:1;
unsigned int has_clksel:1; unsigned int has_clksel:1;
unsigned int has_limited_fmt:1; unsigned int has_limited_fmt:1;
unsigned int has_vidoutcon:1;
}; };
static struct fimd_driver_data s3c64xx_fimd_driver_data = { static struct fimd_driver_data s3c64xx_fimd_driver_data = {
...@@ -82,12 +106,19 @@ static struct fimd_driver_data s3c64xx_fimd_driver_data = { ...@@ -82,12 +106,19 @@ static struct fimd_driver_data s3c64xx_fimd_driver_data = {
static struct fimd_driver_data exynos4_fimd_driver_data = { static struct fimd_driver_data exynos4_fimd_driver_data = {
.timing_base = 0x0, .timing_base = 0x0,
.lcdblk_offset = 0x210,
.lcdblk_vt_shift = 10,
.lcdblk_bypass_shift = 1,
.has_shadowcon = 1, .has_shadowcon = 1,
}; };
static struct fimd_driver_data exynos5_fimd_driver_data = { static struct fimd_driver_data exynos5_fimd_driver_data = {
.timing_base = 0x20000, .timing_base = 0x20000,
.lcdblk_offset = 0x214,
.lcdblk_vt_shift = 24,
.lcdblk_bypass_shift = 15,
.has_shadowcon = 1, .has_shadowcon = 1,
.has_vidoutcon = 1,
}; };
struct fimd_win_data { struct fimd_win_data {
...@@ -112,15 +143,22 @@ struct fimd_context { ...@@ -112,15 +143,22 @@ struct fimd_context {
struct clk *bus_clk; struct clk *bus_clk;
struct clk *lcd_clk; struct clk *lcd_clk;
void __iomem *regs; void __iomem *regs;
struct regmap *sysreg;
struct drm_display_mode mode; struct drm_display_mode mode;
struct fimd_win_data win_data[WINDOWS_NR]; struct fimd_win_data win_data[WINDOWS_NR];
unsigned int default_win; unsigned int default_win;
unsigned long irq_flags; unsigned long irq_flags;
u32 vidcon0;
u32 vidcon1; u32 vidcon1;
u32 vidout_con;
u32 i80ifcon;
bool i80_if;
bool suspended; bool suspended;
int pipe; int pipe;
wait_queue_head_t wait_vsync_queue; wait_queue_head_t wait_vsync_queue;
atomic_t wait_vsync_event; atomic_t wait_vsync_event;
atomic_t win_updated;
atomic_t triggering;
struct exynos_drm_panel_info panel; struct exynos_drm_panel_info panel;
struct fimd_driver_data *driver_data; struct fimd_driver_data *driver_data;
...@@ -136,6 +174,7 @@ static const struct of_device_id fimd_driver_dt_match[] = { ...@@ -136,6 +174,7 @@ static const struct of_device_id fimd_driver_dt_match[] = {
.data = &exynos5_fimd_driver_data }, .data = &exynos5_fimd_driver_data },
{}, {},
}; };
MODULE_DEVICE_TABLE(of, fimd_driver_dt_match);
static inline struct fimd_driver_data *drm_fimd_get_driver_data( static inline struct fimd_driver_data *drm_fimd_get_driver_data(
struct platform_device *pdev) struct platform_device *pdev)
...@@ -243,6 +282,14 @@ static u32 fimd_calc_clkdiv(struct fimd_context *ctx, ...@@ -243,6 +282,14 @@ static u32 fimd_calc_clkdiv(struct fimd_context *ctx,
unsigned long ideal_clk = mode->htotal * mode->vtotal * mode->vrefresh; unsigned long ideal_clk = mode->htotal * mode->vtotal * mode->vrefresh;
u32 clkdiv; u32 clkdiv;
if (ctx->i80_if) {
/*
* The frame done interrupt should be occurred prior to the
* next TE signal.
*/
ideal_clk *= 2;
}
/* Find the clock divider value that gets us closest to ideal_clk */ /* Find the clock divider value that gets us closest to ideal_clk */
clkdiv = DIV_ROUND_UP(clk_get_rate(ctx->lcd_clk), ideal_clk); clkdiv = DIV_ROUND_UP(clk_get_rate(ctx->lcd_clk), ideal_clk);
...@@ -271,11 +318,10 @@ static void fimd_commit(struct exynos_drm_manager *mgr) ...@@ -271,11 +318,10 @@ static void fimd_commit(struct exynos_drm_manager *mgr)
{ {
struct fimd_context *ctx = mgr->ctx; struct fimd_context *ctx = mgr->ctx;
struct drm_display_mode *mode = &ctx->mode; struct drm_display_mode *mode = &ctx->mode;
struct fimd_driver_data *driver_data; struct fimd_driver_data *driver_data = ctx->driver_data;
u32 val, clkdiv, vidcon1; void *timing_base = ctx->regs + driver_data->timing_base;
int vsync_len, vbpd, vfpd, hsync_len, hbpd, hfpd; u32 val, clkdiv;
driver_data = ctx->driver_data;
if (ctx->suspended) if (ctx->suspended)
return; return;
...@@ -283,33 +329,65 @@ static void fimd_commit(struct exynos_drm_manager *mgr) ...@@ -283,33 +329,65 @@ static void fimd_commit(struct exynos_drm_manager *mgr)
if (mode->htotal == 0 || mode->vtotal == 0) if (mode->htotal == 0 || mode->vtotal == 0)
return; return;
/* setup polarity values */ if (ctx->i80_if) {
vidcon1 = ctx->vidcon1; val = ctx->i80ifcon | I80IFEN_ENABLE;
if (mode->flags & DRM_MODE_FLAG_NVSYNC) writel(val, timing_base + I80IFCONFAx(0));
vidcon1 |= VIDCON1_INV_VSYNC;
if (mode->flags & DRM_MODE_FLAG_NHSYNC) /* disable auto frame rate */
vidcon1 |= VIDCON1_INV_HSYNC; writel(0, timing_base + I80IFCONFBx(0));
writel(vidcon1, ctx->regs + driver_data->timing_base + VIDCON1);
/* set video type selection to I80 interface */
/* setup vertical timing values. */ if (ctx->sysreg && regmap_update_bits(ctx->sysreg,
vsync_len = mode->crtc_vsync_end - mode->crtc_vsync_start; driver_data->lcdblk_offset,
vbpd = mode->crtc_vtotal - mode->crtc_vsync_end; 0x3 << driver_data->lcdblk_vt_shift,
vfpd = mode->crtc_vsync_start - mode->crtc_vdisplay; 0x1 << driver_data->lcdblk_vt_shift)) {
DRM_ERROR("Failed to update sysreg for I80 i/f.\n");
val = VIDTCON0_VBPD(vbpd - 1) | return;
VIDTCON0_VFPD(vfpd - 1) | }
VIDTCON0_VSPW(vsync_len - 1); } else {
writel(val, ctx->regs + driver_data->timing_base + VIDTCON0); int vsync_len, vbpd, vfpd, hsync_len, hbpd, hfpd;
u32 vidcon1;
/* setup horizontal timing values. */
hsync_len = mode->crtc_hsync_end - mode->crtc_hsync_start; /* setup polarity values */
hbpd = mode->crtc_htotal - mode->crtc_hsync_end; vidcon1 = ctx->vidcon1;
hfpd = mode->crtc_hsync_start - mode->crtc_hdisplay; if (mode->flags & DRM_MODE_FLAG_NVSYNC)
vidcon1 |= VIDCON1_INV_VSYNC;
val = VIDTCON1_HBPD(hbpd - 1) | if (mode->flags & DRM_MODE_FLAG_NHSYNC)
VIDTCON1_HFPD(hfpd - 1) | vidcon1 |= VIDCON1_INV_HSYNC;
VIDTCON1_HSPW(hsync_len - 1); writel(vidcon1, ctx->regs + driver_data->timing_base + VIDCON1);
writel(val, ctx->regs + driver_data->timing_base + VIDTCON1);
/* setup vertical timing values. */
vsync_len = mode->crtc_vsync_end - mode->crtc_vsync_start;
vbpd = mode->crtc_vtotal - mode->crtc_vsync_end;
vfpd = mode->crtc_vsync_start - mode->crtc_vdisplay;
val = VIDTCON0_VBPD(vbpd - 1) |
VIDTCON0_VFPD(vfpd - 1) |
VIDTCON0_VSPW(vsync_len - 1);
writel(val, ctx->regs + driver_data->timing_base + VIDTCON0);
/* setup horizontal timing values. */
hsync_len = mode->crtc_hsync_end - mode->crtc_hsync_start;
hbpd = mode->crtc_htotal - mode->crtc_hsync_end;
hfpd = mode->crtc_hsync_start - mode->crtc_hdisplay;
val = VIDTCON1_HBPD(hbpd - 1) |
VIDTCON1_HFPD(hfpd - 1) |
VIDTCON1_HSPW(hsync_len - 1);
writel(val, ctx->regs + driver_data->timing_base + VIDTCON1);
}
if (driver_data->has_vidoutcon)
writel(ctx->vidout_con, timing_base + VIDOUT_CON);
/* set bypass selection */
if (ctx->sysreg && regmap_update_bits(ctx->sysreg,
driver_data->lcdblk_offset,
0x1 << driver_data->lcdblk_bypass_shift,
0x1 << driver_data->lcdblk_bypass_shift)) {
DRM_ERROR("Failed to update sysreg for bypass setting.\n");
return;
}
/* setup horizontal and vertical display size. */ /* setup horizontal and vertical display size. */
val = VIDTCON2_LINEVAL(mode->vdisplay - 1) | val = VIDTCON2_LINEVAL(mode->vdisplay - 1) |
...@@ -322,7 +400,8 @@ static void fimd_commit(struct exynos_drm_manager *mgr) ...@@ -322,7 +400,8 @@ static void fimd_commit(struct exynos_drm_manager *mgr)
* fields of register with prefix '_F' would be updated * fields of register with prefix '_F' would be updated
* at vsync(same as dma start) * at vsync(same as dma start)
*/ */
val = VIDCON0_ENVID | VIDCON0_ENVID_F; val = ctx->vidcon0;
val |= VIDCON0_ENVID | VIDCON0_ENVID_F;
if (ctx->driver_data->has_clksel) if (ctx->driver_data->has_clksel)
val |= VIDCON0_CLKSEL_LCD; val |= VIDCON0_CLKSEL_LCD;
...@@ -660,6 +739,9 @@ static void fimd_win_commit(struct exynos_drm_manager *mgr, int zpos) ...@@ -660,6 +739,9 @@ static void fimd_win_commit(struct exynos_drm_manager *mgr, int zpos)
} }
win_data->enabled = true; win_data->enabled = true;
if (ctx->i80_if)
atomic_set(&ctx->win_updated, 1);
} }
static void fimd_win_disable(struct exynos_drm_manager *mgr, int zpos) static void fimd_win_disable(struct exynos_drm_manager *mgr, int zpos)
...@@ -838,6 +920,58 @@ static void fimd_dpms(struct exynos_drm_manager *mgr, int mode) ...@@ -838,6 +920,58 @@ static void fimd_dpms(struct exynos_drm_manager *mgr, int mode)
} }
} }
static void fimd_trigger(struct device *dev)
{
struct exynos_drm_manager *mgr = get_fimd_manager(dev);
struct fimd_context *ctx = mgr->ctx;
struct fimd_driver_data *driver_data = ctx->driver_data;
void *timing_base = ctx->regs + driver_data->timing_base;
u32 reg;
atomic_set(&ctx->triggering, 1);
reg = readl(ctx->regs + VIDINTCON0);
reg |= (VIDINTCON0_INT_ENABLE | VIDINTCON0_INT_I80IFDONE |
VIDINTCON0_INT_SYSMAINCON);
writel(reg, ctx->regs + VIDINTCON0);
reg = readl(timing_base + TRIGCON);
reg |= (TRGMODE_I80_RGB_ENABLE_I80 | SWTRGCMD_I80_RGB_ENABLE);
writel(reg, timing_base + TRIGCON);
}
static void fimd_te_handler(struct exynos_drm_manager *mgr)
{
struct fimd_context *ctx = mgr->ctx;
/* Checks the crtc is detached already from encoder */
if (ctx->pipe < 0 || !ctx->drm_dev)
return;
/*
* Skips to trigger if in triggering state, because multiple triggering
* requests can cause panel reset.
*/
if (atomic_read(&ctx->triggering))
return;
/*
* If there is a page flip request, triggers and handles the page flip
* event so that current fb can be updated into panel GRAM.
*/
if (atomic_add_unless(&ctx->win_updated, -1, 0))
fimd_trigger(ctx->dev);
/* Wakes up vsync event queue */
if (atomic_read(&ctx->wait_vsync_event)) {
atomic_set(&ctx->wait_vsync_event, 0);
wake_up(&ctx->wait_vsync_queue);
if (!atomic_read(&ctx->triggering))
drm_handle_vblank(ctx->drm_dev, ctx->pipe);
}
}
static struct exynos_drm_manager_ops fimd_manager_ops = { static struct exynos_drm_manager_ops fimd_manager_ops = {
.dpms = fimd_dpms, .dpms = fimd_dpms,
.mode_fixup = fimd_mode_fixup, .mode_fixup = fimd_mode_fixup,
...@@ -849,6 +983,7 @@ static struct exynos_drm_manager_ops fimd_manager_ops = { ...@@ -849,6 +983,7 @@ static struct exynos_drm_manager_ops fimd_manager_ops = {
.win_mode_set = fimd_win_mode_set, .win_mode_set = fimd_win_mode_set,
.win_commit = fimd_win_commit, .win_commit = fimd_win_commit,
.win_disable = fimd_win_disable, .win_disable = fimd_win_disable,
.te_handler = fimd_te_handler,
}; };
static struct exynos_drm_manager fimd_manager = { static struct exynos_drm_manager fimd_manager = {
...@@ -859,26 +994,40 @@ static struct exynos_drm_manager fimd_manager = { ...@@ -859,26 +994,40 @@ static struct exynos_drm_manager fimd_manager = {
static irqreturn_t fimd_irq_handler(int irq, void *dev_id) static irqreturn_t fimd_irq_handler(int irq, void *dev_id)
{ {
struct fimd_context *ctx = (struct fimd_context *)dev_id; struct fimd_context *ctx = (struct fimd_context *)dev_id;
u32 val; u32 val, clear_bit;
val = readl(ctx->regs + VIDINTCON1); val = readl(ctx->regs + VIDINTCON1);
if (val & VIDINTCON1_INT_FRAME) clear_bit = ctx->i80_if ? VIDINTCON1_INT_I80 : VIDINTCON1_INT_FRAME;
/* VSYNC interrupt */ if (val & clear_bit)
writel(VIDINTCON1_INT_FRAME, ctx->regs + VIDINTCON1); writel(clear_bit, ctx->regs + VIDINTCON1);
/* check the crtc is detached already from encoder */ /* check the crtc is detached already from encoder */
if (ctx->pipe < 0 || !ctx->drm_dev) if (ctx->pipe < 0 || !ctx->drm_dev)
goto out; goto out;
drm_handle_vblank(ctx->drm_dev, ctx->pipe); if (ctx->i80_if) {
exynos_drm_crtc_finish_pageflip(ctx->drm_dev, ctx->pipe); /* unset I80 frame done interrupt */
val = readl(ctx->regs + VIDINTCON0);
val &= ~(VIDINTCON0_INT_I80IFDONE | VIDINTCON0_INT_SYSMAINCON);
writel(val, ctx->regs + VIDINTCON0);
/* set wait vsync event to zero and wake up queue. */ /* exit triggering mode */
if (atomic_read(&ctx->wait_vsync_event)) { atomic_set(&ctx->triggering, 0);
atomic_set(&ctx->wait_vsync_event, 0);
wake_up(&ctx->wait_vsync_queue); drm_handle_vblank(ctx->drm_dev, ctx->pipe);
exynos_drm_crtc_finish_pageflip(ctx->drm_dev, ctx->pipe);
} else {
drm_handle_vblank(ctx->drm_dev, ctx->pipe);
exynos_drm_crtc_finish_pageflip(ctx->drm_dev, ctx->pipe);
/* set wait vsync event to zero and wake up queue. */
if (atomic_read(&ctx->wait_vsync_event)) {
atomic_set(&ctx->wait_vsync_event, 0);
wake_up(&ctx->wait_vsync_queue);
}
} }
out: out:
return IRQ_HANDLED; return IRQ_HANDLED;
} }
...@@ -923,6 +1072,7 @@ static int fimd_probe(struct platform_device *pdev) ...@@ -923,6 +1072,7 @@ static int fimd_probe(struct platform_device *pdev)
{ {
struct device *dev = &pdev->dev; struct device *dev = &pdev->dev;
struct fimd_context *ctx; struct fimd_context *ctx;
struct device_node *i80_if_timings;
struct resource *res; struct resource *res;
int ret = -EINVAL; int ret = -EINVAL;
...@@ -944,12 +1094,51 @@ static int fimd_probe(struct platform_device *pdev) ...@@ -944,12 +1094,51 @@ static int fimd_probe(struct platform_device *pdev)
ctx->dev = dev; ctx->dev = dev;
ctx->suspended = true; ctx->suspended = true;
ctx->driver_data = drm_fimd_get_driver_data(pdev);
if (of_property_read_bool(dev->of_node, "samsung,invert-vden")) if (of_property_read_bool(dev->of_node, "samsung,invert-vden"))
ctx->vidcon1 |= VIDCON1_INV_VDEN; ctx->vidcon1 |= VIDCON1_INV_VDEN;
if (of_property_read_bool(dev->of_node, "samsung,invert-vclk")) if (of_property_read_bool(dev->of_node, "samsung,invert-vclk"))
ctx->vidcon1 |= VIDCON1_INV_VCLK; ctx->vidcon1 |= VIDCON1_INV_VCLK;
i80_if_timings = of_get_child_by_name(dev->of_node, "i80-if-timings");
if (i80_if_timings) {
u32 val;
ctx->i80_if = true;
if (ctx->driver_data->has_vidoutcon)
ctx->vidout_con |= VIDOUT_CON_F_I80_LDI0;
else
ctx->vidcon0 |= VIDCON0_VIDOUT_I80_LDI0;
/*
* The user manual describes that this "DSI_EN" bit is required
* to enable I80 24-bit data interface.
*/
ctx->vidcon0 |= VIDCON0_DSI_EN;
if (of_property_read_u32(i80_if_timings, "cs-setup", &val))
val = 0;
ctx->i80ifcon = LCD_CS_SETUP(val);
if (of_property_read_u32(i80_if_timings, "wr-setup", &val))
val = 0;
ctx->i80ifcon |= LCD_WR_SETUP(val);
if (of_property_read_u32(i80_if_timings, "wr-active", &val))
val = 1;
ctx->i80ifcon |= LCD_WR_ACTIVE(val);
if (of_property_read_u32(i80_if_timings, "wr-hold", &val))
val = 0;
ctx->i80ifcon |= LCD_WR_HOLD(val);
}
of_node_put(i80_if_timings);
ctx->sysreg = syscon_regmap_lookup_by_phandle(dev->of_node,
"samsung,sysreg");
if (IS_ERR(ctx->sysreg)) {
dev_warn(dev, "failed to get system register.\n");
ctx->sysreg = NULL;
}
ctx->bus_clk = devm_clk_get(dev, "fimd"); ctx->bus_clk = devm_clk_get(dev, "fimd");
if (IS_ERR(ctx->bus_clk)) { if (IS_ERR(ctx->bus_clk)) {
dev_err(dev, "failed to get bus clock\n"); dev_err(dev, "failed to get bus clock\n");
...@@ -972,7 +1161,8 @@ static int fimd_probe(struct platform_device *pdev) ...@@ -972,7 +1161,8 @@ static int fimd_probe(struct platform_device *pdev)
goto err_del_component; goto err_del_component;
} }
res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "vsync"); res = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
ctx->i80_if ? "lcd_sys" : "vsync");
if (!res) { if (!res) {
dev_err(dev, "irq request failed.\n"); dev_err(dev, "irq request failed.\n");
ret = -ENXIO; ret = -ENXIO;
...@@ -986,7 +1176,6 @@ static int fimd_probe(struct platform_device *pdev) ...@@ -986,7 +1176,6 @@ static int fimd_probe(struct platform_device *pdev)
goto err_del_component; goto err_del_component;
} }
ctx->driver_data = drm_fimd_get_driver_data(pdev);
init_waitqueue_head(&ctx->wait_vsync_queue); init_waitqueue_head(&ctx->wait_vsync_queue);
atomic_set(&ctx->wait_vsync_event, 0); atomic_set(&ctx->wait_vsync_event, 0);
......
...@@ -1042,8 +1042,23 @@ static int g2d_check_reg_offset(struct device *dev, ...@@ -1042,8 +1042,23 @@ static int g2d_check_reg_offset(struct device *dev,
int exynos_g2d_get_ver_ioctl(struct drm_device *drm_dev, void *data, int exynos_g2d_get_ver_ioctl(struct drm_device *drm_dev, void *data,
struct drm_file *file) struct drm_file *file)
{ {
struct drm_exynos_file_private *file_priv = file->driver_priv;
struct exynos_drm_g2d_private *g2d_priv = file_priv->g2d_priv;
struct device *dev;
struct g2d_data *g2d;
struct drm_exynos_g2d_get_ver *ver = data; struct drm_exynos_g2d_get_ver *ver = data;
if (!g2d_priv)
return -ENODEV;
dev = g2d_priv->dev;
if (!dev)
return -ENODEV;
g2d = dev_get_drvdata(dev);
if (!g2d)
return -EFAULT;
ver->major = G2D_HW_MAJOR_VER; ver->major = G2D_HW_MAJOR_VER;
ver->minor = G2D_HW_MINOR_VER; ver->minor = G2D_HW_MINOR_VER;
...@@ -1056,7 +1071,7 @@ int exynos_g2d_set_cmdlist_ioctl(struct drm_device *drm_dev, void *data, ...@@ -1056,7 +1071,7 @@ int exynos_g2d_set_cmdlist_ioctl(struct drm_device *drm_dev, void *data,
{ {
struct drm_exynos_file_private *file_priv = file->driver_priv; struct drm_exynos_file_private *file_priv = file->driver_priv;
struct exynos_drm_g2d_private *g2d_priv = file_priv->g2d_priv; struct exynos_drm_g2d_private *g2d_priv = file_priv->g2d_priv;
struct device *dev = g2d_priv->dev; struct device *dev;
struct g2d_data *g2d; struct g2d_data *g2d;
struct drm_exynos_g2d_set_cmdlist *req = data; struct drm_exynos_g2d_set_cmdlist *req = data;
struct drm_exynos_g2d_cmd *cmd; struct drm_exynos_g2d_cmd *cmd;
...@@ -1067,6 +1082,10 @@ int exynos_g2d_set_cmdlist_ioctl(struct drm_device *drm_dev, void *data, ...@@ -1067,6 +1082,10 @@ int exynos_g2d_set_cmdlist_ioctl(struct drm_device *drm_dev, void *data,
int size; int size;
int ret; int ret;
if (!g2d_priv)
return -ENODEV;
dev = g2d_priv->dev;
if (!dev) if (!dev)
return -ENODEV; return -ENODEV;
...@@ -1223,13 +1242,17 @@ int exynos_g2d_exec_ioctl(struct drm_device *drm_dev, void *data, ...@@ -1223,13 +1242,17 @@ int exynos_g2d_exec_ioctl(struct drm_device *drm_dev, void *data,
{ {
struct drm_exynos_file_private *file_priv = file->driver_priv; struct drm_exynos_file_private *file_priv = file->driver_priv;
struct exynos_drm_g2d_private *g2d_priv = file_priv->g2d_priv; struct exynos_drm_g2d_private *g2d_priv = file_priv->g2d_priv;
struct device *dev = g2d_priv->dev; struct device *dev;
struct g2d_data *g2d; struct g2d_data *g2d;
struct drm_exynos_g2d_exec *req = data; struct drm_exynos_g2d_exec *req = data;
struct g2d_runqueue_node *runqueue_node; struct g2d_runqueue_node *runqueue_node;
struct list_head *run_cmdlist; struct list_head *run_cmdlist;
struct list_head *event_list; struct list_head *event_list;
if (!g2d_priv)
return -ENODEV;
dev = g2d_priv->dev;
if (!dev) if (!dev)
return -ENODEV; return -ENODEV;
...@@ -1544,8 +1567,10 @@ static const struct dev_pm_ops g2d_pm_ops = { ...@@ -1544,8 +1567,10 @@ static const struct dev_pm_ops g2d_pm_ops = {
static const struct of_device_id exynos_g2d_match[] = { static const struct of_device_id exynos_g2d_match[] = {
{ .compatible = "samsung,exynos5250-g2d" }, { .compatible = "samsung,exynos5250-g2d" },
{ .compatible = "samsung,exynos4212-g2d" },
{}, {},
}; };
MODULE_DEVICE_TABLE(of, exynos_g2d_match);
struct platform_driver g2d_driver = { struct platform_driver g2d_driver = {
.probe = g2d_probe, .probe = g2d_probe,
......
...@@ -301,7 +301,6 @@ void exynos_drm_gem_put_dma_addr(struct drm_device *dev, ...@@ -301,7 +301,6 @@ void exynos_drm_gem_put_dma_addr(struct drm_device *dev,
unsigned int gem_handle, unsigned int gem_handle,
struct drm_file *filp) struct drm_file *filp)
{ {
struct exynos_drm_gem_obj *exynos_gem_obj;
struct drm_gem_object *obj; struct drm_gem_object *obj;
obj = drm_gem_object_lookup(dev, filp, gem_handle); obj = drm_gem_object_lookup(dev, filp, gem_handle);
...@@ -310,8 +309,6 @@ void exynos_drm_gem_put_dma_addr(struct drm_device *dev, ...@@ -310,8 +309,6 @@ void exynos_drm_gem_put_dma_addr(struct drm_device *dev,
return; return;
} }
exynos_gem_obj = to_exynos_gem_obj(obj);
drm_gem_object_unreference_unlocked(obj); drm_gem_object_unreference_unlocked(obj);
/* /*
......
...@@ -129,9 +129,6 @@ void exynos_platform_device_ipp_unregister(void) ...@@ -129,9 +129,6 @@ void exynos_platform_device_ipp_unregister(void)
int exynos_drm_ippdrv_register(struct exynos_drm_ippdrv *ippdrv) int exynos_drm_ippdrv_register(struct exynos_drm_ippdrv *ippdrv)
{ {
if (!ippdrv)
return -EINVAL;
mutex_lock(&exynos_drm_ippdrv_lock); mutex_lock(&exynos_drm_ippdrv_lock);
list_add_tail(&ippdrv->drv_list, &exynos_drm_ippdrv_list); list_add_tail(&ippdrv->drv_list, &exynos_drm_ippdrv_list);
mutex_unlock(&exynos_drm_ippdrv_lock); mutex_unlock(&exynos_drm_ippdrv_lock);
...@@ -141,9 +138,6 @@ int exynos_drm_ippdrv_register(struct exynos_drm_ippdrv *ippdrv) ...@@ -141,9 +138,6 @@ int exynos_drm_ippdrv_register(struct exynos_drm_ippdrv *ippdrv)
int exynos_drm_ippdrv_unregister(struct exynos_drm_ippdrv *ippdrv) int exynos_drm_ippdrv_unregister(struct exynos_drm_ippdrv *ippdrv)
{ {
if (!ippdrv)
return -EINVAL;
mutex_lock(&exynos_drm_ippdrv_lock); mutex_lock(&exynos_drm_ippdrv_lock);
list_del(&ippdrv->drv_list); list_del(&ippdrv->drv_list);
mutex_unlock(&exynos_drm_ippdrv_lock); mutex_unlock(&exynos_drm_ippdrv_lock);
...@@ -151,20 +145,15 @@ int exynos_drm_ippdrv_unregister(struct exynos_drm_ippdrv *ippdrv) ...@@ -151,20 +145,15 @@ int exynos_drm_ippdrv_unregister(struct exynos_drm_ippdrv *ippdrv)
return 0; return 0;
} }
static int ipp_create_id(struct idr *id_idr, struct mutex *lock, void *obj, static int ipp_create_id(struct idr *id_idr, struct mutex *lock, void *obj)
u32 *idp)
{ {
int ret; int ret;
/* do the allocation under our mutexlock */
mutex_lock(lock); mutex_lock(lock);
ret = idr_alloc(id_idr, obj, 1, 0, GFP_KERNEL); ret = idr_alloc(id_idr, obj, 1, 0, GFP_KERNEL);
mutex_unlock(lock); mutex_unlock(lock);
if (ret < 0)
return ret;
*idp = ret; return ret;
return 0;
} }
static void ipp_remove_id(struct idr *id_idr, struct mutex *lock, u32 id) static void ipp_remove_id(struct idr *id_idr, struct mutex *lock, u32 id)
...@@ -178,35 +167,25 @@ static void *ipp_find_obj(struct idr *id_idr, struct mutex *lock, u32 id) ...@@ -178,35 +167,25 @@ static void *ipp_find_obj(struct idr *id_idr, struct mutex *lock, u32 id)
{ {
void *obj; void *obj;
DRM_DEBUG_KMS("id[%d]\n", id);
mutex_lock(lock); mutex_lock(lock);
/* find object using handle */
obj = idr_find(id_idr, id); obj = idr_find(id_idr, id);
if (!obj) {
DRM_ERROR("failed to find object.\n");
mutex_unlock(lock);
return ERR_PTR(-ENODEV);
}
mutex_unlock(lock); mutex_unlock(lock);
return obj; return obj;
} }
static inline bool ipp_check_dedicated(struct exynos_drm_ippdrv *ippdrv, static int ipp_check_driver(struct exynos_drm_ippdrv *ippdrv,
enum drm_exynos_ipp_cmd cmd) struct drm_exynos_ipp_property *property)
{ {
/* if (ippdrv->dedicated || (!ipp_is_m2m_cmd(property->cmd) &&
* check dedicated flag and WB, OUTPUT operation with !pm_runtime_suspended(ippdrv->dev)))
* power on state. return -EBUSY;
*/
if (ippdrv->dedicated || (!ipp_is_m2m_cmd(cmd) &&
!pm_runtime_suspended(ippdrv->dev)))
return true;
return false; if (ippdrv->check_property &&
ippdrv->check_property(ippdrv->dev, property))
return -EINVAL;
return 0;
} }
static struct exynos_drm_ippdrv *ipp_find_driver(struct ipp_context *ctx, static struct exynos_drm_ippdrv *ipp_find_driver(struct ipp_context *ctx,
...@@ -214,62 +193,30 @@ static struct exynos_drm_ippdrv *ipp_find_driver(struct ipp_context *ctx, ...@@ -214,62 +193,30 @@ static struct exynos_drm_ippdrv *ipp_find_driver(struct ipp_context *ctx,
{ {
struct exynos_drm_ippdrv *ippdrv; struct exynos_drm_ippdrv *ippdrv;
u32 ipp_id = property->ipp_id; u32 ipp_id = property->ipp_id;
int ret;
DRM_DEBUG_KMS("ipp_id[%d]\n", ipp_id);
if (ipp_id) { if (ipp_id) {
/* find ipp driver using idr */ ippdrv = ipp_find_obj(&ctx->ipp_idr, &ctx->ipp_lock, ipp_id);
ippdrv = ipp_find_obj(&ctx->ipp_idr, &ctx->ipp_lock, if (!ippdrv) {
ipp_id); DRM_DEBUG("ipp%d driver not found\n", ipp_id);
if (IS_ERR(ippdrv)) { return ERR_PTR(-ENODEV);
DRM_ERROR("not found ipp%d driver.\n", ipp_id);
return ippdrv;
} }
/* ret = ipp_check_driver(ippdrv, property);
* WB, OUTPUT opertion not supported multi-operation. if (ret < 0) {
* so, make dedicated state at set property ioctl. DRM_DEBUG("ipp%d driver check error %d\n", ipp_id, ret);
* when ipp driver finished operations, clear dedicated flags. return ERR_PTR(ret);
*/
if (ipp_check_dedicated(ippdrv, property->cmd)) {
DRM_ERROR("already used choose device.\n");
return ERR_PTR(-EBUSY);
}
/*
* This is necessary to find correct device in ipp drivers.
* ipp drivers have different abilities,
* so need to check property.
*/
if (ippdrv->check_property &&
ippdrv->check_property(ippdrv->dev, property)) {
DRM_ERROR("not support property.\n");
return ERR_PTR(-EINVAL);
} }
return ippdrv; return ippdrv;
} else { } else {
/*
* This case is search all ipp driver for finding.
* user application don't set ipp_id in this case,
* so ipp subsystem search correct driver in driver list.
*/
list_for_each_entry(ippdrv, &exynos_drm_ippdrv_list, drv_list) { list_for_each_entry(ippdrv, &exynos_drm_ippdrv_list, drv_list) {
if (ipp_check_dedicated(ippdrv, property->cmd)) { ret = ipp_check_driver(ippdrv, property);
DRM_DEBUG_KMS("used device.\n"); if (ret == 0)
continue; return ippdrv;
}
if (ippdrv->check_property &&
ippdrv->check_property(ippdrv->dev, property)) {
DRM_DEBUG_KMS("not support property.\n");
continue;
}
return ippdrv;
} }
DRM_ERROR("not support ipp driver operations.\n"); DRM_DEBUG("cannot find driver suitable for given property.\n");
} }
return ERR_PTR(-ENODEV); return ERR_PTR(-ENODEV);
...@@ -308,8 +255,7 @@ int exynos_drm_ipp_get_property(struct drm_device *drm_dev, void *data, ...@@ -308,8 +255,7 @@ int exynos_drm_ipp_get_property(struct drm_device *drm_dev, void *data,
struct drm_file *file) struct drm_file *file)
{ {
struct drm_exynos_file_private *file_priv = file->driver_priv; struct drm_exynos_file_private *file_priv = file->driver_priv;
struct exynos_drm_ipp_private *priv = file_priv->ipp_priv; struct device *dev = file_priv->ipp_dev;
struct device *dev = priv->dev;
struct ipp_context *ctx = get_ipp_context(dev); struct ipp_context *ctx = get_ipp_context(dev);
struct drm_exynos_ipp_prop_list *prop_list = data; struct drm_exynos_ipp_prop_list *prop_list = data;
struct exynos_drm_ippdrv *ippdrv; struct exynos_drm_ippdrv *ippdrv;
...@@ -346,10 +292,10 @@ int exynos_drm_ipp_get_property(struct drm_device *drm_dev, void *data, ...@@ -346,10 +292,10 @@ int exynos_drm_ipp_get_property(struct drm_device *drm_dev, void *data,
*/ */
ippdrv = ipp_find_obj(&ctx->ipp_idr, &ctx->ipp_lock, ippdrv = ipp_find_obj(&ctx->ipp_idr, &ctx->ipp_lock,
prop_list->ipp_id); prop_list->ipp_id);
if (IS_ERR(ippdrv)) { if (!ippdrv) {
DRM_ERROR("not found ipp%d driver.\n", DRM_ERROR("not found ipp%d driver.\n",
prop_list->ipp_id); prop_list->ipp_id);
return PTR_ERR(ippdrv); return -ENODEV;
} }
*prop_list = ippdrv->prop_list; *prop_list = ippdrv->prop_list;
...@@ -432,7 +378,7 @@ static struct drm_exynos_ipp_event_work *ipp_create_event_work(void) ...@@ -432,7 +378,7 @@ static struct drm_exynos_ipp_event_work *ipp_create_event_work(void)
if (!event_work) if (!event_work)
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
INIT_WORK((struct work_struct *)event_work, ipp_sched_event); INIT_WORK(&event_work->work, ipp_sched_event);
return event_work; return event_work;
} }
...@@ -441,8 +387,7 @@ int exynos_drm_ipp_set_property(struct drm_device *drm_dev, void *data, ...@@ -441,8 +387,7 @@ int exynos_drm_ipp_set_property(struct drm_device *drm_dev, void *data,
struct drm_file *file) struct drm_file *file)
{ {
struct drm_exynos_file_private *file_priv = file->driver_priv; struct drm_exynos_file_private *file_priv = file->driver_priv;
struct exynos_drm_ipp_private *priv = file_priv->ipp_priv; struct device *dev = file_priv->ipp_dev;
struct device *dev = priv->dev;
struct ipp_context *ctx = get_ipp_context(dev); struct ipp_context *ctx = get_ipp_context(dev);
struct drm_exynos_ipp_property *property = data; struct drm_exynos_ipp_property *property = data;
struct exynos_drm_ippdrv *ippdrv; struct exynos_drm_ippdrv *ippdrv;
...@@ -489,19 +434,18 @@ int exynos_drm_ipp_set_property(struct drm_device *drm_dev, void *data, ...@@ -489,19 +434,18 @@ int exynos_drm_ipp_set_property(struct drm_device *drm_dev, void *data,
if (!c_node) if (!c_node)
return -ENOMEM; return -ENOMEM;
/* create property id */ ret = ipp_create_id(&ctx->prop_idr, &ctx->prop_lock, c_node);
ret = ipp_create_id(&ctx->prop_idr, &ctx->prop_lock, c_node, if (ret < 0) {
&property->prop_id);
if (ret) {
DRM_ERROR("failed to create id.\n"); DRM_ERROR("failed to create id.\n");
goto err_clear; goto err_clear;
} }
property->prop_id = ret;
DRM_DEBUG_KMS("created prop_id[%d]cmd[%d]ippdrv[0x%x]\n", DRM_DEBUG_KMS("created prop_id[%d]cmd[%d]ippdrv[0x%x]\n",
property->prop_id, property->cmd, (int)ippdrv); property->prop_id, property->cmd, (int)ippdrv);
/* stored property information and ippdrv in private data */ /* stored property information and ippdrv in private data */
c_node->priv = priv; c_node->dev = dev;
c_node->property = *property; c_node->property = *property;
c_node->state = IPP_STATE_IDLE; c_node->state = IPP_STATE_IDLE;
...@@ -534,7 +478,6 @@ int exynos_drm_ipp_set_property(struct drm_device *drm_dev, void *data, ...@@ -534,7 +478,6 @@ int exynos_drm_ipp_set_property(struct drm_device *drm_dev, void *data,
INIT_LIST_HEAD(&c_node->mem_list[i]); INIT_LIST_HEAD(&c_node->mem_list[i]);
INIT_LIST_HEAD(&c_node->event_list); INIT_LIST_HEAD(&c_node->event_list);
list_splice_init(&priv->event_list, &c_node->event_list);
mutex_lock(&ippdrv->cmd_lock); mutex_lock(&ippdrv->cmd_lock);
list_add_tail(&c_node->list, &ippdrv->cmd_list); list_add_tail(&c_node->list, &ippdrv->cmd_list);
mutex_unlock(&ippdrv->cmd_lock); mutex_unlock(&ippdrv->cmd_lock);
...@@ -577,42 +520,18 @@ static void ipp_clean_cmd_node(struct ipp_context *ctx, ...@@ -577,42 +520,18 @@ static void ipp_clean_cmd_node(struct ipp_context *ctx,
kfree(c_node); kfree(c_node);
} }
static int ipp_check_mem_list(struct drm_exynos_ipp_cmd_node *c_node) static bool ipp_check_mem_list(struct drm_exynos_ipp_cmd_node *c_node)
{ {
struct drm_exynos_ipp_property *property = &c_node->property; switch (c_node->property.cmd) {
struct drm_exynos_ipp_mem_node *m_node; case IPP_CMD_WB:
struct list_head *head; return !list_empty(&c_node->mem_list[EXYNOS_DRM_OPS_DST]);
int ret, i, count[EXYNOS_DRM_OPS_MAX] = { 0, }; case IPP_CMD_OUTPUT:
return !list_empty(&c_node->mem_list[EXYNOS_DRM_OPS_SRC]);
for_each_ipp_ops(i) { case IPP_CMD_M2M:
/* source/destination memory list */ default:
head = &c_node->mem_list[i]; return !list_empty(&c_node->mem_list[EXYNOS_DRM_OPS_SRC]) &&
!list_empty(&c_node->mem_list[EXYNOS_DRM_OPS_DST]);
/* find memory node entry */
list_for_each_entry(m_node, head, list) {
DRM_DEBUG_KMS("%s,count[%d]m_node[0x%x]\n",
i ? "dst" : "src", count[i], (int)m_node);
count[i]++;
}
} }
DRM_DEBUG_KMS("min[%d]max[%d]\n",
min(count[EXYNOS_DRM_OPS_SRC], count[EXYNOS_DRM_OPS_DST]),
max(count[EXYNOS_DRM_OPS_SRC], count[EXYNOS_DRM_OPS_DST]));
/*
* M2M operations should be need paired memory address.
* so, need to check minimum count about src, dst.
* other case not use paired memory, so use maximum count
*/
if (ipp_is_m2m_cmd(property->cmd))
ret = min(count[EXYNOS_DRM_OPS_SRC],
count[EXYNOS_DRM_OPS_DST]);
else
ret = max(count[EXYNOS_DRM_OPS_SRC],
count[EXYNOS_DRM_OPS_DST]);
return ret;
} }
static struct drm_exynos_ipp_mem_node static struct drm_exynos_ipp_mem_node
...@@ -683,16 +602,14 @@ static struct drm_exynos_ipp_mem_node ...@@ -683,16 +602,14 @@ static struct drm_exynos_ipp_mem_node
struct drm_exynos_ipp_queue_buf *qbuf) struct drm_exynos_ipp_queue_buf *qbuf)
{ {
struct drm_exynos_ipp_mem_node *m_node; struct drm_exynos_ipp_mem_node *m_node;
struct drm_exynos_ipp_buf_info buf_info; struct drm_exynos_ipp_buf_info *buf_info;
void *addr;
int i; int i;
m_node = kzalloc(sizeof(*m_node), GFP_KERNEL); m_node = kzalloc(sizeof(*m_node), GFP_KERNEL);
if (!m_node) if (!m_node)
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
/* clear base address for error handling */ buf_info = &m_node->buf_info;
memset(&buf_info, 0x0, sizeof(buf_info));
/* operations, buffer id */ /* operations, buffer id */
m_node->ops_id = qbuf->ops_id; m_node->ops_id = qbuf->ops_id;
...@@ -707,6 +624,8 @@ static struct drm_exynos_ipp_mem_node ...@@ -707,6 +624,8 @@ static struct drm_exynos_ipp_mem_node
/* get dma address by handle */ /* get dma address by handle */
if (qbuf->handle[i]) { if (qbuf->handle[i]) {
dma_addr_t *addr;
addr = exynos_drm_gem_get_dma_addr(drm_dev, addr = exynos_drm_gem_get_dma_addr(drm_dev,
qbuf->handle[i], file); qbuf->handle[i], file);
if (IS_ERR(addr)) { if (IS_ERR(addr)) {
...@@ -714,15 +633,14 @@ static struct drm_exynos_ipp_mem_node ...@@ -714,15 +633,14 @@ static struct drm_exynos_ipp_mem_node
goto err_clear; goto err_clear;
} }
buf_info.handles[i] = qbuf->handle[i]; buf_info->handles[i] = qbuf->handle[i];
buf_info.base[i] = *(dma_addr_t *) addr; buf_info->base[i] = *addr;
DRM_DEBUG_KMS("i[%d]base[0x%x]hd[0x%x]\n", DRM_DEBUG_KMS("i[%d]base[0x%x]hd[0x%lx]\n", i,
i, buf_info.base[i], (int)buf_info.handles[i]); buf_info->base[i], buf_info->handles[i]);
} }
} }
m_node->filp = file; m_node->filp = file;
m_node->buf_info = buf_info;
mutex_lock(&c_node->mem_lock); mutex_lock(&c_node->mem_lock);
list_add_tail(&m_node->list, &c_node->mem_list[qbuf->ops_id]); list_add_tail(&m_node->list, &c_node->mem_list[qbuf->ops_id]);
mutex_unlock(&c_node->mem_lock); mutex_unlock(&c_node->mem_lock);
...@@ -930,8 +848,7 @@ int exynos_drm_ipp_queue_buf(struct drm_device *drm_dev, void *data, ...@@ -930,8 +848,7 @@ int exynos_drm_ipp_queue_buf(struct drm_device *drm_dev, void *data,
struct drm_file *file) struct drm_file *file)
{ {
struct drm_exynos_file_private *file_priv = file->driver_priv; struct drm_exynos_file_private *file_priv = file->driver_priv;
struct exynos_drm_ipp_private *priv = file_priv->ipp_priv; struct device *dev = file_priv->ipp_dev;
struct device *dev = priv->dev;
struct ipp_context *ctx = get_ipp_context(dev); struct ipp_context *ctx = get_ipp_context(dev);
struct drm_exynos_ipp_queue_buf *qbuf = data; struct drm_exynos_ipp_queue_buf *qbuf = data;
struct drm_exynos_ipp_cmd_node *c_node; struct drm_exynos_ipp_cmd_node *c_node;
...@@ -955,9 +872,9 @@ int exynos_drm_ipp_queue_buf(struct drm_device *drm_dev, void *data, ...@@ -955,9 +872,9 @@ int exynos_drm_ipp_queue_buf(struct drm_device *drm_dev, void *data,
/* find command node */ /* find command node */
c_node = ipp_find_obj(&ctx->prop_idr, &ctx->prop_lock, c_node = ipp_find_obj(&ctx->prop_idr, &ctx->prop_lock,
qbuf->prop_id); qbuf->prop_id);
if (IS_ERR(c_node)) { if (!c_node) {
DRM_ERROR("failed to get command node.\n"); DRM_ERROR("failed to get command node.\n");
return PTR_ERR(c_node); return -ENODEV;
} }
/* buffer control */ /* buffer control */
...@@ -1062,9 +979,8 @@ int exynos_drm_ipp_cmd_ctrl(struct drm_device *drm_dev, void *data, ...@@ -1062,9 +979,8 @@ int exynos_drm_ipp_cmd_ctrl(struct drm_device *drm_dev, void *data,
struct drm_file *file) struct drm_file *file)
{ {
struct drm_exynos_file_private *file_priv = file->driver_priv; struct drm_exynos_file_private *file_priv = file->driver_priv;
struct exynos_drm_ipp_private *priv = file_priv->ipp_priv;
struct exynos_drm_ippdrv *ippdrv = NULL; struct exynos_drm_ippdrv *ippdrv = NULL;
struct device *dev = priv->dev; struct device *dev = file_priv->ipp_dev;
struct ipp_context *ctx = get_ipp_context(dev); struct ipp_context *ctx = get_ipp_context(dev);
struct drm_exynos_ipp_cmd_ctrl *cmd_ctrl = data; struct drm_exynos_ipp_cmd_ctrl *cmd_ctrl = data;
struct drm_exynos_ipp_cmd_work *cmd_work; struct drm_exynos_ipp_cmd_work *cmd_work;
...@@ -1091,9 +1007,9 @@ int exynos_drm_ipp_cmd_ctrl(struct drm_device *drm_dev, void *data, ...@@ -1091,9 +1007,9 @@ int exynos_drm_ipp_cmd_ctrl(struct drm_device *drm_dev, void *data,
c_node = ipp_find_obj(&ctx->prop_idr, &ctx->prop_lock, c_node = ipp_find_obj(&ctx->prop_idr, &ctx->prop_lock,
cmd_ctrl->prop_id); cmd_ctrl->prop_id);
if (IS_ERR(c_node)) { if (!c_node) {
DRM_ERROR("invalid command node list.\n"); DRM_ERROR("invalid command node list.\n");
return PTR_ERR(c_node); return -ENODEV;
} }
if (!exynos_drm_ipp_check_valid(ippdrv->dev, cmd_ctrl->ctrl, if (!exynos_drm_ipp_check_valid(ippdrv->dev, cmd_ctrl->ctrl,
...@@ -1198,7 +1114,6 @@ static int ipp_set_property(struct exynos_drm_ippdrv *ippdrv, ...@@ -1198,7 +1114,6 @@ static int ipp_set_property(struct exynos_drm_ippdrv *ippdrv,
/* reset h/w block */ /* reset h/w block */
if (ippdrv->reset && if (ippdrv->reset &&
ippdrv->reset(ippdrv->dev)) { ippdrv->reset(ippdrv->dev)) {
DRM_ERROR("failed to reset.\n");
return -EINVAL; return -EINVAL;
} }
...@@ -1216,30 +1131,24 @@ static int ipp_set_property(struct exynos_drm_ippdrv *ippdrv, ...@@ -1216,30 +1131,24 @@ static int ipp_set_property(struct exynos_drm_ippdrv *ippdrv,
/* set format */ /* set format */
if (ops->set_fmt) { if (ops->set_fmt) {
ret = ops->set_fmt(ippdrv->dev, config->fmt); ret = ops->set_fmt(ippdrv->dev, config->fmt);
if (ret) { if (ret)
DRM_ERROR("not support format.\n");
return ret; return ret;
}
} }
/* set transform for rotation, flip */ /* set transform for rotation, flip */
if (ops->set_transf) { if (ops->set_transf) {
ret = ops->set_transf(ippdrv->dev, config->degree, ret = ops->set_transf(ippdrv->dev, config->degree,
config->flip, &swap); config->flip, &swap);
if (ret) { if (ret)
DRM_ERROR("not support tranf.\n"); return ret;
return -EINVAL;
}
} }
/* set size */ /* set size */
if (ops->set_size) { if (ops->set_size) {
ret = ops->set_size(ippdrv->dev, swap, &config->pos, ret = ops->set_size(ippdrv->dev, swap, &config->pos,
&config->sz); &config->sz);
if (ret) { if (ret)
DRM_ERROR("not support size.\n");
return ret; return ret;
}
} }
} }
...@@ -1283,11 +1192,6 @@ static int ipp_start_property(struct exynos_drm_ippdrv *ippdrv, ...@@ -1283,11 +1192,6 @@ static int ipp_start_property(struct exynos_drm_ippdrv *ippdrv,
m_node = list_first_entry(head, m_node = list_first_entry(head,
struct drm_exynos_ipp_mem_node, list); struct drm_exynos_ipp_mem_node, list);
if (!m_node) {
DRM_ERROR("failed to get node.\n");
ret = -EFAULT;
goto err_unlock;
}
DRM_DEBUG_KMS("m_node[0x%x]\n", (int)m_node); DRM_DEBUG_KMS("m_node[0x%x]\n", (int)m_node);
...@@ -1545,11 +1449,6 @@ static int ipp_send_event(struct exynos_drm_ippdrv *ippdrv, ...@@ -1545,11 +1449,6 @@ static int ipp_send_event(struct exynos_drm_ippdrv *ippdrv,
m_node = list_first_entry(head, m_node = list_first_entry(head,
struct drm_exynos_ipp_mem_node, list); struct drm_exynos_ipp_mem_node, list);
if (!m_node) {
DRM_ERROR("empty memory node.\n");
ret = -ENOMEM;
goto err_mem_unlock;
}
tbuf_id[i] = m_node->buf_id; tbuf_id[i] = m_node->buf_id;
DRM_DEBUG_KMS("%s buf_id[%d]\n", DRM_DEBUG_KMS("%s buf_id[%d]\n",
...@@ -1586,11 +1485,6 @@ static int ipp_send_event(struct exynos_drm_ippdrv *ippdrv, ...@@ -1586,11 +1485,6 @@ static int ipp_send_event(struct exynos_drm_ippdrv *ippdrv,
m_node = list_first_entry(head, m_node = list_first_entry(head,
struct drm_exynos_ipp_mem_node, list); struct drm_exynos_ipp_mem_node, list);
if (!m_node) {
DRM_ERROR("empty memory node.\n");
ret = -ENOMEM;
goto err_mem_unlock;
}
tbuf_id[EXYNOS_DRM_OPS_SRC] = m_node->buf_id; tbuf_id[EXYNOS_DRM_OPS_SRC] = m_node->buf_id;
...@@ -1704,21 +1598,17 @@ static int ipp_subdrv_probe(struct drm_device *drm_dev, struct device *dev) ...@@ -1704,21 +1598,17 @@ static int ipp_subdrv_probe(struct drm_device *drm_dev, struct device *dev)
/* get ipp driver entry */ /* get ipp driver entry */
list_for_each_entry(ippdrv, &exynos_drm_ippdrv_list, drv_list) { list_for_each_entry(ippdrv, &exynos_drm_ippdrv_list, drv_list) {
u32 ipp_id;
ippdrv->drm_dev = drm_dev; ippdrv->drm_dev = drm_dev;
ret = ipp_create_id(&ctx->ipp_idr, &ctx->ipp_lock, ippdrv, ret = ipp_create_id(&ctx->ipp_idr, &ctx->ipp_lock, ippdrv);
&ipp_id); if (ret < 0) {
if (ret || ipp_id == 0) {
DRM_ERROR("failed to create id.\n"); DRM_ERROR("failed to create id.\n");
goto err; goto err;
} }
ippdrv->prop_list.ipp_id = ret;
DRM_DEBUG_KMS("count[%d]ippdrv[0x%x]ipp_id[%d]\n", DRM_DEBUG_KMS("count[%d]ippdrv[0x%x]ipp_id[%d]\n",
count++, (int)ippdrv, ipp_id); count++, (int)ippdrv, ret);
ippdrv->prop_list.ipp_id = ipp_id;
/* store parent device for node */ /* store parent device for node */
ippdrv->parent_dev = dev; ippdrv->parent_dev = dev;
...@@ -1776,17 +1666,10 @@ static int ipp_subdrv_open(struct drm_device *drm_dev, struct device *dev, ...@@ -1776,17 +1666,10 @@ static int ipp_subdrv_open(struct drm_device *drm_dev, struct device *dev,
struct drm_file *file) struct drm_file *file)
{ {
struct drm_exynos_file_private *file_priv = file->driver_priv; struct drm_exynos_file_private *file_priv = file->driver_priv;
struct exynos_drm_ipp_private *priv;
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
priv->dev = dev;
file_priv->ipp_priv = priv;
INIT_LIST_HEAD(&priv->event_list); file_priv->ipp_dev = dev;
DRM_DEBUG_KMS("done priv[0x%x]\n", (int)priv); DRM_DEBUG_KMS("done priv[0x%x]\n", (int)dev);
return 0; return 0;
} }
...@@ -1795,13 +1678,12 @@ static void ipp_subdrv_close(struct drm_device *drm_dev, struct device *dev, ...@@ -1795,13 +1678,12 @@ static void ipp_subdrv_close(struct drm_device *drm_dev, struct device *dev,
struct drm_file *file) struct drm_file *file)
{ {
struct drm_exynos_file_private *file_priv = file->driver_priv; struct drm_exynos_file_private *file_priv = file->driver_priv;
struct exynos_drm_ipp_private *priv = file_priv->ipp_priv;
struct exynos_drm_ippdrv *ippdrv = NULL; struct exynos_drm_ippdrv *ippdrv = NULL;
struct ipp_context *ctx = get_ipp_context(dev); struct ipp_context *ctx = get_ipp_context(dev);
struct drm_exynos_ipp_cmd_node *c_node, *tc_node; struct drm_exynos_ipp_cmd_node *c_node, *tc_node;
int count = 0; int count = 0;
DRM_DEBUG_KMS("for priv[0x%x]\n", (int)priv); DRM_DEBUG_KMS("for priv[0x%x]\n", (int)file_priv->ipp_dev);
list_for_each_entry(ippdrv, &exynos_drm_ippdrv_list, drv_list) { list_for_each_entry(ippdrv, &exynos_drm_ippdrv_list, drv_list) {
mutex_lock(&ippdrv->cmd_lock); mutex_lock(&ippdrv->cmd_lock);
...@@ -1810,7 +1692,7 @@ static void ipp_subdrv_close(struct drm_device *drm_dev, struct device *dev, ...@@ -1810,7 +1692,7 @@ static void ipp_subdrv_close(struct drm_device *drm_dev, struct device *dev,
DRM_DEBUG_KMS("count[%d]ippdrv[0x%x]\n", DRM_DEBUG_KMS("count[%d]ippdrv[0x%x]\n",
count++, (int)ippdrv); count++, (int)ippdrv);
if (c_node->priv == priv) { if (c_node->dev == file_priv->ipp_dev) {
/* /*
* userland goto unnormal state. process killed. * userland goto unnormal state. process killed.
* and close the file. * and close the file.
...@@ -1832,7 +1714,6 @@ static void ipp_subdrv_close(struct drm_device *drm_dev, struct device *dev, ...@@ -1832,7 +1714,6 @@ static void ipp_subdrv_close(struct drm_device *drm_dev, struct device *dev,
mutex_unlock(&ippdrv->cmd_lock); mutex_unlock(&ippdrv->cmd_lock);
} }
kfree(priv);
return; return;
} }
......
...@@ -48,7 +48,7 @@ struct drm_exynos_ipp_cmd_work { ...@@ -48,7 +48,7 @@ struct drm_exynos_ipp_cmd_work {
/* /*
* A structure of command node. * A structure of command node.
* *
* @priv: IPP private information. * @dev: IPP device.
* @list: list head to command queue information. * @list: list head to command queue information.
* @event_list: list head of event. * @event_list: list head of event.
* @mem_list: list head to source,destination memory queue information. * @mem_list: list head to source,destination memory queue information.
...@@ -64,7 +64,7 @@ struct drm_exynos_ipp_cmd_work { ...@@ -64,7 +64,7 @@ struct drm_exynos_ipp_cmd_work {
* @state: state of command node. * @state: state of command node.
*/ */
struct drm_exynos_ipp_cmd_node { struct drm_exynos_ipp_cmd_node {
struct exynos_drm_ipp_private *priv; struct device *dev;
struct list_head list; struct list_head list;
struct list_head event_list; struct list_head event_list;
struct list_head mem_list[EXYNOS_DRM_OPS_MAX]; struct list_head mem_list[EXYNOS_DRM_OPS_MAX];
......
...@@ -691,6 +691,7 @@ static const struct of_device_id exynos_rotator_match[] = { ...@@ -691,6 +691,7 @@ static const struct of_device_id exynos_rotator_match[] = {
}, },
{}, {},
}; };
MODULE_DEVICE_TABLE(of, exynos_rotator_match);
static int rotator_probe(struct platform_device *pdev) static int rotator_probe(struct platform_device *pdev)
{ {
......
...@@ -84,6 +84,7 @@ struct hdmi_resources { ...@@ -84,6 +84,7 @@ struct hdmi_resources {
struct clk *sclk_hdmiphy; struct clk *sclk_hdmiphy;
struct clk *mout_hdmi; struct clk *mout_hdmi;
struct regulator_bulk_data *regul_bulk; struct regulator_bulk_data *regul_bulk;
struct regulator *reg_hdmi_en;
int regul_count; int regul_count;
}; };
...@@ -592,6 +593,13 @@ static struct hdmi_driver_data exynos4212_hdmi_driver_data = { ...@@ -592,6 +593,13 @@ static struct hdmi_driver_data exynos4212_hdmi_driver_data = {
.is_apb_phy = 0, .is_apb_phy = 0,
}; };
static struct hdmi_driver_data exynos4210_hdmi_driver_data = {
.type = HDMI_TYPE13,
.phy_confs = hdmiphy_v13_configs,
.phy_conf_count = ARRAY_SIZE(hdmiphy_v13_configs),
.is_apb_phy = 0,
};
static struct hdmi_driver_data exynos5_hdmi_driver_data = { static struct hdmi_driver_data exynos5_hdmi_driver_data = {
.type = HDMI_TYPE14, .type = HDMI_TYPE14,
.phy_confs = hdmiphy_v13_configs, .phy_confs = hdmiphy_v13_configs,
...@@ -1241,14 +1249,13 @@ static void hdmi_reg_acr(struct hdmi_context *hdata, u8 *acr) ...@@ -1241,14 +1249,13 @@ static void hdmi_reg_acr(struct hdmi_context *hdata, u8 *acr)
static void hdmi_audio_init(struct hdmi_context *hdata) static void hdmi_audio_init(struct hdmi_context *hdata)
{ {
u32 sample_rate, bits_per_sample, frame_size_code; u32 sample_rate, bits_per_sample;
u32 data_num, bit_ch, sample_frq; u32 data_num, bit_ch, sample_frq;
u32 val; u32 val;
u8 acr[7]; u8 acr[7];
sample_rate = 44100; sample_rate = 44100;
bits_per_sample = 16; bits_per_sample = 16;
frame_size_code = 0;
switch (bits_per_sample) { switch (bits_per_sample) {
case 20: case 20:
...@@ -2168,7 +2175,6 @@ static int hdmi_resources_init(struct hdmi_context *hdata) ...@@ -2168,7 +2175,6 @@ static int hdmi_resources_init(struct hdmi_context *hdata)
struct device *dev = hdata->dev; struct device *dev = hdata->dev;
struct hdmi_resources *res = &hdata->res; struct hdmi_resources *res = &hdata->res;
static char *supply[] = { static char *supply[] = {
"hdmi-en",
"vdd", "vdd",
"vdd_osc", "vdd_osc",
"vdd_pll", "vdd_pll",
...@@ -2228,6 +2234,20 @@ static int hdmi_resources_init(struct hdmi_context *hdata) ...@@ -2228,6 +2234,20 @@ static int hdmi_resources_init(struct hdmi_context *hdata)
} }
res->regul_count = ARRAY_SIZE(supply); res->regul_count = ARRAY_SIZE(supply);
res->reg_hdmi_en = devm_regulator_get(dev, "hdmi-en");
if (IS_ERR(res->reg_hdmi_en) && PTR_ERR(res->reg_hdmi_en) != -ENOENT) {
DRM_ERROR("failed to get hdmi-en regulator\n");
return PTR_ERR(res->reg_hdmi_en);
}
if (!IS_ERR(res->reg_hdmi_en)) {
ret = regulator_enable(res->reg_hdmi_en);
if (ret) {
DRM_ERROR("failed to enable hdmi-en regulator\n");
return ret;
}
} else
res->reg_hdmi_en = NULL;
return ret; return ret;
fail: fail:
DRM_ERROR("HDMI resource init - failed\n"); DRM_ERROR("HDMI resource init - failed\n");
...@@ -2262,6 +2282,9 @@ static struct of_device_id hdmi_match_types[] = { ...@@ -2262,6 +2282,9 @@ static struct of_device_id hdmi_match_types[] = {
{ {
.compatible = "samsung,exynos5-hdmi", .compatible = "samsung,exynos5-hdmi",
.data = &exynos5_hdmi_driver_data, .data = &exynos5_hdmi_driver_data,
}, {
.compatible = "samsung,exynos4210-hdmi",
.data = &exynos4210_hdmi_driver_data,
}, { }, {
.compatible = "samsung,exynos4212-hdmi", .compatible = "samsung,exynos4212-hdmi",
.data = &exynos4212_hdmi_driver_data, .data = &exynos4212_hdmi_driver_data,
...@@ -2272,6 +2295,7 @@ static struct of_device_id hdmi_match_types[] = { ...@@ -2272,6 +2295,7 @@ static struct of_device_id hdmi_match_types[] = {
/* end node */ /* end node */
} }
}; };
MODULE_DEVICE_TABLE (of, hdmi_match_types);
static int hdmi_bind(struct device *dev, struct device *master, void *data) static int hdmi_bind(struct device *dev, struct device *master, void *data)
{ {
...@@ -2494,7 +2518,11 @@ static int hdmi_remove(struct platform_device *pdev) ...@@ -2494,7 +2518,11 @@ static int hdmi_remove(struct platform_device *pdev)
cancel_delayed_work_sync(&hdata->hotplug_work); cancel_delayed_work_sync(&hdata->hotplug_work);
put_device(&hdata->hdmiphy_port->dev); if (hdata->res.reg_hdmi_en)
regulator_disable(hdata->res.reg_hdmi_en);
if (hdata->hdmiphy_port)
put_device(&hdata->hdmiphy_port->dev);
put_device(&hdata->ddc_adpt->dev); put_device(&hdata->ddc_adpt->dev);
pm_runtime_disable(&pdev->dev); pm_runtime_disable(&pdev->dev);
......
...@@ -76,7 +76,7 @@ struct mixer_resources { ...@@ -76,7 +76,7 @@ struct mixer_resources {
struct clk *vp; struct clk *vp;
struct clk *sclk_mixer; struct clk *sclk_mixer;
struct clk *sclk_hdmi; struct clk *sclk_hdmi;
struct clk *sclk_dac; struct clk *mout_mixer;
}; };
enum mixer_version_id { enum mixer_version_id {
...@@ -93,6 +93,7 @@ struct mixer_context { ...@@ -93,6 +93,7 @@ struct mixer_context {
bool interlace; bool interlace;
bool powered; bool powered;
bool vp_enabled; bool vp_enabled;
bool has_sclk;
u32 int_en; u32 int_en;
struct mutex mixer_mutex; struct mutex mixer_mutex;
...@@ -106,6 +107,7 @@ struct mixer_context { ...@@ -106,6 +107,7 @@ struct mixer_context {
struct mixer_drv_data { struct mixer_drv_data {
enum mixer_version_id version; enum mixer_version_id version;
bool is_vp_enabled; bool is_vp_enabled;
bool has_sclk;
}; };
static const u8 filter_y_horiz_tap8[] = { static const u8 filter_y_horiz_tap8[] = {
...@@ -363,6 +365,11 @@ static void mixer_cfg_layer(struct mixer_context *ctx, int win, bool enable) ...@@ -363,6 +365,11 @@ static void mixer_cfg_layer(struct mixer_context *ctx, int win, bool enable)
vp_reg_writemask(res, VP_ENABLE, val, VP_ENABLE_ON); vp_reg_writemask(res, VP_ENABLE, val, VP_ENABLE_ON);
mixer_reg_writemask(res, MXR_CFG, val, mixer_reg_writemask(res, MXR_CFG, val,
MXR_CFG_VP_ENABLE); MXR_CFG_VP_ENABLE);
/* control blending of graphic layer 0 */
mixer_reg_writemask(res, MXR_GRAPHIC_CFG(0), val,
MXR_GRP_CFG_BLEND_PRE_MUL |
MXR_GRP_CFG_PIXEL_BLEND_EN);
} }
break; break;
} }
...@@ -809,19 +816,23 @@ static int vp_resources_init(struct mixer_context *mixer_ctx) ...@@ -809,19 +816,23 @@ static int vp_resources_init(struct mixer_context *mixer_ctx)
dev_err(dev, "failed to get clock 'vp'\n"); dev_err(dev, "failed to get clock 'vp'\n");
return -ENODEV; return -ENODEV;
} }
mixer_res->sclk_mixer = devm_clk_get(dev, "sclk_mixer");
if (IS_ERR(mixer_res->sclk_mixer)) {
dev_err(dev, "failed to get clock 'sclk_mixer'\n");
return -ENODEV;
}
mixer_res->sclk_dac = devm_clk_get(dev, "sclk_dac");
if (IS_ERR(mixer_res->sclk_dac)) {
dev_err(dev, "failed to get clock 'sclk_dac'\n");
return -ENODEV;
}
if (mixer_res->sclk_hdmi) if (mixer_ctx->has_sclk) {
clk_set_parent(mixer_res->sclk_mixer, mixer_res->sclk_hdmi); mixer_res->sclk_mixer = devm_clk_get(dev, "sclk_mixer");
if (IS_ERR(mixer_res->sclk_mixer)) {
dev_err(dev, "failed to get clock 'sclk_mixer'\n");
return -ENODEV;
}
mixer_res->mout_mixer = devm_clk_get(dev, "mout_mixer");
if (IS_ERR(mixer_res->mout_mixer)) {
dev_err(dev, "failed to get clock 'mout_mixer'\n");
return -ENODEV;
}
if (mixer_res->sclk_hdmi && mixer_res->mout_mixer)
clk_set_parent(mixer_res->mout_mixer,
mixer_res->sclk_hdmi);
}
res = platform_get_resource(mixer_ctx->pdev, IORESOURCE_MEM, 1); res = platform_get_resource(mixer_ctx->pdev, IORESOURCE_MEM, 1);
if (res == NULL) { if (res == NULL) {
...@@ -1082,7 +1093,8 @@ static void mixer_poweron(struct exynos_drm_manager *mgr) ...@@ -1082,7 +1093,8 @@ static void mixer_poweron(struct exynos_drm_manager *mgr)
clk_prepare_enable(res->mixer); clk_prepare_enable(res->mixer);
if (ctx->vp_enabled) { if (ctx->vp_enabled) {
clk_prepare_enable(res->vp); clk_prepare_enable(res->vp);
clk_prepare_enable(res->sclk_mixer); if (ctx->has_sclk)
clk_prepare_enable(res->sclk_mixer);
} }
mutex_lock(&ctx->mixer_mutex); mutex_lock(&ctx->mixer_mutex);
...@@ -1121,7 +1133,8 @@ static void mixer_poweroff(struct exynos_drm_manager *mgr) ...@@ -1121,7 +1133,8 @@ static void mixer_poweroff(struct exynos_drm_manager *mgr)
clk_disable_unprepare(res->mixer); clk_disable_unprepare(res->mixer);
if (ctx->vp_enabled) { if (ctx->vp_enabled) {
clk_disable_unprepare(res->vp); clk_disable_unprepare(res->vp);
clk_disable_unprepare(res->sclk_mixer); if (ctx->has_sclk)
clk_disable_unprepare(res->sclk_mixer);
} }
pm_runtime_put_sync(ctx->dev); pm_runtime_put_sync(ctx->dev);
...@@ -1189,9 +1202,15 @@ static struct mixer_drv_data exynos5250_mxr_drv_data = { ...@@ -1189,9 +1202,15 @@ static struct mixer_drv_data exynos5250_mxr_drv_data = {
.is_vp_enabled = 0, .is_vp_enabled = 0,
}; };
static struct mixer_drv_data exynos4212_mxr_drv_data = {
.version = MXR_VER_0_0_0_16,
.is_vp_enabled = 1,
};
static struct mixer_drv_data exynos4210_mxr_drv_data = { static struct mixer_drv_data exynos4210_mxr_drv_data = {
.version = MXR_VER_0_0_0_16, .version = MXR_VER_0_0_0_16,
.is_vp_enabled = 1, .is_vp_enabled = 1,
.has_sclk = 1,
}; };
static struct platform_device_id mixer_driver_types[] = { static struct platform_device_id mixer_driver_types[] = {
...@@ -1208,6 +1227,12 @@ static struct platform_device_id mixer_driver_types[] = { ...@@ -1208,6 +1227,12 @@ static struct platform_device_id mixer_driver_types[] = {
static struct of_device_id mixer_match_types[] = { static struct of_device_id mixer_match_types[] = {
{ {
.compatible = "samsung,exynos4210-mixer",
.data = &exynos4210_mxr_drv_data,
}, {
.compatible = "samsung,exynos4212-mixer",
.data = &exynos4212_mxr_drv_data,
}, {
.compatible = "samsung,exynos5-mixer", .compatible = "samsung,exynos5-mixer",
.data = &exynos5250_mxr_drv_data, .data = &exynos5250_mxr_drv_data,
}, { }, {
...@@ -1220,6 +1245,7 @@ static struct of_device_id mixer_match_types[] = { ...@@ -1220,6 +1245,7 @@ static struct of_device_id mixer_match_types[] = {
/* end node */ /* end node */
} }
}; };
MODULE_DEVICE_TABLE(of, mixer_match_types);
static int mixer_bind(struct device *dev, struct device *manager, void *data) static int mixer_bind(struct device *dev, struct device *manager, void *data)
{ {
...@@ -1251,6 +1277,7 @@ static int mixer_bind(struct device *dev, struct device *manager, void *data) ...@@ -1251,6 +1277,7 @@ static int mixer_bind(struct device *dev, struct device *manager, void *data)
ctx->pdev = pdev; ctx->pdev = pdev;
ctx->dev = dev; ctx->dev = dev;
ctx->vp_enabled = drv->is_vp_enabled; ctx->vp_enabled = drv->is_vp_enabled;
ctx->has_sclk = drv->has_sclk;
ctx->mxr_ver = drv->version; ctx->mxr_ver = drv->version;
init_waitqueue_head(&ctx->wait_vsync_queue); init_waitqueue_head(&ctx->wait_vsync_queue);
atomic_set(&ctx->wait_vsync_event, 0); atomic_set(&ctx->wait_vsync_event, 0);
......
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
/* VIDCON0 */ /* VIDCON0 */
#define VIDCON0 0x00 #define VIDCON0 0x00
#define VIDCON0_DSI_EN (1 << 30)
#define VIDCON0_INTERLACE (1 << 29) #define VIDCON0_INTERLACE (1 << 29)
#define VIDCON0_VIDOUT_MASK (0x7 << 26) #define VIDCON0_VIDOUT_MASK (0x7 << 26)
#define VIDCON0_VIDOUT_SHIFT 26 #define VIDCON0_VIDOUT_SHIFT 26
...@@ -355,7 +356,7 @@ ...@@ -355,7 +356,7 @@
#define VIDINTCON0_INT_ENABLE (1 << 0) #define VIDINTCON0_INT_ENABLE (1 << 0)
#define VIDINTCON1 0x134 #define VIDINTCON1 0x134
#define VIDINTCON1_INT_I180 (1 << 2) #define VIDINTCON1_INT_I80 (1 << 2)
#define VIDINTCON1_INT_FRAME (1 << 1) #define VIDINTCON1_INT_FRAME (1 << 1)
#define VIDINTCON1_INT_FIFO (1 << 0) #define VIDINTCON1_INT_FIFO (1 << 0)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment