Linux Audio

Check our new training course

Loading...
Note: File does not exist in v5.4.
  1// SPDX-License-Identifier: GPL-2.0+
  2/*
  3 * Copyright (C) 2023 Loongson Technology Corporation Limited
  4 */
  5
  6#include <linux/delay.h>
  7
  8#include <drm/drm_file.h>
  9#include <drm/drm_managed.h>
 10#include <drm/drm_print.h>
 11
 12#include "lsdc_drv.h"
 13
 14/*
 15 * GFX PLL is the PLL used by DC, GMC and GPU, the structure of the GFX PLL
 16 * may suffer from change across chip variants.
 17 *
 18 *
 19 *                                            +-------------+  sel_out_dc
 20 *                                       +----| / div_out_0 | _____/ _____ DC
 21 *                                       |    +-------------+
 22 * refclk   +---------+      +-------+   |    +-------------+  sel_out_gmc
 23 * ---+---> | div_ref | ---> | loopc | --+--> | / div_out_1 | _____/ _____ GMC
 24 *    |     +---------+      +-------+   |    +-------------+
 25 *    |          /               *       |    +-------------+  sel_out_gpu
 26 *    |                                  +----| / div_out_2 | _____/ _____ GPU
 27 *    |                                       +-------------+
 28 *    |                                                         ^
 29 *    |                                                         |
 30 *    +--------------------------- bypass ----------------------+
 31 */
 32
 33struct loongson_gfxpll_bitmap {
 34	/* Byte 0 ~ Byte 3 */
 35	unsigned div_out_dc    : 7;  /*  6 : 0    DC output clock divider  */
 36	unsigned div_out_gmc   : 7;  /* 13 : 7    GMC output clock divider */
 37	unsigned div_out_gpu   : 7;  /* 20 : 14   GPU output clock divider */
 38	unsigned loopc         : 9;  /* 29 : 21   clock multiplier         */
 39	unsigned _reserved_1_  : 2;  /* 31 : 30                            */
 40
 41	/* Byte 4 ~ Byte 7 */
 42	unsigned div_ref       : 7;   /* 38 : 32   Input clock divider    */
 43	unsigned locked        : 1;   /* 39        PLL locked indicator   */
 44	unsigned sel_out_dc    : 1;   /* 40        dc output clk enable   */
 45	unsigned sel_out_gmc   : 1;   /* 41        gmc output clk enable  */
 46	unsigned sel_out_gpu   : 1;   /* 42        gpu output clk enable  */
 47	unsigned set_param     : 1;   /* 43        Trigger the update     */
 48	unsigned bypass        : 1;   /* 44                               */
 49	unsigned powerdown     : 1;   /* 45                               */
 50	unsigned _reserved_2_  : 18;  /* 46 : 63   no use                 */
 51};
 52
 53union loongson_gfxpll_reg_bitmap {
 54	struct loongson_gfxpll_bitmap bitmap;
 55	u32 w[2];
 56	u64 d;
 57};
 58
 59static void __gfxpll_rreg(struct loongson_gfxpll *this,
 60			  union loongson_gfxpll_reg_bitmap *reg)
 61{
 62#if defined(CONFIG_64BIT)
 63	reg->d = readq(this->mmio);
 64#else
 65	reg->w[0] = readl(this->mmio);
 66	reg->w[1] = readl(this->mmio + 4);
 67#endif
 68}
 69
 70/* Update new parameters to the hardware */
 71
 72static int loongson_gfxpll_update(struct loongson_gfxpll * const this,
 73				  struct loongson_gfxpll_parms const *pin)
 74{
 75	/* None, TODO */
 76
 77	return 0;
 78}
 79
 80static void loongson_gfxpll_get_rates(struct loongson_gfxpll * const this,
 81				      unsigned int *dc,
 82				      unsigned int *gmc,
 83				      unsigned int *gpu)
 84{
 85	struct loongson_gfxpll_parms *pparms = &this->parms;
 86	union loongson_gfxpll_reg_bitmap gfxpll_reg;
 87	unsigned int pre_output;
 88	unsigned int dc_mhz;
 89	unsigned int gmc_mhz;
 90	unsigned int gpu_mhz;
 91
 92	__gfxpll_rreg(this, &gfxpll_reg);
 93
 94	pparms->div_ref = gfxpll_reg.bitmap.div_ref;
 95	pparms->loopc = gfxpll_reg.bitmap.loopc;
 96
 97	pparms->div_out_dc = gfxpll_reg.bitmap.div_out_dc;
 98	pparms->div_out_gmc = gfxpll_reg.bitmap.div_out_gmc;
 99	pparms->div_out_gpu = gfxpll_reg.bitmap.div_out_gpu;
100
101	pre_output = pparms->ref_clock / pparms->div_ref * pparms->loopc;
102
103	dc_mhz = pre_output / pparms->div_out_dc / 1000;
104	gmc_mhz = pre_output / pparms->div_out_gmc / 1000;
105	gpu_mhz = pre_output / pparms->div_out_gpu / 1000;
106
107	if (dc)
108		*dc = dc_mhz;
109
110	if (gmc)
111		*gmc = gmc_mhz;
112
113	if (gpu)
114		*gpu = gpu_mhz;
115}
116
117static void loongson_gfxpll_print(struct loongson_gfxpll * const this,
118				  struct drm_printer *p,
119				  bool verbose)
120{
121	struct loongson_gfxpll_parms *parms = &this->parms;
122	unsigned int dc, gmc, gpu;
123
124	if (verbose) {
125		drm_printf(p, "reference clock: %u\n", parms->ref_clock);
126		drm_printf(p, "div_ref = %u\n", parms->div_ref);
127		drm_printf(p, "loopc = %u\n", parms->loopc);
128
129		drm_printf(p, "div_out_dc = %u\n", parms->div_out_dc);
130		drm_printf(p, "div_out_gmc = %u\n", parms->div_out_gmc);
131		drm_printf(p, "div_out_gpu = %u\n", parms->div_out_gpu);
132	}
133
134	this->funcs->get_rates(this, &dc, &gmc, &gpu);
135
136	drm_printf(p, "dc: %uMHz, gmc: %uMHz, gpu: %uMHz\n", dc, gmc, gpu);
137}
138
139/* GFX (DC, GPU, GMC) PLL initialization and destroy function */
140
141static void loongson_gfxpll_fini(struct drm_device *ddev, void *data)
142{
143	struct loongson_gfxpll *this = (struct loongson_gfxpll *)data;
144
145	iounmap(this->mmio);
146
147	kfree(this);
148}
149
150static int loongson_gfxpll_init(struct loongson_gfxpll * const this)
151{
152	struct loongson_gfxpll_parms *pparms = &this->parms;
153	struct drm_printer printer = drm_info_printer(this->ddev->dev);
154
155	pparms->ref_clock = LSDC_PLL_REF_CLK_KHZ;
156
157	this->mmio = ioremap(this->reg_base, this->reg_size);
158	if (IS_ERR_OR_NULL(this->mmio))
159		return -ENOMEM;
160
161	this->funcs->print(this, &printer, false);
162
163	return 0;
164}
165
166static const struct loongson_gfxpll_funcs lsdc_gmc_gpu_funcs = {
167	.init = loongson_gfxpll_init,
168	.update = loongson_gfxpll_update,
169	.get_rates = loongson_gfxpll_get_rates,
170	.print = loongson_gfxpll_print,
171};
172
173int loongson_gfxpll_create(struct drm_device *ddev,
174			   struct loongson_gfxpll **ppout)
175{
176	struct lsdc_device *ldev = to_lsdc(ddev);
177	const struct loongson_gfx_desc *gfx = to_loongson_gfx(ldev->descp);
178	struct loongson_gfxpll *this;
179	int ret;
180
181	this = kzalloc(sizeof(*this), GFP_KERNEL);
182	if (IS_ERR_OR_NULL(this))
183		return -ENOMEM;
184
185	this->ddev = ddev;
186	this->reg_size = gfx->gfxpll.reg_size;
187	this->reg_base = gfx->conf_reg_base + gfx->gfxpll.reg_offset;
188	this->funcs = &lsdc_gmc_gpu_funcs;
189
190	ret = this->funcs->init(this);
191	if (unlikely(ret)) {
192		kfree(this);
193		return ret;
194	}
195
196	*ppout = this;
197
198	return drmm_add_action_or_reset(ddev, loongson_gfxpll_fini, this);
199}