| /* |
| * Copyright (C) 2016 Freescale Semiconductor, Inc. |
| * Copyright 2017-2018 NXP |
| * |
| * 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; either version 2 of the License, or (at your |
| * option) any later version. |
| * |
| * 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 <drm/drm_mode.h> |
| #include <linux/io.h> |
| #include <linux/mutex.h> |
| #include <linux/platform_device.h> |
| #include <linux/types.h> |
| #include "dpu-prv.h" |
| |
| #define CLOCKCTRL 0x8 |
| typedef enum { |
| DSPCLKDIVIDE__DIV1, /* Ext disp clk signal has pix clk freq. */ |
| DSPCLKDIVIDE__DIV2, /* Ext disp clk signal has 2x the pix clk freq. */ |
| } clkdivide_t; |
| #define POLARITYCTRL 0xC |
| #define POLHS_HIGH BIT(0) |
| #define POLVS_HIGH BIT(1) |
| #define POLEN_HIGH BIT(2) |
| #define PIXINV_INV BIT(3) |
| #define SRCSELECT 0x10 |
| |
| struct dpu_disengcfg { |
| void __iomem *base; |
| struct mutex mutex; |
| int id; |
| bool inuse; |
| struct dpu_soc *dpu; |
| }; |
| |
| static inline u32 dpu_dec_read(struct dpu_disengcfg *dec, unsigned int offset) |
| { |
| return readl(dec->base + offset); |
| } |
| |
| static inline void dpu_dec_write(struct dpu_disengcfg *dec, u32 value, |
| unsigned int offset) |
| { |
| writel(value, dec->base + offset); |
| } |
| |
| void disengcfg_polarity_ctrl(struct dpu_disengcfg *dec, unsigned int flags) |
| { |
| const struct dpu_devtype *devtype = dec->dpu->devtype; |
| u32 val; |
| |
| mutex_lock(&dec->mutex); |
| val = dpu_dec_read(dec, POLARITYCTRL); |
| if (devtype->pixel_link_nhvsync) { |
| val &= ~POLHS_HIGH; |
| val &= ~POLVS_HIGH; |
| } else { |
| if (flags & DRM_MODE_FLAG_PHSYNC) |
| val |= POLHS_HIGH; |
| if (flags & DRM_MODE_FLAG_NHSYNC) |
| val &= ~POLHS_HIGH; |
| if (flags & DRM_MODE_FLAG_PVSYNC) |
| val |= POLVS_HIGH; |
| if (flags & DRM_MODE_FLAG_NVSYNC) |
| val &= ~POLVS_HIGH; |
| } |
| dpu_dec_write(dec, val, POLARITYCTRL); |
| mutex_unlock(&dec->mutex); |
| } |
| EXPORT_SYMBOL_GPL(disengcfg_polarity_ctrl); |
| |
| struct dpu_disengcfg *dpu_dec_get(struct dpu_soc *dpu, int id) |
| { |
| struct dpu_disengcfg *dec; |
| int i; |
| |
| for (i = 0; i < ARRAY_SIZE(dec_ids); i++) |
| if (dec_ids[i] == id) |
| break; |
| |
| if (i == ARRAY_SIZE(dec_ids)) |
| return ERR_PTR(-EINVAL); |
| |
| dec = dpu->dec_priv[i]; |
| |
| mutex_lock(&dec->mutex); |
| |
| if (dec->inuse) { |
| mutex_unlock(&dec->mutex); |
| return ERR_PTR(-EBUSY); |
| } |
| |
| dec->inuse = true; |
| |
| mutex_unlock(&dec->mutex); |
| |
| return dec; |
| } |
| EXPORT_SYMBOL_GPL(dpu_dec_get); |
| |
| void dpu_dec_put(struct dpu_disengcfg *dec) |
| { |
| mutex_lock(&dec->mutex); |
| |
| dec->inuse = false; |
| |
| mutex_unlock(&dec->mutex); |
| } |
| EXPORT_SYMBOL_GPL(dpu_dec_put); |
| |
| struct dpu_disengcfg *dpu_aux_dec_peek(struct dpu_disengcfg *dec) |
| { |
| return dec->dpu->dec_priv[dec->id ^ 1]; |
| } |
| EXPORT_SYMBOL_GPL(dpu_aux_dec_peek); |
| |
| void _dpu_dec_init(struct dpu_soc *dpu, unsigned int id) |
| { |
| } |
| |
| int dpu_dec_init(struct dpu_soc *dpu, unsigned int id, |
| unsigned long unused, unsigned long base) |
| { |
| struct dpu_disengcfg *dec; |
| |
| dec = devm_kzalloc(dpu->dev, sizeof(*dec), GFP_KERNEL); |
| if (!dec) |
| return -ENOMEM; |
| |
| dpu->dec_priv[id] = dec; |
| |
| dec->base = devm_ioremap(dpu->dev, base, SZ_16); |
| if (!dec->base) |
| return -ENOMEM; |
| |
| dec->dpu = dpu; |
| dec->id = id; |
| mutex_init(&dec->mutex); |
| |
| return 0; |
| } |