[media] mt9p031: Add support for PLL bypass
authorLaurent Pinchart <laurent.pinchart@ideasonboard.com>
Sun, 9 Feb 2014 20:31:47 +0000 (17:31 -0300)
committerMauro Carvalho Chehab <m.chehab@samsung.com>
Mon, 24 Feb 2014 16:12:36 +0000 (13:12 -0300)
When the input clock frequency is out of bounds for the PLL, bypass the
PLL and just divide the input clock to achieve the requested output
frequency.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Mauro Carvalho Chehab <m.chehab@samsung.com>
drivers/media/i2c/mt9p031.c

index a27166277deeed8ab4282baea7574d4ada04c10e..fec76d3f056cbf7cf5857da190dd7c36d25ebf17 100644 (file)
@@ -78,6 +78,9 @@
 #define        MT9P031_PLL_CONFIG_1                            0x11
 #define        MT9P031_PLL_CONFIG_2                            0x12
 #define MT9P031_PIXEL_CLOCK_CONTROL                    0x0a
+#define                MT9P031_PIXEL_CLOCK_INVERT              (1 << 15)
+#define                MT9P031_PIXEL_CLOCK_SHIFT(n)            ((n) << 8)
+#define                MT9P031_PIXEL_CLOCK_DIVIDE(n)           ((n) << 0)
 #define MT9P031_FRAME_RESTART                          0x0b
 #define MT9P031_SHUTTER_DELAY                          0x0c
 #define MT9P031_RST                                    0x0d
@@ -130,6 +133,8 @@ struct mt9p031 {
 
        enum mt9p031_model model;
        struct aptina_pll pll;
+       unsigned int clk_div;
+       bool use_pll;
        int reset;
 
        struct v4l2_ctrl_handler ctrls;
@@ -198,6 +203,11 @@ static int mt9p031_reset(struct mt9p031 *mt9p031)
        if (ret < 0)
                return ret;
 
+       ret = mt9p031_write(client, MT9P031_PIXEL_CLOCK_CONTROL,
+                           MT9P031_PIXEL_CLOCK_DIVIDE(mt9p031->clk_div));
+       if (ret < 0)
+               return ret;
+
        return mt9p031_set_output_control(mt9p031, MT9P031_OUTPUT_CONTROL_CEN,
                                          0);
 }
@@ -229,8 +239,24 @@ static int mt9p031_clk_setup(struct mt9p031 *mt9p031)
 
        clk_set_rate(mt9p031->clk, pdata->ext_freq);
 
+       /* If the external clock frequency is out of bounds for the PLL use the
+        * pixel clock divider only and disable the PLL.
+        */
+       if (pdata->ext_freq > limits.ext_clock_max) {
+               unsigned int div;
+
+               div = DIV_ROUND_UP(pdata->ext_freq, pdata->target_freq);
+               div = roundup_pow_of_two(div) / 2;
+
+               mt9p031->clk_div = max_t(unsigned int, div, 64);
+               mt9p031->use_pll = false;
+
+               return 0;
+       }
+
        mt9p031->pll.ext_clock = pdata->ext_freq;
        mt9p031->pll.pix_clock = pdata->target_freq;
+       mt9p031->use_pll = true;
 
        return aptina_pll_calculate(&client->dev, &limits, &mt9p031->pll);
 }
@@ -240,6 +266,9 @@ static int mt9p031_pll_enable(struct mt9p031 *mt9p031)
        struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev);
        int ret;
 
+       if (!mt9p031->use_pll)
+               return 0;
+
        ret = mt9p031_write(client, MT9P031_PLL_CONTROL,
                            MT9P031_PLL_CONTROL_PWRON);
        if (ret < 0)
@@ -265,6 +294,9 @@ static inline int mt9p031_pll_disable(struct mt9p031 *mt9p031)
 {
        struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev);
 
+       if (!mt9p031->use_pll)
+               return 0;
+
        return mt9p031_write(client, MT9P031_PLL_CONTROL,
                             MT9P031_PLL_CONTROL_PWROFF);
 }