Commit ce4c464b authored by Dave Airlie's avatar Dave Airlie

Merge tag 'topic/crc-pmic-2015-07-23' of git://anongit.freedesktop.org/drm-intel into drm-next

crystalcove pmic support from Shobhit. Patch series has all acks/r-bs from
other mainainers so ok to pull into drm-next. But I'm cc'ing all other
maintainers as fyi and in case they want to pull it into their trees too
to avoid conflicts.

* tag 'topic/crc-pmic-2015-07-23' of git://anongit.freedesktop.org/drm-intel:
  mfd: Add GPIOLIB dependency if INTEL_SOC_PMIC is to be enabled
  drm/i915: Backlight control using CRC PMIC based PWM driver
  drm/i915: Use the CRC gpio for panel enable/disable
  pwm: crc: Add Crystalcove (CRC) PWM driver
  mfd: intel_soc_pmic_core: ADD PWM lookup table for CRC PMIC based PWM
  mfd: intel_soc_pmic_crc: Add PWM cell device for Crystalcove PMIC
  mfd: intel_soc_pmic_core: Add lookup table for Panel Control as GPIO signal
  gpiolib: Add support for removing registered consumer lookup table
parents f60de976 04cbfe68
...@@ -1672,6 +1672,19 @@ void gpiod_add_lookup_table(struct gpiod_lookup_table *table) ...@@ -1672,6 +1672,19 @@ void gpiod_add_lookup_table(struct gpiod_lookup_table *table)
mutex_unlock(&gpio_lookup_lock); mutex_unlock(&gpio_lookup_lock);
} }
/**
* gpiod_remove_lookup_table() - unregister GPIO device consumers
* @table: table of consumers to unregister
*/
void gpiod_remove_lookup_table(struct gpiod_lookup_table *table)
{
mutex_lock(&gpio_lookup_lock);
list_del(&table->list);
mutex_unlock(&gpio_lookup_lock);
}
static struct gpio_desc *of_find_gpio(struct device *dev, const char *con_id, static struct gpio_desc *of_find_gpio(struct device *dev, const char *con_id,
unsigned int idx, unsigned int idx,
enum gpio_lookup_flags *flags) enum gpio_lookup_flags *flags)
......
...@@ -778,6 +778,13 @@ int intel_parse_bios(struct drm_device *dev); ...@@ -778,6 +778,13 @@ int intel_parse_bios(struct drm_device *dev);
#define MIPI_DSI_UNDEFINED_PANEL_ID 0 #define MIPI_DSI_UNDEFINED_PANEL_ID 0
#define MIPI_DSI_GENERIC_PANEL_ID 1 #define MIPI_DSI_GENERIC_PANEL_ID 1
/*
* PMIC vs SoC Backlight support specified in pwm_blc
* field in mipi_config block below.
*/
#define PPS_BLC_PMIC 0
#define PPS_BLC_SOC 1
struct mipi_config { struct mipi_config {
u16 panel_id; u16 panel_id;
......
...@@ -177,6 +177,10 @@ struct intel_panel { ...@@ -177,6 +177,10 @@ struct intel_panel {
bool enabled; bool enabled;
bool combination_mode; /* gen 2/4 only */ bool combination_mode; /* gen 2/4 only */
bool active_low_pwm; bool active_low_pwm;
/* PWM chip */
struct pwm_device *pwm;
struct backlight_device *device; struct backlight_device *device;
} backlight; } backlight;
......
...@@ -31,6 +31,7 @@ ...@@ -31,6 +31,7 @@
#include <drm/drm_panel.h> #include <drm/drm_panel.h>
#include <drm/drm_mipi_dsi.h> #include <drm/drm_mipi_dsi.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/gpio/consumer.h>
#include "i915_drv.h" #include "i915_drv.h"
#include "intel_drv.h" #include "intel_drv.h"
#include "intel_dsi.h" #include "intel_dsi.h"
...@@ -396,6 +397,8 @@ static void intel_dsi_enable(struct intel_encoder *encoder) ...@@ -396,6 +397,8 @@ static void intel_dsi_enable(struct intel_encoder *encoder)
intel_dsi_port_enable(encoder); intel_dsi_port_enable(encoder);
} }
intel_panel_enable_backlight(intel_dsi->attached_connector);
} }
static void intel_dsi_pre_enable(struct intel_encoder *encoder) static void intel_dsi_pre_enable(struct intel_encoder *encoder)
...@@ -410,6 +413,12 @@ static void intel_dsi_pre_enable(struct intel_encoder *encoder) ...@@ -410,6 +413,12 @@ static void intel_dsi_pre_enable(struct intel_encoder *encoder)
DRM_DEBUG_KMS("\n"); DRM_DEBUG_KMS("\n");
/* Panel Enable over CRC PMIC */
if (intel_dsi->gpio_panel)
gpiod_set_value_cansleep(intel_dsi->gpio_panel, 1);
msleep(intel_dsi->panel_on_delay);
/* Disable DPOunit clock gating, can stall pipe /* Disable DPOunit clock gating, can stall pipe
* and we need DPLL REFA always enabled */ * and we need DPLL REFA always enabled */
tmp = I915_READ(DPLL(pipe)); tmp = I915_READ(DPLL(pipe));
...@@ -427,8 +436,6 @@ static void intel_dsi_pre_enable(struct intel_encoder *encoder) ...@@ -427,8 +436,6 @@ static void intel_dsi_pre_enable(struct intel_encoder *encoder)
/* put device in ready state */ /* put device in ready state */
intel_dsi_device_ready(encoder); intel_dsi_device_ready(encoder);
msleep(intel_dsi->panel_on_delay);
drm_panel_prepare(intel_dsi->panel); drm_panel_prepare(intel_dsi->panel);
for_each_dsi_port(port, intel_dsi->ports) for_each_dsi_port(port, intel_dsi->ports)
...@@ -456,6 +463,8 @@ static void intel_dsi_pre_disable(struct intel_encoder *encoder) ...@@ -456,6 +463,8 @@ static void intel_dsi_pre_disable(struct intel_encoder *encoder)
DRM_DEBUG_KMS("\n"); DRM_DEBUG_KMS("\n");
intel_panel_disable_backlight(intel_dsi->attached_connector);
if (is_vid_mode(intel_dsi)) { if (is_vid_mode(intel_dsi)) {
/* Send Shutdown command to the panel in LP mode */ /* Send Shutdown command to the panel in LP mode */
for_each_dsi_port(port, intel_dsi->ports) for_each_dsi_port(port, intel_dsi->ports)
...@@ -571,6 +580,10 @@ static void intel_dsi_post_disable(struct intel_encoder *encoder) ...@@ -571,6 +580,10 @@ static void intel_dsi_post_disable(struct intel_encoder *encoder)
msleep(intel_dsi->panel_off_delay); msleep(intel_dsi->panel_off_delay);
msleep(intel_dsi->panel_pwr_cycle_delay); msleep(intel_dsi->panel_pwr_cycle_delay);
/* Panel Disable over CRC PMIC */
if (intel_dsi->gpio_panel)
gpiod_set_value_cansleep(intel_dsi->gpio_panel, 0);
} }
static bool intel_dsi_get_hw_state(struct intel_encoder *encoder, static bool intel_dsi_get_hw_state(struct intel_encoder *encoder,
...@@ -950,6 +963,11 @@ static void intel_dsi_encoder_destroy(struct drm_encoder *encoder) ...@@ -950,6 +963,11 @@ static void intel_dsi_encoder_destroy(struct drm_encoder *encoder)
/* XXX: Logically this call belongs in the panel driver. */ /* XXX: Logically this call belongs in the panel driver. */
drm_panel_remove(intel_dsi->panel); drm_panel_remove(intel_dsi->panel);
} }
/* dispose of the gpios */
if (intel_dsi->gpio_panel)
gpiod_put(intel_dsi->gpio_panel);
intel_encoder_destroy(encoder); intel_encoder_destroy(encoder);
} }
...@@ -1065,6 +1083,20 @@ void intel_dsi_init(struct drm_device *dev) ...@@ -1065,6 +1083,20 @@ void intel_dsi_init(struct drm_device *dev)
goto err; goto err;
} }
/*
* In case of BYT with CRC PMIC, we need to use GPIO for
* Panel control.
*/
if (dev_priv->vbt.dsi.config->pwm_blc == PPS_BLC_PMIC) {
intel_dsi->gpio_panel =
gpiod_get(dev->dev, "panel", GPIOD_OUT_HIGH);
if (IS_ERR(intel_dsi->gpio_panel)) {
DRM_ERROR("Failed to own gpio for panel control\n");
intel_dsi->gpio_panel = NULL;
}
}
intel_encoder->type = INTEL_OUTPUT_DSI; intel_encoder->type = INTEL_OUTPUT_DSI;
intel_encoder->cloneable = 0; intel_encoder->cloneable = 0;
drm_connector_init(dev, connector, &intel_dsi_connector_funcs, drm_connector_init(dev, connector, &intel_dsi_connector_funcs,
...@@ -1098,6 +1130,7 @@ void intel_dsi_init(struct drm_device *dev) ...@@ -1098,6 +1130,7 @@ void intel_dsi_init(struct drm_device *dev)
} }
intel_panel_init(&intel_connector->panel, fixed_mode, NULL); intel_panel_init(&intel_connector->panel, fixed_mode, NULL);
intel_panel_setup_backlight(connector, INVALID_PIPE);
return; return;
......
...@@ -42,6 +42,9 @@ struct intel_dsi { ...@@ -42,6 +42,9 @@ struct intel_dsi {
struct drm_panel *panel; struct drm_panel *panel;
struct intel_dsi_host *dsi_hosts[I915_MAX_PORTS]; struct intel_dsi_host *dsi_hosts[I915_MAX_PORTS];
/* GPIO Desc for CRC based Panel control */
struct gpio_desc *gpio_panel;
struct intel_connector *attached_connector; struct intel_connector *attached_connector;
/* bit mask of ports being driven */ /* bit mask of ports being driven */
......
...@@ -32,8 +32,11 @@ ...@@ -32,8 +32,11 @@
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/moduleparam.h> #include <linux/moduleparam.h>
#include <linux/pwm.h>
#include "intel_drv.h" #include "intel_drv.h"
#define CRC_PMIC_PWM_PERIOD_NS 21333
void void
intel_fixed_panel_mode(const struct drm_display_mode *fixed_mode, intel_fixed_panel_mode(const struct drm_display_mode *fixed_mode,
struct drm_display_mode *adjusted_mode) struct drm_display_mode *adjusted_mode)
...@@ -544,6 +547,15 @@ static u32 bxt_get_backlight(struct intel_connector *connector) ...@@ -544,6 +547,15 @@ static u32 bxt_get_backlight(struct intel_connector *connector)
return I915_READ(BXT_BLC_PWM_DUTY1); return I915_READ(BXT_BLC_PWM_DUTY1);
} }
static u32 pwm_get_backlight(struct intel_connector *connector)
{
struct intel_panel *panel = &connector->panel;
int duty_ns;
duty_ns = pwm_get_duty_cycle(panel->backlight.pwm);
return DIV_ROUND_UP(duty_ns * 100, CRC_PMIC_PWM_PERIOD_NS);
}
static u32 intel_panel_get_backlight(struct intel_connector *connector) static u32 intel_panel_get_backlight(struct intel_connector *connector)
{ {
struct drm_device *dev = connector->base.dev; struct drm_device *dev = connector->base.dev;
...@@ -632,6 +644,14 @@ static void bxt_set_backlight(struct intel_connector *connector, u32 level) ...@@ -632,6 +644,14 @@ static void bxt_set_backlight(struct intel_connector *connector, u32 level)
I915_WRITE(BXT_BLC_PWM_DUTY1, level); I915_WRITE(BXT_BLC_PWM_DUTY1, level);
} }
static void pwm_set_backlight(struct intel_connector *connector, u32 level)
{
struct intel_panel *panel = &connector->panel;
int duty_ns = DIV_ROUND_UP(level * CRC_PMIC_PWM_PERIOD_NS, 100);
pwm_config(panel->backlight.pwm, duty_ns, CRC_PMIC_PWM_PERIOD_NS);
}
static void static void
intel_panel_actually_set_backlight(struct intel_connector *connector, u32 level) intel_panel_actually_set_backlight(struct intel_connector *connector, u32 level)
{ {
...@@ -769,6 +789,16 @@ static void bxt_disable_backlight(struct intel_connector *connector) ...@@ -769,6 +789,16 @@ static void bxt_disable_backlight(struct intel_connector *connector)
I915_WRITE(BXT_BLC_PWM_CTL1, tmp & ~BXT_BLC_PWM_ENABLE); I915_WRITE(BXT_BLC_PWM_CTL1, tmp & ~BXT_BLC_PWM_ENABLE);
} }
static void pwm_disable_backlight(struct intel_connector *connector)
{
struct intel_panel *panel = &connector->panel;
/* Disable the backlight */
pwm_config(panel->backlight.pwm, 0, CRC_PMIC_PWM_PERIOD_NS);
usleep_range(2000, 3000);
pwm_disable(panel->backlight.pwm);
}
void intel_panel_disable_backlight(struct intel_connector *connector) void intel_panel_disable_backlight(struct intel_connector *connector)
{ {
struct drm_device *dev = connector->base.dev; struct drm_device *dev = connector->base.dev;
...@@ -1010,6 +1040,14 @@ static void bxt_enable_backlight(struct intel_connector *connector) ...@@ -1010,6 +1040,14 @@ static void bxt_enable_backlight(struct intel_connector *connector)
I915_WRITE(BXT_BLC_PWM_CTL1, pwm_ctl | BXT_BLC_PWM_ENABLE); I915_WRITE(BXT_BLC_PWM_CTL1, pwm_ctl | BXT_BLC_PWM_ENABLE);
} }
static void pwm_enable_backlight(struct intel_connector *connector)
{
struct intel_panel *panel = &connector->panel;
pwm_enable(panel->backlight.pwm);
intel_panel_actually_set_backlight(connector, panel->backlight.level);
}
void intel_panel_enable_backlight(struct intel_connector *connector) void intel_panel_enable_backlight(struct intel_connector *connector)
{ {
struct drm_device *dev = connector->base.dev; struct drm_device *dev = connector->base.dev;
...@@ -1386,6 +1424,40 @@ bxt_setup_backlight(struct intel_connector *connector, enum pipe unused) ...@@ -1386,6 +1424,40 @@ bxt_setup_backlight(struct intel_connector *connector, enum pipe unused)
return 0; return 0;
} }
static int pwm_setup_backlight(struct intel_connector *connector,
enum pipe pipe)
{
struct drm_device *dev = connector->base.dev;
struct intel_panel *panel = &connector->panel;
int retval;
/* Get the PWM chip for backlight control */
panel->backlight.pwm = pwm_get(dev->dev, "pwm_backlight");
if (IS_ERR(panel->backlight.pwm)) {
DRM_ERROR("Failed to own the pwm chip\n");
panel->backlight.pwm = NULL;
return -ENODEV;
}
retval = pwm_config(panel->backlight.pwm, CRC_PMIC_PWM_PERIOD_NS,
CRC_PMIC_PWM_PERIOD_NS);
if (retval < 0) {
DRM_ERROR("Failed to configure the pwm chip\n");
pwm_put(panel->backlight.pwm);
panel->backlight.pwm = NULL;
return retval;
}
panel->backlight.min = 0; /* 0% */
panel->backlight.max = 100; /* 100% */
panel->backlight.level = DIV_ROUND_UP(
pwm_get_duty_cycle(panel->backlight.pwm) * 100,
CRC_PMIC_PWM_PERIOD_NS);
panel->backlight.enabled = panel->backlight.level != 0;
return 0;
}
int intel_panel_setup_backlight(struct drm_connector *connector, enum pipe pipe) int intel_panel_setup_backlight(struct drm_connector *connector, enum pipe pipe)
{ {
struct drm_device *dev = connector->dev; struct drm_device *dev = connector->dev;
...@@ -1429,6 +1501,10 @@ void intel_panel_destroy_backlight(struct drm_connector *connector) ...@@ -1429,6 +1501,10 @@ void intel_panel_destroy_backlight(struct drm_connector *connector)
struct intel_connector *intel_connector = to_intel_connector(connector); struct intel_connector *intel_connector = to_intel_connector(connector);
struct intel_panel *panel = &intel_connector->panel; struct intel_panel *panel = &intel_connector->panel;
/* dispose of the pwm */
if (panel->backlight.pwm)
pwm_put(panel->backlight.pwm);
panel->backlight.present = false; panel->backlight.present = false;
} }
...@@ -1456,11 +1532,19 @@ void intel_panel_init_backlight_funcs(struct drm_device *dev) ...@@ -1456,11 +1532,19 @@ void intel_panel_init_backlight_funcs(struct drm_device *dev)
dev_priv->display.set_backlight = pch_set_backlight; dev_priv->display.set_backlight = pch_set_backlight;
dev_priv->display.get_backlight = pch_get_backlight; dev_priv->display.get_backlight = pch_get_backlight;
} else if (IS_VALLEYVIEW(dev)) { } else if (IS_VALLEYVIEW(dev)) {
dev_priv->display.setup_backlight = vlv_setup_backlight; if (dev_priv->vbt.has_mipi) {
dev_priv->display.enable_backlight = vlv_enable_backlight; dev_priv->display.setup_backlight = pwm_setup_backlight;
dev_priv->display.disable_backlight = vlv_disable_backlight; dev_priv->display.enable_backlight = pwm_enable_backlight;
dev_priv->display.set_backlight = vlv_set_backlight; dev_priv->display.disable_backlight = pwm_disable_backlight;
dev_priv->display.get_backlight = vlv_get_backlight; dev_priv->display.set_backlight = pwm_set_backlight;
dev_priv->display.get_backlight = pwm_get_backlight;
} else {
dev_priv->display.setup_backlight = vlv_setup_backlight;
dev_priv->display.enable_backlight = vlv_enable_backlight;
dev_priv->display.disable_backlight = vlv_disable_backlight;
dev_priv->display.set_backlight = vlv_set_backlight;
dev_priv->display.get_backlight = vlv_get_backlight;
}
} else if (IS_GEN4(dev)) { } else if (IS_GEN4(dev)) {
dev_priv->display.setup_backlight = i965_setup_backlight; dev_priv->display.setup_backlight = i965_setup_backlight;
dev_priv->display.enable_backlight = i965_enable_backlight; dev_priv->display.enable_backlight = i965_enable_backlight;
......
...@@ -318,6 +318,7 @@ config LPC_SCH ...@@ -318,6 +318,7 @@ config LPC_SCH
config INTEL_SOC_PMIC config INTEL_SOC_PMIC
bool "Support for Intel Atom SoC PMIC" bool "Support for Intel Atom SoC PMIC"
depends on GPIOLIB
depends on I2C=y depends on I2C=y
select MFD_CORE select MFD_CORE
select REGMAP_I2C select REGMAP_I2C
......
...@@ -24,8 +24,25 @@ ...@@ -24,8 +24,25 @@
#include <linux/acpi.h> #include <linux/acpi.h>
#include <linux/regmap.h> #include <linux/regmap.h>
#include <linux/mfd/intel_soc_pmic.h> #include <linux/mfd/intel_soc_pmic.h>
#include <linux/gpio/machine.h>
#include <linux/pwm.h>
#include "intel_soc_pmic_core.h" #include "intel_soc_pmic_core.h"
/* Lookup table for the Panel Enable/Disable line as GPIO signals */
static struct gpiod_lookup_table panel_gpio_table = {
/* Intel GFX is consumer */
.dev_id = "0000:00:02.0",
.table = {
/* Panel EN/DISABLE */
GPIO_LOOKUP("gpio_crystalcove", 94, "panel", GPIO_ACTIVE_HIGH),
},
};
/* PWM consumed by the Intel GFX */
static struct pwm_lookup crc_pwm_lookup[] = {
PWM_LOOKUP("crystal_cove_pwm", 0, "0000:00:02.0", "pwm_backlight", 0, PWM_POLARITY_NORMAL),
};
static int intel_soc_pmic_find_gpio_irq(struct device *dev) static int intel_soc_pmic_find_gpio_irq(struct device *dev)
{ {
struct gpio_desc *desc; struct gpio_desc *desc;
...@@ -85,6 +102,12 @@ static int intel_soc_pmic_i2c_probe(struct i2c_client *i2c, ...@@ -85,6 +102,12 @@ static int intel_soc_pmic_i2c_probe(struct i2c_client *i2c,
if (ret) if (ret)
dev_warn(dev, "Can't enable IRQ as wake source: %d\n", ret); dev_warn(dev, "Can't enable IRQ as wake source: %d\n", ret);
/* Add lookup table binding for Panel Control to the GPIO Chip */
gpiod_add_lookup_table(&panel_gpio_table);
/* Add lookup table for crc-pwm */
pwm_add_table(crc_pwm_lookup, ARRAY_SIZE(crc_pwm_lookup));
ret = mfd_add_devices(dev, -1, config->cell_dev, ret = mfd_add_devices(dev, -1, config->cell_dev,
config->n_cell_devs, NULL, 0, config->n_cell_devs, NULL, 0,
regmap_irq_get_domain(pmic->irq_chip_data)); regmap_irq_get_domain(pmic->irq_chip_data));
...@@ -104,6 +127,12 @@ static int intel_soc_pmic_i2c_remove(struct i2c_client *i2c) ...@@ -104,6 +127,12 @@ static int intel_soc_pmic_i2c_remove(struct i2c_client *i2c)
regmap_del_irq_chip(pmic->irq, pmic->irq_chip_data); regmap_del_irq_chip(pmic->irq, pmic->irq_chip_data);
/* Remove lookup table for Panel Control from the GPIO Chip */
gpiod_remove_lookup_table(&panel_gpio_table);
/* remove crc-pwm lookup table */
pwm_remove_table(crc_pwm_lookup, ARRAY_SIZE(crc_pwm_lookup));
mfd_remove_devices(&i2c->dev); mfd_remove_devices(&i2c->dev);
return 0; return 0;
......
...@@ -109,6 +109,9 @@ static struct mfd_cell crystal_cove_dev[] = { ...@@ -109,6 +109,9 @@ static struct mfd_cell crystal_cove_dev[] = {
{ {
.name = "crystal_cove_pmic", .name = "crystal_cove_pmic",
}, },
{
.name = "crystal_cove_pwm",
},
}; };
static const struct regmap_config crystal_cove_regmap_config = { static const struct regmap_config crystal_cove_regmap_config = {
......
...@@ -111,6 +111,13 @@ config PWM_CLPS711X ...@@ -111,6 +111,13 @@ config PWM_CLPS711X
To compile this driver as a module, choose M here: the module To compile this driver as a module, choose M here: the module
will be called pwm-clps711x. will be called pwm-clps711x.
config PWM_CRC
bool "Intel Crystalcove (CRC) PWM support"
depends on X86 && INTEL_SOC_PMIC
help
Generic PWM framework driver for Crystalcove (CRC) PMIC based PWM
control.
config PWM_EP93XX config PWM_EP93XX
tristate "Cirrus Logic EP93xx PWM support" tristate "Cirrus Logic EP93xx PWM support"
depends on ARCH_EP93XX depends on ARCH_EP93XX
......
...@@ -8,6 +8,7 @@ obj-$(CONFIG_PWM_BCM_KONA) += pwm-bcm-kona.o ...@@ -8,6 +8,7 @@ obj-$(CONFIG_PWM_BCM_KONA) += pwm-bcm-kona.o
obj-$(CONFIG_PWM_BCM2835) += pwm-bcm2835.o obj-$(CONFIG_PWM_BCM2835) += pwm-bcm2835.o
obj-$(CONFIG_PWM_BFIN) += pwm-bfin.o obj-$(CONFIG_PWM_BFIN) += pwm-bfin.o
obj-$(CONFIG_PWM_CLPS711X) += pwm-clps711x.o obj-$(CONFIG_PWM_CLPS711X) += pwm-clps711x.o
obj-$(CONFIG_PWM_CRC) += pwm-crc.o
obj-$(CONFIG_PWM_EP93XX) += pwm-ep93xx.o obj-$(CONFIG_PWM_EP93XX) += pwm-ep93xx.o
obj-$(CONFIG_PWM_FSL_FTM) += pwm-fsl-ftm.o obj-$(CONFIG_PWM_FSL_FTM) += pwm-fsl-ftm.o
obj-$(CONFIG_PWM_IMG) += pwm-img.o obj-$(CONFIG_PWM_IMG) += pwm-img.o
......
/*
* Copyright (C) 2015 Intel Corporation. All rights reserved.
*
* 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 published by the Free Software Foundation.
*
* 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.
*
* Author: Shobhit Kumar <shobhit.kumar@intel.com>
*/
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/mfd/intel_soc_pmic.h>
#include <linux/pwm.h>
#define PWM0_CLK_DIV 0x4B
#define PWM_OUTPUT_ENABLE BIT(7)
#define PWM_DIV_CLK_0 0x00 /* DIVIDECLK = BASECLK */
#define PWM_DIV_CLK_100 0x63 /* DIVIDECLK = BASECLK/100 */
#define PWM_DIV_CLK_128 0x7F /* DIVIDECLK = BASECLK/128 */
#define PWM0_DUTY_CYCLE 0x4E
#define BACKLIGHT_EN 0x51
#define PWM_MAX_LEVEL 0xFF
#define PWM_BASE_CLK 6000000 /* 6 MHz */
#define PWM_MAX_PERIOD_NS 21333 /* 46.875KHz */
/**
* struct crystalcove_pwm - Crystal Cove PWM controller
* @chip: the abstract pwm_chip structure.
* @regmap: the regmap from the parent device.
*/
struct crystalcove_pwm {
struct pwm_chip chip;
struct regmap *regmap;
};
static inline struct crystalcove_pwm *to_crc_pwm(struct pwm_chip *pc)
{
return container_of(pc, struct crystalcove_pwm, chip);
}
static int crc_pwm_enable(struct pwm_chip *c, struct pwm_device *pwm)
{
struct crystalcove_pwm *crc_pwm = to_crc_pwm(c);
regmap_write(crc_pwm->regmap, BACKLIGHT_EN, 1);
return 0;
}
static void crc_pwm_disable(struct pwm_chip *c, struct pwm_device *pwm)
{
struct crystalcove_pwm *crc_pwm = to_crc_pwm(c);
regmap_write(crc_pwm->regmap, BACKLIGHT_EN, 0);
}
static int crc_pwm_config(struct pwm_chip *c, struct pwm_device *pwm,
int duty_ns, int period_ns)
{
struct crystalcove_pwm *crc_pwm = to_crc_pwm(c);
struct device *dev = crc_pwm->chip.dev;
int level;
if (period_ns > PWM_MAX_PERIOD_NS) {
dev_err(dev, "un-supported period_ns\n");
return -EINVAL;
}
if (pwm->period != period_ns) {
int clk_div;
/* changing the clk divisor, need to disable fisrt */
crc_pwm_disable(c, pwm);
clk_div = PWM_BASE_CLK * period_ns / NSEC_PER_SEC;
regmap_write(crc_pwm->regmap, PWM0_CLK_DIV,
clk_div | PWM_OUTPUT_ENABLE);
/* enable back */
crc_pwm_enable(c, pwm);
}
/* change the pwm duty cycle */
level = duty_ns * PWM_MAX_LEVEL / period_ns;
regmap_write(crc_pwm->regmap, PWM0_DUTY_CYCLE, level);
return 0;
}
static const struct pwm_ops crc_pwm_ops = {
.config = crc_pwm_config,
.enable = crc_pwm_enable,
.disable = crc_pwm_disable,
};
static int crystalcove_pwm_probe(struct platform_device *pdev)
{
struct crystalcove_pwm *pwm;
struct device *dev = pdev->dev.parent;
struct intel_soc_pmic *pmic = dev_get_drvdata(dev);
pwm = devm_kzalloc(&pdev->dev, sizeof(*pwm), GFP_KERNEL);
if (!pwm)
return -ENOMEM;
pwm->chip.dev = &pdev->dev;
pwm->chip.ops = &crc_pwm_ops;
pwm->chip.base = -1;
pwm->chip.npwm = 1;
/* get the PMIC regmap */
pwm->regmap = pmic->regmap;
platform_set_drvdata(pdev, pwm);
return pwmchip_add(&pwm->chip);
}
static int crystalcove_pwm_remove(struct platform_device *pdev)
{
struct crystalcove_pwm *pwm = platform_get_drvdata(pdev);
return pwmchip_remove(&pwm->chip);
}
static struct platform_driver crystalcove_pwm_driver = {
.probe = crystalcove_pwm_probe,
.remove = crystalcove_pwm_remove,
.driver = {
.name = "crystal_cove_pwm",
},
};
builtin_platform_driver(crystalcove_pwm_driver);
...@@ -57,5 +57,6 @@ struct gpiod_lookup_table { ...@@ -57,5 +57,6 @@ struct gpiod_lookup_table {
} }
void gpiod_add_lookup_table(struct gpiod_lookup_table *table); void gpiod_add_lookup_table(struct gpiod_lookup_table *table);
void gpiod_remove_lookup_table(struct gpiod_lookup_table *table);
#endif /* __LINUX_GPIO_MACHINE_H */ #endif /* __LINUX_GPIO_MACHINE_H */
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