// SPDX-License-Identifier: GPL-2.0
/*
* camss-csiphy.c
*
* Qualcomm MSM Camera Subsystem - CSIPHY Module
*
* Copyright (c) 2011-2015, The Linux Foundation. All rights reserved.
* Copyright (C) 2016-2018 Linaro Ltd.
*/
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <media/media-entity.h>
#include <media/v4l2-device.h>
#include <media/v4l2-subdev.h>
#include "camss-csiphy.h"
#include "camss.h"
#define MSM_CSIPHY_NAME "msm_csiphy"
#define CAMSS_CSI_PHY_LNn_CFG2(n) (0x004 + 0x40 * (n))
#define CAMSS_CSI_PHY_LNn_CFG3(n) (0x008 + 0x40 * (n))
#define CAMSS_CSI_PHY_GLBL_RESET 0x140
#define CAMSS_CSI_PHY_GLBL_PWR_CFG 0x144
#define CAMSS_CSI_PHY_GLBL_IRQ_CMD 0x164
#define CAMSS_CSI_PHY_HW_VERSION 0x188
#define CAMSS_CSI_PHY_INTERRUPT_STATUSn(n) (0x18c + 0x4 * (n))
#define CAMSS_CSI_PHY_INTERRUPT_MASKn(n) (0x1ac + 0x4 * (n))
#define CAMSS_CSI_PHY_INTERRUPT_CLEARn(n) (0x1cc + 0x4 * (n))
#define CAMSS_CSI_PHY_GLBL_T_INIT_CFG0 0x1ec
#define CAMSS_CSI_PHY_T_WAKEUP_CFG0 0x1f4
static const struct {
u32 code;
u8 bpp;
} csiphy_formats[] = {
{
MEDIA_BUS_FMT_UYVY8_2X8,
8,
},
{
MEDIA_BUS_FMT_VYUY8_2X8,
8,
},
{
MEDIA_BUS_FMT_YUYV8_2X8,
8,
},
{
MEDIA_BUS_FMT_YVYU8_2X8,
8,
},
{
MEDIA_BUS_FMT_SBGGR8_1X8,
8,
},
{
MEDIA_BUS_FMT_SGBRG8_1X8,
8,
},
{
MEDIA_BUS_FMT_SGRBG8_1X8,
8,
},
{
MEDIA_BUS_FMT_SRGGB8_1X8,
8,
},
{
MEDIA_BUS_FMT_SBGGR10_1X10,
10,
},
{
MEDIA_BUS_FMT_SGBRG10_1X10,
10,
},
{
MEDIA_BUS_FMT_SGRBG10_1X10,
10,
},
{
MEDIA_BUS_FMT_SRGGB10_1X10,
10,
},
{
MEDIA_BUS_FMT_SBGGR12_1X12,
12,
},
{
MEDIA_BUS_FMT_SGBRG12_1X12,
12,
},
{
MEDIA_BUS_FMT_SGRBG12_1X12,
12,
},
{
MEDIA_BUS_FMT_SRGGB12_1X12,
12,
}
};
/*
* csiphy_get_bpp - map media bus format to bits per pixel
* @code: media bus format code
*
* Return number of bits per pixel
*/
static u8 csiphy_get_bpp(u32 code)
{
unsigned int i;
for (i = 0; i < ARRAY_SIZE(csiphy_formats); i++)
if (code == csiphy_formats[i].code)
return csiphy_formats[i].bpp;
WARN(1, "Unknown format\n");
return csiphy_formats[0].bpp;
}
/*
* csiphy_isr - CSIPHY module interrupt handler
* @irq: Interrupt line
* @dev: CSIPHY device
*
* Return IRQ_HANDLED on success
*/
static irqreturn_t csiphy_isr(int irq, void *dev)
{
struct csiphy_device *csiphy = dev;
u8 i;
for (i = 0; i < 8; i++) {
u8 val = readl_relaxed(csiphy->base +
CAMSS_CSI_PHY_INTERRUPT_STATUSn(i));
writel_relaxed(val, csiphy->base +
CAMSS_CSI_PHY_INTERRUPT_CLEARn(i));
writel_relaxed(0x1, csiphy->base + CAMSS_CSI_PHY_GLBL_IRQ_CMD);
writel_relaxed(0x0, csiphy->base + CAMSS_CSI_PHY_GLBL_IRQ_CMD);
writel_relaxed(0x0, csiphy->base +
CAMSS_CSI_PHY_INTERRUPT_CLEARn(i));
}
return IRQ_HANDLED;
}
/*
* csiphy_set_clock_rates - Calculate and set clock rates on CSIPHY module
* @csiphy: CSIPHY device
*/
static int csiphy_set_clock_rates(struct csiphy_device *csiphy)
{
struct device *dev = to_device_index(csiphy, csiphy->id);
u32 pixel_clock;
int i, j;
int ret;
ret = camss_get_pixel_clock(&csiphy->subdev.entity, &pixel_clock);
if (ret)
pixel_clock = 0;
for (i = 0; i < csiphy->nclocks; i++) {
struct camss_clock *clock = &csiphy->clock[i];
if (!strcmp(clock->name, "csiphy0_timer") ||
!strcmp(clock->name, "csiphy1_timer")) {
u8 bpp = csiphy_get_bpp(
csiphy->fmt[MSM_CSIPHY_PAD_SINK].code);
u8 num_lanes = csiphy->cfg.csi2->lane_cfg.num_data;
u64 min_rate = pixel_clock * bpp / (2 * num_lanes * 4);
long round_rate;
camss_add_clock_margin(&min_rate);
for (j = 0; j < clock->nfreqs; j++)
if (min_rate < clock->freq[j])
break;
if (j == clock->nfreqs) {
dev_err(dev,
"Pixel clock is too high for CSIPHY\n");
return -EINVAL;
}
/* if sensor pixel clock is not available */
/* set highest possible CSIPHY clock rate */
if (min_rate == 0)
j = clock->nfreqs - 1;
round_rate = clk_round_rate(clock->clk, clock->freq[j]);
if (round_rate < 0) {
dev_err(dev, "clk round rate failed: %ld\n",
round_rate);
return -EINVAL;
}
csiphy->timer_clk_rate = round_rate;
ret = clk_set_rate(clock->clk, csiphy->timer_clk_rate);
if (ret < 0) {
dev_err(dev, "clk set rate failed: %d\n", ret);
return ret;
}
}
}
return 0;
}
/*
* csiphy_reset - Perform software reset on CSIPHY module
* @csiphy: CSIPHY device
*/
static