Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

Merge branch 'topic/omap3isp' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media

Pull omap3isp clk support from Mauro Carvalho Chehab:
"This patch were sent in separate as it depends on a merge from clock
framework, that you merged in commit 362ed48dee50"

* 'topic/omap3isp' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media:
[media] omap3isp: Use the common clock framework

+225 -84
+202 -75
drivers/media/platform/omap3isp/isp.c
··· 55 55 #include <asm/cacheflush.h> 56 56 57 57 #include <linux/clk.h> 58 + #include <linux/clkdev.h> 58 59 #include <linux/delay.h> 59 60 #include <linux/device.h> 60 61 #include <linux/dma-mapping.h> ··· 149 148 isp_reg_readl(isp, OMAP3_ISP_IOMEM_MAIN, ISP_REVISION); 150 149 } 151 150 151 + /* ----------------------------------------------------------------------------- 152 + * XCLK 153 + */ 154 + 155 + #define to_isp_xclk(_hw) container_of(_hw, struct isp_xclk, hw) 156 + 157 + static void isp_xclk_update(struct isp_xclk *xclk, u32 divider) 158 + { 159 + switch (xclk->id) { 160 + case ISP_XCLK_A: 161 + isp_reg_clr_set(xclk->isp, OMAP3_ISP_IOMEM_MAIN, ISP_TCTRL_CTRL, 162 + ISPTCTRL_CTRL_DIVA_MASK, 163 + divider << ISPTCTRL_CTRL_DIVA_SHIFT); 164 + break; 165 + case ISP_XCLK_B: 166 + isp_reg_clr_set(xclk->isp, OMAP3_ISP_IOMEM_MAIN, ISP_TCTRL_CTRL, 167 + ISPTCTRL_CTRL_DIVB_MASK, 168 + divider << ISPTCTRL_CTRL_DIVB_SHIFT); 169 + break; 170 + } 171 + } 172 + 173 + static int isp_xclk_prepare(struct clk_hw *hw) 174 + { 175 + struct isp_xclk *xclk = to_isp_xclk(hw); 176 + 177 + omap3isp_get(xclk->isp); 178 + 179 + return 0; 180 + } 181 + 182 + static void isp_xclk_unprepare(struct clk_hw *hw) 183 + { 184 + struct isp_xclk *xclk = to_isp_xclk(hw); 185 + 186 + omap3isp_put(xclk->isp); 187 + } 188 + 189 + static int isp_xclk_enable(struct clk_hw *hw) 190 + { 191 + struct isp_xclk *xclk = to_isp_xclk(hw); 192 + unsigned long flags; 193 + 194 + spin_lock_irqsave(&xclk->lock, flags); 195 + isp_xclk_update(xclk, xclk->divider); 196 + xclk->enabled = true; 197 + spin_unlock_irqrestore(&xclk->lock, flags); 198 + 199 + return 0; 200 + } 201 + 202 + static void isp_xclk_disable(struct clk_hw *hw) 203 + { 204 + struct isp_xclk *xclk = to_isp_xclk(hw); 205 + unsigned long flags; 206 + 207 + spin_lock_irqsave(&xclk->lock, flags); 208 + isp_xclk_update(xclk, 0); 209 + xclk->enabled = false; 210 + spin_unlock_irqrestore(&xclk->lock, flags); 211 + } 212 + 213 + static unsigned long isp_xclk_recalc_rate(struct clk_hw *hw, 214 + unsigned long parent_rate) 215 + { 216 + struct isp_xclk *xclk = to_isp_xclk(hw); 217 + 218 + return parent_rate / xclk->divider; 219 + } 220 + 221 + static u32 isp_xclk_calc_divider(unsigned long *rate, unsigned long parent_rate) 222 + { 223 + u32 divider; 224 + 225 + if (*rate >= parent_rate) { 226 + *rate = parent_rate; 227 + return ISPTCTRL_CTRL_DIV_BYPASS; 228 + } 229 + 230 + divider = DIV_ROUND_CLOSEST(parent_rate, *rate); 231 + if (divider >= ISPTCTRL_CTRL_DIV_BYPASS) 232 + divider = ISPTCTRL_CTRL_DIV_BYPASS - 1; 233 + 234 + *rate = parent_rate / divider; 235 + return divider; 236 + } 237 + 238 + static long isp_xclk_round_rate(struct clk_hw *hw, unsigned long rate, 239 + unsigned long *parent_rate) 240 + { 241 + isp_xclk_calc_divider(&rate, *parent_rate); 242 + return rate; 243 + } 244 + 245 + static int isp_xclk_set_rate(struct clk_hw *hw, unsigned long rate, 246 + unsigned long parent_rate) 247 + { 248 + struct isp_xclk *xclk = to_isp_xclk(hw); 249 + unsigned long flags; 250 + u32 divider; 251 + 252 + divider = isp_xclk_calc_divider(&rate, parent_rate); 253 + 254 + spin_lock_irqsave(&xclk->lock, flags); 255 + 256 + xclk->divider = divider; 257 + if (xclk->enabled) 258 + isp_xclk_update(xclk, divider); 259 + 260 + spin_unlock_irqrestore(&xclk->lock, flags); 261 + 262 + dev_dbg(xclk->isp->dev, "%s: cam_xclk%c set to %lu Hz (div %u)\n", 263 + __func__, xclk->id == ISP_XCLK_A ? 'a' : 'b', rate, divider); 264 + return 0; 265 + } 266 + 267 + static const struct clk_ops isp_xclk_ops = { 268 + .prepare = isp_xclk_prepare, 269 + .unprepare = isp_xclk_unprepare, 270 + .enable = isp_xclk_enable, 271 + .disable = isp_xclk_disable, 272 + .recalc_rate = isp_xclk_recalc_rate, 273 + .round_rate = isp_xclk_round_rate, 274 + .set_rate = isp_xclk_set_rate, 275 + }; 276 + 277 + static const char *isp_xclk_parent_name = "cam_mclk"; 278 + 279 + static const struct clk_init_data isp_xclk_init_data = { 280 + .name = "cam_xclk", 281 + .ops = &isp_xclk_ops, 282 + .parent_names = &isp_xclk_parent_name, 283 + .num_parents = 1, 284 + }; 285 + 286 + static int isp_xclk_init(struct isp_device *isp) 287 + { 288 + struct isp_platform_data *pdata = isp->pdata; 289 + struct clk_init_data init; 290 + unsigned int i; 291 + 292 + for (i = 0; i < ARRAY_SIZE(isp->xclks); ++i) { 293 + struct isp_xclk *xclk = &isp->xclks[i]; 294 + struct clk *clk; 295 + 296 + xclk->isp = isp; 297 + xclk->id = i == 0 ? ISP_XCLK_A : ISP_XCLK_B; 298 + xclk->divider = 1; 299 + spin_lock_init(&xclk->lock); 300 + 301 + init.name = i == 0 ? "cam_xclka" : "cam_xclkb"; 302 + init.ops = &isp_xclk_ops; 303 + init.parent_names = &isp_xclk_parent_name; 304 + init.num_parents = 1; 305 + 306 + xclk->hw.init = &init; 307 + 308 + clk = devm_clk_register(isp->dev, &xclk->hw); 309 + if (IS_ERR(clk)) 310 + return PTR_ERR(clk); 311 + 312 + if (pdata->xclks[i].con_id == NULL && 313 + pdata->xclks[i].dev_id == NULL) 314 + continue; 315 + 316 + xclk->lookup = kzalloc(sizeof(*xclk->lookup), GFP_KERNEL); 317 + if (xclk->lookup == NULL) 318 + return -ENOMEM; 319 + 320 + xclk->lookup->con_id = pdata->xclks[i].con_id; 321 + xclk->lookup->dev_id = pdata->xclks[i].dev_id; 322 + xclk->lookup->clk = clk; 323 + 324 + clkdev_add(xclk->lookup); 325 + } 326 + 327 + return 0; 328 + } 329 + 330 + static void isp_xclk_cleanup(struct isp_device *isp) 331 + { 332 + unsigned int i; 333 + 334 + for (i = 0; i < ARRAY_SIZE(isp->xclks); ++i) { 335 + struct isp_xclk *xclk = &isp->xclks[i]; 336 + 337 + if (xclk->lookup) 338 + clkdev_drop(xclk->lookup); 339 + } 340 + } 341 + 342 + /* ----------------------------------------------------------------------------- 343 + * Interrupts 344 + */ 345 + 152 346 /* 153 347 * isp_enable_interrupts - Enable ISP interrupts. 154 348 * @isp: OMAP3 ISP device ··· 374 178 static void isp_disable_interrupts(struct isp_device *isp) 375 179 { 376 180 isp_reg_writel(isp, 0, OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0ENABLE); 377 - } 378 - 379 - /** 380 - * isp_set_xclk - Configures the specified cam_xclk to the desired frequency. 381 - * @isp: OMAP3 ISP device 382 - * @xclk: Desired frequency of the clock in Hz. 0 = stable low, 1 is stable high 383 - * @xclksel: XCLK to configure (0 = A, 1 = B). 384 - * 385 - * Configures the specified MCLK divisor in the ISP timing control register 386 - * (TCTRL_CTRL) to generate the desired xclk clock value. 387 - * 388 - * Divisor = cam_mclk_hz / xclk 389 - * 390 - * Returns the final frequency that is actually being generated 391 - **/ 392 - static u32 isp_set_xclk(struct isp_device *isp, u32 xclk, u8 xclksel) 393 - { 394 - u32 divisor; 395 - u32 currentxclk; 396 - unsigned long mclk_hz; 397 - 398 - if (!omap3isp_get(isp)) 399 - return 0; 400 - 401 - mclk_hz = clk_get_rate(isp->clock[ISP_CLK_CAM_MCLK]); 402 - 403 - if (xclk >= mclk_hz) { 404 - divisor = ISPTCTRL_CTRL_DIV_BYPASS; 405 - currentxclk = mclk_hz; 406 - } else if (xclk >= 2) { 407 - divisor = mclk_hz / xclk; 408 - if (divisor >= ISPTCTRL_CTRL_DIV_BYPASS) 409 - divisor = ISPTCTRL_CTRL_DIV_BYPASS - 1; 410 - currentxclk = mclk_hz / divisor; 411 - } else { 412 - divisor = xclk; 413 - currentxclk = 0; 414 - } 415 - 416 - switch (xclksel) { 417 - case ISP_XCLK_A: 418 - isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_MAIN, ISP_TCTRL_CTRL, 419 - ISPTCTRL_CTRL_DIVA_MASK, 420 - divisor << ISPTCTRL_CTRL_DIVA_SHIFT); 421 - dev_dbg(isp->dev, "isp_set_xclk(): cam_xclka set to %d Hz\n", 422 - currentxclk); 423 - break; 424 - case ISP_XCLK_B: 425 - isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_MAIN, ISP_TCTRL_CTRL, 426 - ISPTCTRL_CTRL_DIVB_MASK, 427 - divisor << ISPTCTRL_CTRL_DIVB_SHIFT); 428 - dev_dbg(isp->dev, "isp_set_xclk(): cam_xclkb set to %d Hz\n", 429 - currentxclk); 430 - break; 431 - case ISP_XCLK_NONE: 432 - default: 433 - omap3isp_put(isp); 434 - dev_dbg(isp->dev, "ISP_ERR: isp_set_xclk(): Invalid requested " 435 - "xclk. Must be 0 (A) or 1 (B).\n"); 436 - return -EINVAL; 437 - } 438 - 439 - /* Do we go from stable whatever to clock? */ 440 - if (divisor >= 2 && isp->xclk_divisor[xclksel - 1] < 2) 441 - omap3isp_get(isp); 442 - /* Stopping the clock. */ 443 - else if (divisor < 2 && isp->xclk_divisor[xclksel - 1] >= 2) 444 - omap3isp_put(isp); 445 - 446 - isp->xclk_divisor[xclksel - 1] = divisor; 447 - 448 - omap3isp_put(isp); 449 - 450 - return currentxclk; 451 181 } 452 182 453 183 /* ··· 2091 1969 2092 1970 isp_unregister_entities(isp); 2093 1971 isp_cleanup_modules(isp); 1972 + isp_xclk_cleanup(isp); 2094 1973 2095 1974 __omap3isp_get(isp, false); 2096 1975 iommu_detach_device(isp->domain, &pdev->dev); ··· 2165 2042 } 2166 2043 2167 2044 isp->autoidle = autoidle; 2168 - isp->platform_cb.set_xclk = isp_set_xclk; 2169 2045 2170 2046 mutex_init(&isp->isp_mutex); 2171 2047 spin_lock_init(&isp->stat_lock); ··· 2212 2090 } 2213 2091 2214 2092 ret = isp_reset(isp); 2093 + if (ret < 0) 2094 + goto error_isp; 2095 + 2096 + ret = isp_xclk_init(isp); 2215 2097 if (ret < 0) 2216 2098 goto error_isp; 2217 2099 ··· 2288 2162 free_domain: 2289 2163 iommu_domain_free(isp->domain); 2290 2164 error_isp: 2165 + isp_xclk_cleanup(isp); 2291 2166 omap3isp_put(isp); 2292 2167 error: 2293 2168 platform_set_drvdata(pdev, NULL);
+17 -5
drivers/media/platform/omap3isp/isp.h
··· 29 29 30 30 #include <media/omap3isp.h> 31 31 #include <media/v4l2-device.h> 32 + #include <linux/clk-provider.h> 32 33 #include <linux/device.h> 33 34 #include <linux/io.h> 34 35 #include <linux/iommu.h> ··· 126 125 u32 val; 127 126 }; 128 127 129 - struct isp_platform_callback { 130 - u32 (*set_xclk)(struct isp_device *isp, u32 xclk, u8 xclksel); 128 + enum isp_xclk_id { 129 + ISP_XCLK_A, 130 + ISP_XCLK_B, 131 + }; 132 + 133 + struct isp_xclk { 134 + struct isp_device *isp; 135 + struct clk_hw hw; 136 + struct clk_lookup *lookup; 137 + enum isp_xclk_id id; 138 + 139 + spinlock_t lock; /* Protects enabled and divider */ 140 + bool enabled; 141 + unsigned int divider; 131 142 }; 132 143 133 144 /* ··· 162 149 * @cam_mclk: Pointer to camera functional clock structure. 163 150 * @csi2_fck: Pointer to camera CSI2 complexIO clock structure. 164 151 * @l3_ick: Pointer to OMAP3 L3 bus interface clock. 152 + * @xclks: External clocks provided by the ISP 165 153 * @irq: Currently attached ISP ISR callbacks information structure. 166 154 * @isp_af: Pointer to current settings for ISP AutoFocus SCM. 167 155 * @isp_hist: Pointer to current settings for ISP Histogram SCM. ··· 199 185 int has_context; 200 186 int ref_count; 201 187 unsigned int autoidle; 202 - u32 xclk_divisor[2]; /* Two clocks, a and b. */ 203 188 #define ISP_CLK_CAM_ICK 0 204 189 #define ISP_CLK_CAM_MCLK 1 205 190 #define ISP_CLK_CSI2_FCK 2 206 191 #define ISP_CLK_L3_ICK 3 207 192 struct clk *clock[4]; 193 + struct isp_xclk xclks[2]; 208 194 209 195 /* ISP modules */ 210 196 struct ispstat isp_af; ··· 223 209 unsigned int subclk_resources; 224 210 225 211 struct iommu_domain *domain; 226 - 227 - struct isp_platform_callback platform_cb; 228 212 }; 229 213 230 214 #define v4l2_dev_to_isp_device(dev) \
+6 -4
include/media/omap3isp.h
··· 29 29 struct i2c_board_info; 30 30 struct isp_device; 31 31 32 - #define ISP_XCLK_NONE 0 33 - #define ISP_XCLK_A 1 34 - #define ISP_XCLK_B 2 35 - 36 32 enum isp_interface_type { 37 33 ISP_INTERFACE_PARALLEL, 38 34 ISP_INTERFACE_CSI2A_PHY2, ··· 149 153 } bus; /* gcc < 4.6.0 chokes on anonymous union initializers */ 150 154 }; 151 155 156 + struct isp_platform_xclk { 157 + const char *dev_id; 158 + const char *con_id; 159 + }; 160 + 152 161 struct isp_platform_data { 162 + struct isp_platform_xclk xclks[2]; 153 163 struct isp_v4l2_subdevs_group *subdevs; 154 164 void (*set_constraints)(struct isp_device *isp, bool enable); 155 165 };