Commit d5426f4c authored by Pierre-Louis Bossart's avatar Pierre-Louis Bossart Committed by Mauro Carvalho Chehab

media: staging: atomisp: use clock framework for camera clocks

The Atom ISP driver initializes and configures PMC clocks which are
already handled by the clock framework.

Remove all legacy vlv2_platform_clock stuff and move to the clk API to
avoid conflicts, e.g. with audio machine drivers enabling the MCLK for
external codecs

Fixes: a49d2536 ("staging/atomisp: Add support for the Intel IPU v2")
Tested-by: default avatarCarlo Caione <carlo@endlessm.com>
Reviewed-by: default avatarAndy Shevchenko <andriy.shevchenko@linux.intel.com>
Signed-off-by: default avatarPierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
Signed-off-by: default avatarSakari Ailus <sakari.ailus@linux.intel.com>
Signed-off-by: default avatarMauro Carvalho Chehab <mchehab@s-opensource.com>
parent abe51392
menuconfig INTEL_ATOMISP menuconfig INTEL_ATOMISP
bool "Enable support to Intel MIPI camera drivers" bool "Enable support to Intel MIPI camera drivers"
depends on X86 && EFI && MEDIA_CONTROLLER && PCI && ACPI depends on X86 && EFI && MEDIA_CONTROLLER && PCI && ACPI
select COMMON_CLK
help help
Enable support for the Intel ISP2 camera interfaces and MIPI Enable support for the Intel ISP2 camera interfaces and MIPI
sensor drivers. sensor drivers.
......
...@@ -2,5 +2,4 @@ ...@@ -2,5 +2,4 @@
# Makefile for camera drivers. # Makefile for camera drivers.
# #
obj-$(CONFIG_INTEL_ATOMISP) += clock/
obj-$(CONFIG_INTEL_ATOMISP) += intel-mid/ obj-$(CONFIG_INTEL_ATOMISP) += intel-mid/
#
# Makefile for clock devices.
#
obj-$(CONFIG_INTEL_ATOMISP) += vlv2_plat_clock.o
obj-$(CONFIG_INTEL_ATOMISP) += platform_vlv2_plat_clk.o
/*
* platform_vlv2_plat_clk.c - VLV2 platform clock driver
* Copyright (C) 2013 Intel Corporation
*
* Author: Asutosh Pathak <asutosh.pathak@intel.com>
* Author: Chandra Sekhar Anagani <chandra.sekhar.anagani@intel.com>
* Author: Sergio Aguirre <sergio.a.aguirre.rodriguez@intel.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
*/
#include <linux/device.h>
#include <linux/err.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/printk.h>
static int __init vlv2_plat_clk_init(void)
{
struct platform_device *pdev;
pdev = platform_device_register_simple("vlv2_plat_clk", -1, NULL, 0);
if (IS_ERR(pdev)) {
pr_err("platform_vlv2_plat_clk:register failed: %ld\n",
PTR_ERR(pdev));
return PTR_ERR(pdev);
}
return 0;
}
device_initcall(vlv2_plat_clk_init);
/*
* platform_vlv2_plat_clk.h: platform clock driver library header file
* Copyright (C) 2013 Intel Corporation
*
* Author: Asutosh Pathak <asutosh.pathak@intel.com>
* Author: Chandra Sekhar Anagani <chandra.sekhar.anagani@intel.com>
* Author: Sergio Aguirre <sergio.a.aguirre.rodriguez@intel.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
*/
#ifndef _PLATFORM_VLV2_PLAT_CLK_H_
#define _PLATFORM_VLV2_PLAT_CLK_H_
#include <linux/sfi.h>
extern void __init *vlv2_plat_clk_device_platform_data(
void *info) __attribute__((weak));
#endif
/*
* vlv2_plat_clock.c - VLV2 platform clock driver
* Copyright (C) 2013 Intel Corporation
*
* Author: Asutosh Pathak <asutosh.pathak@intel.com>
* Author: Chandra Sekhar Anagani <chandra.sekhar.anagani@intel.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
#include <linux/err.h>
#include <linux/io.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include "../../include/linux/vlv2_plat_clock.h"
/* NOTE: Most of below constants could come from platform data.
* To be fixed when appropriate ACPI support comes.
*/
#define VLV2_PMC_CLK_BASE_ADDRESS 0xfed03060
#define PLT_CLK_CTL_OFFSET(x) (0x04 * (x))
#define CLK_CONFG_BIT_POS 0
#define CLK_CONFG_BIT_LEN 2
#define CLK_CONFG_D3_GATED 0
#define CLK_CONFG_FORCE_ON 1
#define CLK_CONFG_FORCE_OFF 2
#define CLK_FREQ_TYPE_BIT_POS 2
#define CLK_FREQ_TYPE_BIT_LEN 1
#define CLK_FREQ_TYPE_XTAL 0 /* 25 MHz */
#define CLK_FREQ_TYPE_PLL 1 /* 19.2 MHz */
#define MAX_CLK_COUNT 5
/* Helper macros to manipulate bitfields */
#define REG_MASK(n) (((1 << (n##_BIT_LEN)) - 1) << (n##_BIT_POS))
#define REG_SET_FIELD(r, n, v) (((r) & ~REG_MASK(n)) | \
(((v) << (n##_BIT_POS)) & REG_MASK(n)))
#define REG_GET_FIELD(r, n) (((r) & REG_MASK(n)) >> n##_BIT_POS)
/*
* vlv2 platform has 6 platform clocks, controlled by 4 byte registers
* Total size required for mapping is 6*4 = 24 bytes
*/
#define PMC_MAP_SIZE 24
static DEFINE_MUTEX(clk_mutex);
static void __iomem *pmc_base;
/*
* vlv2_plat_set_clock_freq - Set clock frequency to a specified platform clock
* @clk_num: Platform clock number (i.e. 0, 1, 2, ...,5)
* @freq_type: Clock frequency (0-25 MHz(XTAL), 1-19.2 MHz(PLL) )
*/
int vlv2_plat_set_clock_freq(int clk_num, int freq_type)
{
void __iomem *addr;
if (clk_num < 0 || clk_num >= MAX_CLK_COUNT) {
pr_err("Clock number out of range (%d)\n", clk_num);
return -EINVAL;
}
if (freq_type != CLK_FREQ_TYPE_XTAL &&
freq_type != CLK_FREQ_TYPE_PLL) {
pr_err("wrong clock type\n");
return -EINVAL;
}
if (!pmc_base) {
pr_err("memio map is not set\n");
return -EINVAL;
}
addr = pmc_base + PLT_CLK_CTL_OFFSET(clk_num);
mutex_lock(&clk_mutex);
writel(REG_SET_FIELD(readl(addr), CLK_FREQ_TYPE, freq_type), addr);
mutex_unlock(&clk_mutex);
return 0;
}
EXPORT_SYMBOL_GPL(vlv2_plat_set_clock_freq);
/*
* vlv2_plat_get_clock_freq - Get the status of specified platform clock
* @clk_num: Platform clock number (i.e. 0, 1, 2, ...,5)
*
* Returns 0 for 25 MHz(XTAL) and 1 for 19.2 MHz(PLL)
*/
int vlv2_plat_get_clock_freq(int clk_num)
{
u32 ret;
if (clk_num < 0 || clk_num >= MAX_CLK_COUNT) {
pr_err("Clock number out of range (%d)\n", clk_num);
return -EINVAL;
}
if (!pmc_base) {
pr_err("memio map is not set\n");
return -EINVAL;
}
mutex_lock(&clk_mutex);
ret = REG_GET_FIELD(readl(pmc_base + PLT_CLK_CTL_OFFSET(clk_num)),
CLK_FREQ_TYPE);
mutex_unlock(&clk_mutex);
return ret;
}
EXPORT_SYMBOL_GPL(vlv2_plat_get_clock_freq);
/*
* vlv2_plat_configure_clock - Configure the specified platform clock
* @clk_num: Platform clock number (i.e. 0, 1, 2, ...,5)
* @conf: Clock gating:
* 0 - Clock gated on D3 state
* 1 - Force on
* 2,3 - Force off
*/
int vlv2_plat_configure_clock(int clk_num, u32 conf)
{
void __iomem *addr;
if (clk_num < 0 || clk_num >= MAX_CLK_COUNT) {
pr_err("Clock number out of range (%d)\n", clk_num);
return -EINVAL;
}
if (conf != CLK_CONFG_D3_GATED &&
conf != CLK_CONFG_FORCE_ON &&
conf != CLK_CONFG_FORCE_OFF) {
pr_err("Invalid clock configuration requested\n");
return -EINVAL;
}
if (!pmc_base) {
pr_err("memio map is not set\n");
return -EINVAL;
}
addr = pmc_base + PLT_CLK_CTL_OFFSET(clk_num);
mutex_lock(&clk_mutex);
writel(REG_SET_FIELD(readl(addr), CLK_CONFG, conf), addr);
mutex_unlock(&clk_mutex);
return 0;
}
EXPORT_SYMBOL_GPL(vlv2_plat_configure_clock);
/*
* vlv2_plat_get_clock_status - Get the status of specified platform clock
* @clk_num: Platform clock number (i.e. 0, 1, 2, ...,5)
*
* Returns 1 - On, 0 - Off
*/
int vlv2_plat_get_clock_status(int clk_num)
{
int ret;
if (clk_num < 0 || clk_num >= MAX_CLK_COUNT) {
pr_err("Clock number out of range (%d)\n", clk_num);
return -EINVAL;
}
if (!pmc_base) {
pr_err("memio map is not set\n");
return -EINVAL;
}
mutex_lock(&clk_mutex);
ret = (int)REG_GET_FIELD(readl(pmc_base + PLT_CLK_CTL_OFFSET(clk_num)),
CLK_CONFG);
mutex_unlock(&clk_mutex);
return ret;
}
EXPORT_SYMBOL_GPL(vlv2_plat_get_clock_status);
static int vlv2_plat_clk_probe(struct platform_device *pdev)
{
int i = 0;
pmc_base = ioremap_nocache(VLV2_PMC_CLK_BASE_ADDRESS, PMC_MAP_SIZE);
if (!pmc_base) {
dev_err(&pdev->dev, "I/O memory remapping failed\n");
return -ENOMEM;
}
/* Initialize all clocks as disabled */
for (i = 0; i < MAX_CLK_COUNT; i++)
vlv2_plat_configure_clock(i, CLK_CONFG_FORCE_OFF);
dev_info(&pdev->dev, "vlv2_plat_clk initialized\n");
return 0;
}
static const struct platform_device_id vlv2_plat_clk_id[] = {
{"vlv2_plat_clk", 0},
{}
};
static int vlv2_resume(struct device *device)
{
int i;
/* Initialize all clocks as disabled */
for (i = 0; i < MAX_CLK_COUNT; i++)
vlv2_plat_configure_clock(i, CLK_CONFG_FORCE_OFF);
return 0;
}
static int vlv2_suspend(struct device *device)
{
return 0;
}
static const struct dev_pm_ops vlv2_pm_ops = {
.suspend = vlv2_suspend,
.resume = vlv2_resume,
};
static struct platform_driver vlv2_plat_clk_driver = {
.probe = vlv2_plat_clk_probe,
.id_table = vlv2_plat_clk_id,
.driver = {
.name = "vlv2_plat_clk",
.pm = &vlv2_pm_ops,
},
};
static int __init vlv2_plat_clk_init(void)
{
return platform_driver_register(&vlv2_plat_clk_driver);
}
arch_initcall(vlv2_plat_clk_init);
...@@ -4,10 +4,10 @@ ...@@ -4,10 +4,10 @@
#include <linux/efi.h> #include <linux/efi.h>
#include <linux/pci.h> #include <linux/pci.h>
#include <linux/acpi.h> #include <linux/acpi.h>
#include <linux/clk.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <media/v4l2-subdev.h> #include <media/v4l2-subdev.h>
#include <linux/mfd/intel_soc_pmic.h> #include <linux/mfd/intel_soc_pmic.h>
#include "../../include/linux/vlv2_plat_clock.h"
#include <linux/regulator/consumer.h> #include <linux/regulator/consumer.h>
#include <linux/gpio/consumer.h> #include <linux/gpio/consumer.h>
#include <linux/gpio.h> #include <linux/gpio.h>
...@@ -17,11 +17,7 @@ ...@@ -17,11 +17,7 @@
#define MAX_SUBDEVS 8 #define MAX_SUBDEVS 8
/* Should be defined in vlv2_plat_clock API, isn't: */ #define VLV2_CLK_PLL_19P2MHZ 1 /* XTAL on CHT */
#define VLV2_CLK_PLL_19P2MHZ 1
#define VLV2_CLK_XTAL_19P2MHZ 0
#define VLV2_CLK_ON 1
#define VLV2_CLK_OFF 2
#define ELDO1_SEL_REG 0x19 #define ELDO1_SEL_REG 0x19
#define ELDO1_1P8V 0x16 #define ELDO1_1P8V 0x16
#define ELDO1_CTRL_SHIFT 0x00 #define ELDO1_CTRL_SHIFT 0x00
...@@ -33,6 +29,7 @@ struct gmin_subdev { ...@@ -33,6 +29,7 @@ struct gmin_subdev {
struct v4l2_subdev *subdev; struct v4l2_subdev *subdev;
int clock_num; int clock_num;
int clock_src; int clock_src;
struct clk *pmc_clk;
struct gpio_desc *gpio0; struct gpio_desc *gpio0;
struct gpio_desc *gpio1; struct gpio_desc *gpio1;
struct regulator *v1p8_reg; struct regulator *v1p8_reg;
...@@ -344,6 +341,9 @@ static int gmin_platform_deinit(void) ...@@ -344,6 +341,9 @@ static int gmin_platform_deinit(void)
return 0; return 0;
} }
#define GMIN_PMC_CLK_NAME 14 /* "pmc_plt_clk_[0..5]" */
static char gmin_pmc_clk_name[GMIN_PMC_CLK_NAME];
static struct gmin_subdev *gmin_subdev_add(struct v4l2_subdev *subdev) static struct gmin_subdev *gmin_subdev_add(struct v4l2_subdev *subdev)
{ {
int i, ret; int i, ret;
...@@ -377,6 +377,37 @@ static struct gmin_subdev *gmin_subdev_add(struct v4l2_subdev *subdev) ...@@ -377,6 +377,37 @@ static struct gmin_subdev *gmin_subdev_add(struct v4l2_subdev *subdev)
gmin_subdevs[i].gpio0 = gpiod_get_index(dev, NULL, 0, GPIOD_OUT_LOW); gmin_subdevs[i].gpio0 = gpiod_get_index(dev, NULL, 0, GPIOD_OUT_LOW);
gmin_subdevs[i].gpio1 = gpiod_get_index(dev, NULL, 1, GPIOD_OUT_LOW); gmin_subdevs[i].gpio1 = gpiod_get_index(dev, NULL, 1, GPIOD_OUT_LOW);
/* get PMC clock with clock framework */
snprintf(gmin_pmc_clk_name,
sizeof(gmin_pmc_clk_name),
"%s_%d", "pmc_plt_clk", gmin_subdevs[i].clock_num);
gmin_subdevs[i].pmc_clk = devm_clk_get(dev, gmin_pmc_clk_name);
if (IS_ERR(gmin_subdevs[i].pmc_clk)) {
ret = PTR_ERR(gmin_subdevs[i].pmc_clk);
dev_err(dev,
"Failed to get clk from %s : %d\n",
gmin_pmc_clk_name,
ret);
return NULL;
}
/*
* The firmware might enable the clock at
* boot (this information may or may not
* be reflected in the enable clock register).
* To change the rate we must disable the clock
* first to cover these cases. Due to common
* clock framework restrictions that do not allow
* to disable a clock that has not been enabled,
* we need to enable the clock first.
*/
ret = clk_prepare_enable(gmin_subdevs[i].pmc_clk);
if (!ret)
clk_disable_unprepare(gmin_subdevs[i].pmc_clk);
if (!IS_ERR(gmin_subdevs[i].gpio0)) { if (!IS_ERR(gmin_subdevs[i].gpio0)) {
ret = gpiod_direction_output(gmin_subdevs[i].gpio0, 0); ret = gpiod_direction_output(gmin_subdevs[i].gpio0, 0);
if (ret) if (ret)
...@@ -539,13 +570,21 @@ static int gmin_flisclk_ctrl(struct v4l2_subdev *subdev, int on) ...@@ -539,13 +570,21 @@ static int gmin_flisclk_ctrl(struct v4l2_subdev *subdev, int on)
{ {
int ret = 0; int ret = 0;
struct gmin_subdev *gs = find_gmin_subdev(subdev); struct gmin_subdev *gs = find_gmin_subdev(subdev);
struct i2c_client *client = v4l2_get_subdevdata(subdev);
if (on) {
ret = clk_set_rate(gs->pmc_clk, gs->clock_src);
if (ret)
dev_err(&client->dev, "unable to set PMC rate %d\n",
gs->clock_src);
if (on) ret = clk_prepare_enable(gs->pmc_clk);
ret = vlv2_plat_set_clock_freq(gs->clock_num, gs->clock_src); } else {
if (ret) clk_disable_unprepare(gs->pmc_clk);
return ret; }
return vlv2_plat_configure_clock(gs->clock_num,
on ? VLV2_CLK_ON : VLV2_CLK_OFF); return ret;
} }
static int gmin_csi_cfg(struct v4l2_subdev *sd, int flag) static int gmin_csi_cfg(struct v4l2_subdev *sd, int flag)
......
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