cregit-Linux how code gets into the kernel

Release 4.11 drivers/gpu/drm/mediatek/mtk_hdmi_ddc.c

/*
 * Copyright (c) 2014 MediaTek Inc.
 * Author: Jie Qiu <jie.qiu@mediatek.com>
 *
 * 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.
 */
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/time.h>
#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/err.h>
#include <linux/platform_device.h>
#include <linux/clk.h>
#include <linux/slab.h>
#include <linux/io.h>
#include <linux/iopoll.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/of_platform.h>


#define SIF1_CLOK		(288)

#define DDC_DDCMCTL0		(0x0)

#define DDCM_ODRAIN			BIT(31)

#define DDCM_CLK_DIV_OFFSET		(16)

#define DDCM_CLK_DIV_MASK		(0xfff << 16)

#define DDCM_CS_STATUS			BIT(4)

#define DDCM_SCL_STATE			BIT(3)

#define DDCM_SDA_STATE			BIT(2)

#define DDCM_SM0EN			BIT(1)

#define DDCM_SCL_STRECH			BIT(0)

#define DDC_DDCMCTL1		(0x4)

#define DDCM_ACK_OFFSET			(16)

#define DDCM_ACK_MASK			(0xff << 16)

#define DDCM_PGLEN_OFFSET		(8)

#define DDCM_PGLEN_MASK			(0x7 << 8)

#define DDCM_SIF_MODE_OFFSET		(4)

#define DDCM_SIF_MODE_MASK		(0x7 << 4)

#define DDCM_START			(0x1)

#define DDCM_WRITE_DATA			(0x2)

#define DDCM_STOP			(0x3)

#define DDCM_READ_DATA_NO_ACK		(0x4)

#define DDCM_READ_DATA_ACK		(0x5)

#define DDCM_TRI			BIT(0)

#define DDC_DDCMD0		(0x8)

#define DDCM_DATA3			(0xff << 24)

#define DDCM_DATA2			(0xff << 16)

#define DDCM_DATA1			(0xff << 8)

#define DDCM_DATA0			(0xff << 0)

#define DDC_DDCMD1		(0xc)

#define DDCM_DATA7			(0xff << 24)

#define DDCM_DATA6			(0xff << 16)

#define DDCM_DATA5			(0xff << 8)

#define DDCM_DATA4			(0xff << 0)


struct mtk_hdmi_ddc {
	
struct i2c_adapter adap;
	
struct clk *clk;
	
void __iomem *regs;
};


static inline void sif_set_bit(struct mtk_hdmi_ddc *ddc, unsigned int offset, unsigned int val) { writel(readl(ddc->regs + offset) | val, ddc->regs + offset); }

Contributors

PersonTokensPropCommitsCommitProp
Jie Qiu40100.00%1100.00%
Total40100.00%1100.00%


static inline void sif_clr_bit(struct mtk_hdmi_ddc *ddc, unsigned int offset, unsigned int val) { writel(readl(ddc->regs + offset) & ~val, ddc->regs + offset); }

Contributors

PersonTokensPropCommitsCommitProp
Jie Qiu41100.00%1100.00%
Total41100.00%1100.00%


static inline bool sif_bit_is_set(struct mtk_hdmi_ddc *ddc, unsigned int offset, unsigned int val) { return (readl(ddc->regs + offset) & val) == val; }

Contributors

PersonTokensPropCommitsCommitProp
Jie Qiu36100.00%1100.00%
Total36100.00%1100.00%


static inline void sif_write_mask(struct mtk_hdmi_ddc *ddc, unsigned int offset, unsigned int mask, unsigned int shift, unsigned int val) { unsigned int tmp; tmp = readl(ddc->regs + offset); tmp &= ~mask; tmp |= (val << shift) & mask; writel(tmp, ddc->regs + offset); }

Contributors

PersonTokensPropCommitsCommitProp
Jie Qiu69100.00%1100.00%
Total69100.00%1100.00%


static inline unsigned int sif_read_mask(struct mtk_hdmi_ddc *ddc, unsigned int offset, unsigned int mask, unsigned int shift) { return (readl(ddc->regs + offset) & mask) >> shift; }

Contributors

PersonTokensPropCommitsCommitProp
Jie Qiu41100.00%1100.00%
Total41100.00%1100.00%


static void ddcm_trigger_mode(struct mtk_hdmi_ddc *ddc, int mode) { u32 val; sif_write_mask(ddc, DDC_DDCMCTL1, DDCM_SIF_MODE_MASK, DDCM_SIF_MODE_OFFSET, mode); sif_set_bit(ddc, DDC_DDCMCTL1, DDCM_TRI); readl_poll_timeout(ddc->regs + DDC_DDCMCTL1, val, (val & DDCM_TRI) != DDCM_TRI, 4, 20000); }

