/*
- * Samsung S5P/EXYNOS4 SoC series MIPI-CSI receiver driver
+ * Samsung S5P/EXYNOS SoC series MIPI-CSI receiver driver
  *
- * Copyright (C) 2011 - 2012 Samsung Electronics Co., Ltd.
- * Sylwester Nawrocki <s.nawrocki@samsung.com>
+ * Copyright (C) 2011 - 2013 Samsung Electronics Co., Ltd.
+ * Author: Sylwester Nawrocki <s.nawrocki@samsung.com>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
 
 /* Interrupt mask */
 #define S5PCSIS_INTMSK                 0x10
-#define S5PCSIS_INTMSK_EN_ALL          0xf000103f
 #define S5PCSIS_INTMSK_EVEN_BEFORE     (1 << 31)
 #define S5PCSIS_INTMSK_EVEN_AFTER      (1 << 30)
 #define S5PCSIS_INTMSK_ODD_BEFORE      (1 << 29)
 #define S5PCSIS_INTMSK_ODD_AFTER       (1 << 28)
+#define S5PCSIS_INTMSK_FRAME_START     (1 << 27)
+#define S5PCSIS_INTMSK_FRAME_END       (1 << 26)
 #define S5PCSIS_INTMSK_ERR_SOT_HS      (1 << 12)
 #define S5PCSIS_INTMSK_ERR_LOST_FS     (1 << 5)
 #define S5PCSIS_INTMSK_ERR_LOST_FE     (1 << 4)
 #define S5PCSIS_INTMSK_ERR_ECC         (1 << 2)
 #define S5PCSIS_INTMSK_ERR_CRC         (1 << 1)
 #define S5PCSIS_INTMSK_ERR_UNKNOWN     (1 << 0)
+#define S5PCSIS_INTMSK_EXYNOS4_EN_ALL  0xf000103f
+#define S5PCSIS_INTMSK_EXYNOS5_EN_ALL  0xfc00103f
 
 /* Interrupt source */
 #define S5PCSIS_INTSRC                 0x14
 #define S5PCSIS_INTSRC_ODD_AFTER       (1 << 28)
 #define S5PCSIS_INTSRC_ODD             (0x3 << 28)
 #define S5PCSIS_INTSRC_NON_IMAGE_DATA  (0xff << 28)
+#define S5PCSIS_INTSRC_FRAME_START     (1 << 27)
+#define S5PCSIS_INTSRC_FRAME_END       (1 << 26)
 #define S5PCSIS_INTSRC_ERR_SOT_HS      (0xf << 12)
 #define S5PCSIS_INTSRC_ERR_LOST_FS     (1 << 5)
 #define S5PCSIS_INTSRC_ERR_LOST_FE     (1 << 4)
        { S5PCSIS_INTSRC_EVEN_AFTER,    "Non-image data after even frame" },
        { S5PCSIS_INTSRC_ODD_BEFORE,    "Non-image data before odd frame" },
        { S5PCSIS_INTSRC_ODD_AFTER,     "Non-image data after odd frame" },
+       /* Frame start/end */
+       { S5PCSIS_INTSRC_FRAME_START,   "Frame Start" },
+       { S5PCSIS_INTSRC_FRAME_END,     "Frame End" },
 };
 #define S5PCSIS_NUM_EVENTS ARRAY_SIZE(s5pcsis_events)
 
        unsigned int len;
 };
 
+struct csis_drvdata {
+       /* Mask of all used interrupts in S5PCSIS_INTMSK register */
+       u32 interrupt_mask;
+};
+
 /**
  * struct csis_state - the driver's internal state data structure
  * @lock: mutex serializing the subdev and power management operations,
  * @supplies: CSIS regulator supplies
  * @clock: CSIS clocks
  * @irq: requested s5p-mipi-csis irq number
+ * @interrupt_mask: interrupt mask of the all used interrupts
  * @flags: the state variable for power and streaming control
  * @clock_frequency: device bus clock frequency
  * @hs_settle: HS-RX settle time
        struct regulator_bulk_data supplies[CSIS_NUM_SUPPLIES];
        struct clk *clock[NUM_CSIS_CLOCKS];
        int irq;
+       u32 interrupt_mask;
        u32 flags;
 
        u32 clk_frequency;
 static void s5pcsis_enable_interrupts(struct csis_state *state, bool on)
 {
        u32 val = s5pcsis_read(state, S5PCSIS_INTMSK);
-
-       val = on ? val | S5PCSIS_INTMSK_EN_ALL :
-                  val & ~S5PCSIS_INTMSK_EN_ALL;
+       if (on)
+               val |= state->interrupt_mask;
+       else
+               val &= ~state->interrupt_mask;
        s5pcsis_write(state, S5PCSIS_INTMSK, val);
 }
 
 #define s5pcsis_parse_dt(pdev, state) (-ENOSYS)
 #endif
 
+static const struct of_device_id s5pcsis_of_match[];
+
 static int s5pcsis_probe(struct platform_device *pdev)
 {
+       const struct of_device_id *of_id;
+       const struct csis_drvdata *drv_data;
        struct device *dev = &pdev->dev;
        struct resource *mem_res;
        struct csis_state *state;
        spin_lock_init(&state->slock);
        state->pdev = pdev;
 
-       if (dev->of_node)
+       if (dev->of_node) {
+               of_id = of_match_node(s5pcsis_of_match, dev->of_node);
+               if (WARN_ON(of_id == NULL))
+                       return -EINVAL;
+
+               drv_data = of_id->data;
+               state->interrupt_mask = drv_data->interrupt_mask;
+
                ret = s5pcsis_parse_dt(pdev, state);
-       else
+       } else {
                ret = s5pcsis_get_platform_data(pdev, state);
+       }
+
        if (ret < 0)
                return ret;
 
        SET_SYSTEM_SLEEP_PM_OPS(s5pcsis_suspend, s5pcsis_resume)
 };
 
+static const struct csis_drvdata exynos4_csis_drvdata = {
+       .interrupt_mask = S5PCSIS_INTMSK_EXYNOS4_EN_ALL,
+};
+
+static const struct csis_drvdata exynos5_csis_drvdata = {
+       .interrupt_mask = S5PCSIS_INTMSK_EXYNOS5_EN_ALL,
+};
+
 static const struct of_device_id s5pcsis_of_match[] = {
-       { .compatible = "samsung,s5pv210-csis" },
-       { .compatible = "samsung,exynos4210-csis" },
+       {
+               .compatible = "samsung,s5pv210-csis",
+               .data = &exynos4_csis_drvdata,
+       }, {
+               .compatible = "samsung,exynos4210-csis",
+               .data = &exynos4_csis_drvdata,
+       }, {
+               .compatible = "samsung,exynos5250-csis",
+               .data = &exynos5_csis_drvdata,
+       },
        { /* sentinel */ },
 };
 MODULE_DEVICE_TABLE(of, s5pcsis_of_match);