1/*
2 * linux/drivers/media/video/s5p-mfc/s5p_mfc_ctrl.c
3 *
4 * Copyright (c) 2010 Samsung Electronics Co., Ltd.
5 *		http://www.samsung.com/
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 */
12
13#include <linux/delay.h>
14#include <linux/err.h>
15#include <linux/firmware.h>
16#include <linux/jiffies.h>
17#include <linux/sched.h>
18#include "regs-mfc.h"
19#include "s5p_mfc_cmd.h"
20#include "s5p_mfc_common.h"
21#include "s5p_mfc_debug.h"
22#include "s5p_mfc_intr.h"
23#include "s5p_mfc_pm.h"
24
25static void *s5p_mfc_bitproc_buf;
26static size_t s5p_mfc_bitproc_phys;
27static unsigned char *s5p_mfc_bitproc_virt;
28
29/* Allocate and load firmware */
30int s5p_mfc_alloc_and_load_firmware(struct s5p_mfc_dev *dev)
31{
32	struct firmware *fw_blob;
33	size_t bank2_base_phys;
34	void *b_base;
35	int err;
36
37	/* Firmare has to be present as a separate file or compiled
38	 * into kernel. */
39	mfc_debug_enter();
40	err = request_firmware((const struct firmware **)&fw_blob,
41				     "s5p-mfc.fw", dev->v4l2_dev.dev);
42	if (err != 0) {
43		mfc_err("Firmware is not present in the /lib/firmware directory nor compiled in kernel\n");
44		return -EINVAL;
45	}
46	dev->fw_size = ALIGN(fw_blob->size, FIRMWARE_ALIGN);
47	if (s5p_mfc_bitproc_buf) {
48		mfc_err("Attempting to allocate firmware when it seems that it is already loaded\n");
49		release_firmware(fw_blob);
50		return -ENOMEM;
51	}
52	s5p_mfc_bitproc_buf = vb2_dma_contig_memops.alloc(
53		dev->alloc_ctx[MFC_BANK1_ALLOC_CTX], dev->fw_size);
54	if (IS_ERR(s5p_mfc_bitproc_buf)) {
55		s5p_mfc_bitproc_buf = 0;
56		mfc_err("Allocating bitprocessor buffer failed\n");
57		release_firmware(fw_blob);
58		return -ENOMEM;
59	}
60	s5p_mfc_bitproc_phys = s5p_mfc_mem_cookie(
61		dev->alloc_ctx[MFC_BANK1_ALLOC_CTX], s5p_mfc_bitproc_buf);
62	if (s5p_mfc_bitproc_phys & ((1 << MFC_BASE_ALIGN_ORDER) - 1)) {
63		mfc_err("The base memory for bank 1 is not aligned to 128KB\n");
64		vb2_dma_contig_memops.put(s5p_mfc_bitproc_buf);
65		s5p_mfc_bitproc_phys = 0;
66		s5p_mfc_bitproc_buf = 0;
67		release_firmware(fw_blob);
68		return -EIO;
69	}
70	s5p_mfc_bitproc_virt = vb2_dma_contig_memops.vaddr(s5p_mfc_bitproc_buf);
71	if (!s5p_mfc_bitproc_virt) {
72		mfc_err("Bitprocessor memory remap failed\n");
73		vb2_dma_contig_memops.put(s5p_mfc_bitproc_buf);
74		s5p_mfc_bitproc_phys = 0;
75		s5p_mfc_bitproc_buf = 0;
76		release_firmware(fw_blob);
77		return -EIO;
78	}
79	dev->bank1 = s5p_mfc_bitproc_phys;
80	b_base = vb2_dma_contig_memops.alloc(
81		dev->alloc_ctx[MFC_BANK2_ALLOC_CTX], 1 << MFC_BANK2_ALIGN_ORDER);
82	if (IS_ERR(b_base)) {
83		vb2_dma_contig_memops.put(s5p_mfc_bitproc_buf);
84		s5p_mfc_bitproc_phys = 0;
85		s5p_mfc_bitproc_buf = 0;
86		mfc_err("Allocating bank2 base failed\n");
87	release_firmware(fw_blob);
88		return -ENOMEM;
89	}
90	bank2_base_phys = s5p_mfc_mem_cookie(
91		dev->alloc_ctx[MFC_BANK2_ALLOC_CTX], b_base);
92	vb2_dma_contig_memops.put(b_base);
93	if (bank2_base_phys & ((1 << MFC_BASE_ALIGN_ORDER) - 1)) {
94		mfc_err("The base memory for bank 2 is not aligned to 128KB\n");
95		vb2_dma_contig_memops.put(s5p_mfc_bitproc_buf);
96		s5p_mfc_bitproc_phys = 0;
97		s5p_mfc_bitproc_buf = 0;
98		release_firmware(fw_blob);
99		return -EIO;
100	}
101	dev->bank2 = bank2_base_phys;
102	memcpy(s5p_mfc_bitproc_virt, fw_blob->data, fw_blob->size);
103	wmb();
104	release_firmware(fw_blob);
105	mfc_debug_leave();
106	return 0;
107}
108
109/* Reload firmware to MFC */
110int s5p_mfc_reload_firmware(struct s5p_mfc_dev *dev)
111{
112	struct firmware *fw_blob;
113	int err;
114
115	/* Firmare has to be present as a separate file or compiled
116	 * into kernel. */
117	mfc_debug_enter();
118	err = request_firmware((const struct firmware **)&fw_blob,
119				     "s5p-mfc.fw", dev->v4l2_dev.dev);
120	if (err != 0) {
121		mfc_err("Firmware is not present in the /lib/firmware directory nor compiled in kernel\n");
122		return -EINVAL;
123	}
124	if (fw_blob->size > dev->fw_size) {
125		mfc_err("MFC firmware is too big to be loaded\n");
126		release_firmware(fw_blob);
127		return -ENOMEM;
128	}
129	if (s5p_mfc_bitproc_buf == 0 || s5p_mfc_bitproc_phys == 0) {
130		mfc_err("MFC firmware is not allocated or was not mapped correctly\n");
131		release_firmware(fw_blob);
132		return -EINVAL;
133	}
134	memcpy(s5p_mfc_bitproc_virt, fw_blob->data, fw_blob->size);
135	wmb();
136	release_firmware(fw_blob);
137	mfc_debug_leave();
138	return 0;
139}
140
141/* Release firmware memory */
142int s5p_mfc_release_firmware(struct s5p_mfc_dev *dev)
143{
144	/* Before calling this function one has to make sure
145	 * that MFC is no longer processing */
146	if (!s5p_mfc_bitproc_buf)
147		return -EINVAL;
148	vb2_dma_contig_memops.put(s5p_mfc_bitproc_buf);
149	s5p_mfc_bitproc_virt =  0;
150	s5p_mfc_bitproc_phys = 0;
151	s5p_mfc_bitproc_buf = 0;
152	return 0;
153}
154
155/* Reset the device */
156int s5p_mfc_reset(struct s5p_mfc_dev *dev)
157{
158	unsigned int mc_status;
159	unsigned long timeout;
160
161	mfc_debug_enter();
162	/* Stop procedure */
163	/*  reset RISC */
164	mfc_write(dev, 0x3f6, S5P_FIMV_SW_RESET);
165	/*  All reset except for MC */
166	mfc_write(dev, 0x3e2, S5P_FIMV_SW_RESET);
167	mdelay(10);
168
169	timeout = jiffies + msecs_to_jiffies(MFC_BW_TIMEOUT);
170	/* Check MC status */
171	do {
172		if (time_after(jiffies, timeout)) {
173			mfc_err("Timeout while resetting MFC\n");
174			return -EIO;
175		}
176
177		mc_status = mfc_read(dev, S5P_FIMV_MC_STATUS);
178
179	} while (mc_status & 0x3);
180
181	mfc_write(dev, 0x0, S5P_FIMV_SW_RESET);
182	mfc_write(dev, 0x3fe, S5P_FIMV_SW_RESET);
183	mfc_debug_leave();
184	return 0;
185}
186
187static inline void s5p_mfc_init_memctrl(struct s5p_mfc_dev *dev)
188{
189	mfc_write(dev, dev->bank1, S5P_FIMV_MC_DRAMBASE_ADR_A);
190	mfc_write(dev, dev->bank2, S5P_FIMV_MC_DRAMBASE_ADR_B);
191	mfc_debug(2, "Bank1: %08x, Bank2: %08x\n", dev->bank1, dev->bank2);
192}
193
194static inline void s5p_mfc_clear_cmds(struct s5p_mfc_dev *dev)
195{
196	mfc_write(dev, 0xffffffff, S5P_FIMV_SI_CH0_INST_ID);
197	mfc_write(dev, 0xffffffff, S5P_FIMV_SI_CH1_INST_ID);
198	mfc_write(dev, 0, S5P_FIMV_RISC2HOST_CMD);
199	mfc_write(dev, 0, S5P_FIMV_HOST2RISC_CMD);
200}
201
202/* Initialize hardware */
203int s5p_mfc_init_hw(struct s5p_mfc_dev *dev)
204{
205	unsigned int ver;
206	int ret;
207
208	mfc_debug_enter();
209	if (!s5p_mfc_bitproc_buf)
210		return -EINVAL;
211
212	/* 0. MFC reset */
213	mfc_debug(2, "MFC reset..\n");
214	s5p_mfc_clock_on();
215	ret = s5p_mfc_reset(dev);
216	if (ret) {
217		mfc_err("Failed to reset MFC - timeout\n");
218		return ret;
219	}
220	mfc_debug(2, "Done MFC reset..\n");
221	/* 1. Set DRAM base Addr */
222	s5p_mfc_init_memctrl(dev);
223	/* 2. Initialize registers of channel I/F */
224	s5p_mfc_clear_cmds(dev);
225	/* 3. Release reset signal to the RISC */
226	s5p_mfc_clean_dev_int_flags(dev);
227	mfc_write(dev, 0x3ff, S5P_FIMV_SW_RESET);
228	mfc_debug(2, "Will now wait for completion of firmware transfer\n");
229	if (s5p_mfc_wait_for_done_dev(dev, S5P_FIMV_R2H_CMD_FW_STATUS_RET)) {
230		mfc_err("Failed to load firmware\n");
231		s5p_mfc_reset(dev);
232		s5p_mfc_clock_off();
233		return -EIO;
234	}
235	s5p_mfc_clean_dev_int_flags(dev);
236	/* 4. Initialize firmware */
237	ret = s5p_mfc_sys_init_cmd(dev);
238	if (ret) {
239		mfc_err("Failed to send command to MFC - timeout\n");
240		s5p_mfc_reset(dev);
241		s5p_mfc_clock_off();
242		return ret;
243	}
244	mfc_debug(2, "Ok, now will write a command to init the system\n");
245	if (s5p_mfc_wait_for_done_dev(dev, S5P_FIMV_R2H_CMD_SYS_INIT_RET)) {
246		mfc_err("Failed to load firmware\n");
247		s5p_mfc_reset(dev);
248		s5p_mfc_clock_off();
249		return -EIO;
250	}
251	dev->int_cond = 0;
252	if (dev->int_err != 0 || dev->int_type !=
253					S5P_FIMV_R2H_CMD_SYS_INIT_RET) {
254		/* Failure. */
255		mfc_err("Failed to init firmware - error: %d int: %d\n",
256						dev->int_err, dev->int_type);
257		s5p_mfc_reset(dev);
258		s5p_mfc_clock_off();
259		return -EIO;
260	}
261	ver = mfc_read(dev, S5P_FIMV_FW_VERSION);
262	mfc_debug(2, "MFC F/W version : %02xyy, %02xmm, %02xdd\n",
263		(ver >> 16) & 0xFF, (ver >> 8) & 0xFF, ver & 0xFF);
264	s5p_mfc_clock_off();
265	mfc_debug_leave();
266	return 0;
267}
268
269
270int s5p_mfc_sleep(struct s5p_mfc_dev *dev)
271{
272	int ret;
273
274	mfc_debug_enter();
275	s5p_mfc_clock_on();
276	s5p_mfc_clean_dev_int_flags(dev);
277	ret = s5p_mfc_sleep_cmd(dev);
278	if (ret) {
279		mfc_err("Failed to send command to MFC - timeout\n");
280		return ret;
281	}
282	if (s5p_mfc_wait_for_done_dev(dev, S5P_FIMV_R2H_CMD_SLEEP_RET)) {
283		mfc_err("Failed to sleep\n");
284		return -EIO;
285	}
286	s5p_mfc_clock_off();
287	dev->int_cond = 0;
288	if (dev->int_err != 0 || dev->int_type !=
289						S5P_FIMV_R2H_CMD_SLEEP_RET) {
290		/* Failure. */
291		mfc_err("Failed to sleep - error: %d int: %d\n", dev->int_err,
292								dev->int_type);
293		return -EIO;
294	}
295	mfc_debug_leave();
296	return ret;
297}
298
299int s5p_mfc_wakeup(struct s5p_mfc_dev *dev)
300{
301	int ret;
302
303	mfc_debug_enter();
304	/* 0. MFC reset */
305	mfc_debug(2, "MFC reset..\n");
306	s5p_mfc_clock_on();
307	ret = s5p_mfc_reset(dev);
308	if (ret) {
309		mfc_err("Failed to reset MFC - timeout\n");
310		return ret;
311	}
312	mfc_debug(2, "Done MFC reset..\n");
313	/* 1. Set DRAM base Addr */
314	s5p_mfc_init_memctrl(dev);
315	/* 2. Initialize registers of channel I/F */
316	s5p_mfc_clear_cmds(dev);
317	s5p_mfc_clean_dev_int_flags(dev);
318	/* 3. Initialize firmware */
319	ret = s5p_mfc_wakeup_cmd(dev);
320	if (ret) {
321		mfc_err("Failed to send command to MFC - timeout\n");
322		return ret;
323	}
324	/* 4. Release reset signal to the RISC */
325	mfc_write(dev, 0x3ff, S5P_FIMV_SW_RESET);
326	mfc_debug(2, "Ok, now will write a command to wakeup the system\n");
327	if (s5p_mfc_wait_for_done_dev(dev, S5P_FIMV_R2H_CMD_WAKEUP_RET)) {
328		mfc_err("Failed to load firmware\n");
329		return -EIO;
330	}
331	s5p_mfc_clock_off();
332	dev->int_cond = 0;
333	if (dev->int_err != 0 || dev->int_type !=
334						S5P_FIMV_R2H_CMD_WAKEUP_RET) {
335		/* Failure. */
336		mfc_err("Failed to wakeup - error: %d int: %d\n", dev->int_err,
337								dev->int_type);
338		return -EIO;
339	}
340	mfc_debug_leave();
341	return 0;
342}
343
344