Contributors

PersonTokensPropCommitsCommitProp
Jie Qiu62100.00%1100.00%
Total62100.00%1100.00%


static int mtk_hdmi_ddc_read_msg(struct mtk_hdmi_ddc *ddc, struct i2c_msg *msg) { struct device *dev = ddc->adap.dev.parent; u32 remain_count, ack_count, ack_final, read_count, temp_count; u32 index = 0; u32 ack; int i; ddcm_trigger_mode(ddc, DDCM_START); sif_write_mask(ddc, DDC_DDCMD0, 0xff, 0, (msg->addr << 1) | 0x01); sif_write_mask(ddc, DDC_DDCMCTL1, DDCM_PGLEN_MASK, DDCM_PGLEN_OFFSET, 0x00); ddcm_trigger_mode(ddc, DDCM_WRITE_DATA); ack = sif_read_mask(ddc, DDC_DDCMCTL1, DDCM_ACK_MASK, DDCM_ACK_OFFSET); dev_dbg(dev, "ack = 0x%x\n", ack); if (ack != 0x01) { dev_err(dev, "i2c ack err!\n"); return -ENXIO; } remain_count = msg->len; ack_count = (msg->len - 1) / 8; ack_final = 0; while (remain_count > 0) { if (ack_count > 0) { read_count = 8; ack_final = 0; ack_count--; } else { read_count = remain_count; ack_final = 1; } sif_write_mask(ddc, DDC_DDCMCTL1, DDCM_PGLEN_MASK, DDCM_PGLEN_OFFSET, read_count - 1); ddcm_trigger_mode(ddc, (ack_final == 1) ? DDCM_READ_DATA_NO_ACK : DDCM_READ_DATA_ACK); ack = sif_read_mask(ddc, DDC_DDCMCTL1, DDCM_ACK_MASK, DDCM_ACK_OFFSET); temp_count = 0; while (((ack & (1 << temp_count)) != 0) && (temp_count < 8)) temp_count++; if (((ack_final == 1) && (temp_count != (read_count - 1))) || ((ack_final == 0) && (temp_count != read_count))) { dev_err(dev, "Address NACK! ACK(0x%x)\n", ack); break; } for (i = read_count; i >= 1; i--) { int shift; int offset; if (i > 4) { offset = DDC_DDCMD1; shift = (i - 5) * 8; } else { offset = DDC_DDCMD0; shift = (i - 1) * 8; } msg->buf[index + i - 1] = sif_read_mask(ddc, offset, 0xff << shift, shift); } remain_count -= read_count; index += read_count; } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Jie Qiu413100.00%1100.00%
Total413100.00%1100.00%


static int mtk_hdmi_ddc_write_msg(struct mtk_hdmi_ddc *ddc, struct i2c_msg *msg) { struct device *dev = ddc->adap.dev.parent; u32 ack; ddcm_trigger_mode(ddc, DDCM_START); sif_write_mask(ddc, DDC_DDCMD0, DDCM_DATA0, 0, msg->addr << 1); sif_write_mask(ddc, DDC_DDCMD0, DDCM_DATA1, 8, msg->buf[0]); sif_write_mask(ddc, DDC_DDCMCTL1, DDCM_PGLEN_MASK, DDCM_PGLEN_OFFSET, 0x1); ddcm_trigger_mode(ddc, DDCM_WRITE_DATA); ack = sif_read_mask(ddc, DDC_DDCMCTL1, DDCM_ACK_MASK, DDCM_ACK_OFFSET); dev_dbg(dev, "ack = %d\n", ack); if (ack != 0x03) { dev_err(dev, "i2c ack err!\n"); return -EIO; } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Jie Qiu138100.00%1100.00%
Total138100.00%1100.00%


static int mtk_hdmi_ddc_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs, int num) { struct mtk_hdmi_ddc *ddc = adapter->algo_data; struct device *dev = adapter->dev.parent; int ret; int i; if (!ddc) { dev_err(dev, "invalid arguments\n"); return -EINVAL; } sif_set_bit(ddc, DDC_DDCMCTL0, DDCM_SCL_STRECH); sif_set_bit(ddc, DDC_DDCMCTL0, DDCM_SM0EN); sif_clr_bit(ddc, DDC_DDCMCTL0, DDCM_ODRAIN); if (sif_bit_is_set(ddc, DDC_DDCMCTL1, DDCM_TRI)) { dev_err(dev, "ddc line is busy!\n"); return -EBUSY; } sif_write_mask(ddc, DDC_DDCMCTL0, DDCM_CLK_DIV_MASK, DDCM_CLK_DIV_OFFSET, SIF1_CLOK); for (i = 0; i < num; i++) { struct i2c_msg *msg = &msgs[i]; dev_dbg(dev, "i2c msg, adr:0x%x, flags:%d, len :0x%x\n", msg->addr, msg->flags, msg->len); if (msg->flags & I2C_M_RD) ret = mtk_hdmi_ddc_read_msg(ddc, msg); else ret = mtk_hdmi_ddc_write_msg(ddc, msg); if (ret < 0) goto xfer_end; } ddcm_trigger_mode(ddc, DDCM_STOP); return i; xfer_end: ddcm_trigger_mode(ddc, DDCM_STOP); dev_err(dev, "ddc failed!\n"); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
Jie Qiu237100.00%1100.00%
Total237100.00%1100.00%


static u32 mtk_hdmi_ddc_func(struct i2c_adapter *adapter) { return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; }

Contributors

PersonTokensPropCommitsCommitProp
Jie Qiu16100.00%1100.00%
Total16100.00%1100.00%

static const struct i2c_algorithm mtk_hdmi_ddc_algorithm = { .master_xfer = mtk_hdmi_ddc_xfer, .functionality = mtk_hdmi_ddc_func, };
static int mtk_hdmi_ddc_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct mtk_hdmi_ddc *ddc; struct resource *mem; int ret; ddc = devm_kzalloc(dev, sizeof(struct mtk_hdmi_ddc), GFP_KERNEL); if (!ddc) return -ENOMEM; ddc->clk = devm_clk_get(dev, "ddc-i2c"); if (IS_ERR(ddc->clk)) { dev_err(dev, "get ddc_clk failed: %p ,\n", ddc->clk); return PTR_ERR(ddc->clk); } mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); ddc->regs = devm_ioremap_resource(&pdev->dev, mem); if (IS_ERR(ddc->regs)) return PTR_ERR(ddc->regs); ret = clk_prepare_enable(ddc->clk); if (ret) { dev_err(dev, "enable ddc clk failed!\n"); return ret; } strlcpy(ddc->adap.name, "mediatek-hdmi-ddc", sizeof(ddc->adap.name)); ddc->adap.owner = THIS_MODULE; ddc->adap.class = I2C_CLASS_DDC; ddc->adap.algo = &mtk_hdmi_ddc_algorithm; ddc->adap.retries = 3; ddc->adap.dev.of_node = dev->of_node; ddc->adap.algo_data = ddc; ddc->adap.dev.parent = &pdev->dev; ret = i2c_add_adapter(&ddc->adap); if (ret < 0) { dev_err(dev, "failed to add bus to i2c core\n"); goto err_clk_disable; } platform_set_drvdata(pdev, ddc); dev_dbg(dev, "ddc->adap: %p\n", &ddc->adap); dev_dbg(dev, "ddc->clk: %p\n", ddc->clk); dev_dbg(dev, "physical adr: %pa, end: %pa\n", &mem->start, &mem->end); return 0; err_clk_disable: clk_disable_unprepare(ddc->clk); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
Jie Qiu342100.00%1100.00%
Total342100.00%1100.00%


static int mtk_hdmi_ddc_remove(struct platform_device *pdev) { struct mtk_hdmi_ddc *ddc = platform_get_drvdata(pdev); i2c_del_adapter(&ddc->adap); clk_disable_unprepare(ddc->clk); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Jie Qiu39100.00%1100.00%
Total39100.00%1100.00%

static const struct of_device_id mtk_hdmi_ddc_match[] = { { .compatible = "mediatek,mt8173-hdmi-ddc", }, {}, }; struct platform_driver mtk_hdmi_ddc_driver = { .probe = mtk_hdmi_ddc_probe, .remove = mtk_hdmi_ddc_remove, .driver = { .name = "mediatek-hdmi-ddc", .of_match_table = mtk_hdmi_ddc_match, }, }; MODULE_AUTHOR("Jie Qiu <jie.qiu@mediatek.com>"); MODULE_DESCRIPTION("MediaTek HDMI DDC Driver"); MODULE_LICENSE("GPL v2");

Overall Contributors

PersonTokensPropCommitsCommitProp
Jie Qiu1753100.00%1100.00%
Total1753100.00%1100.00%
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with cregit.