brintos

brintos / linux-shallow public Read only

0
0
Text · 7.3 KiB · 0af01a0 Raw
310 lines · c
1/*2 * Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved.3 *4 * Permission is hereby granted, free of charge, to any person obtaining a5 * copy of this software and associated documentation files (the "Software"),6 * to deal in the Software without restriction, including without limitation7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,8 * and/or sell copies of the Software, and to permit persons to whom the9 * Software is furnished to do so, subject to the following conditions:10 *11 * The above copyright notice and this permission notice shall be included in12 * all copies or substantial portions of the Software.13 *14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL17 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER18 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING19 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER20 * DEALINGS IN THE SOFTWARE.21 */22#include <core/device.h>23#include <core/firmware.h>24 25#include <subdev/fb.h>26#include <subdev/mmu.h>27 28int29nvkm_firmware_load_name(const struct nvkm_subdev *subdev, const char *base,30			const char *name, int ver, const struct firmware **pfw)31{32	char path[64];33	int ret;34 35	snprintf(path, sizeof(path), "%s%s", base, name);36	ret = nvkm_firmware_get(subdev, path, ver, pfw);37	if (ret < 0)38		return ret;39 40	return 0;41}42 43int44nvkm_firmware_load_blob(const struct nvkm_subdev *subdev, const char *base,45			const char *name, int ver, struct nvkm_blob *blob)46{47	const struct firmware *fw;48	int ret;49 50	ret = nvkm_firmware_load_name(subdev, base, name, ver, &fw);51	if (ret == 0) {52		blob->data = kmemdup(fw->data, fw->size, GFP_KERNEL);53		blob->size = fw->size;54		nvkm_firmware_put(fw);55		if (!blob->data)56			return -ENOMEM;57	}58 59	return ret;60}61 62/**63 * nvkm_firmware_get - load firmware from the official nvidia/chip/ directory64 * @subdev:	subdevice that will use that firmware65 * @fwname:	name of firmware file to load66 * @ver:	firmware version to load67 * @fw:		firmware structure to load to68 *69 * Use this function to load firmware files in the form nvidia/chip/fwname.bin.70 * Firmware files released by NVIDIA will always follow this format.71 */72int73nvkm_firmware_get(const struct nvkm_subdev *subdev, const char *fwname, int ver,74		  const struct firmware **fw)75{76	struct nvkm_device *device = subdev->device;77	char f[64];78	char cname[16];79	int i;80 81	/* Convert device name to lowercase */82	strscpy(cname, device->chip->name, sizeof(cname));83	i = strlen(cname);84	while (i) {85		--i;86		cname[i] = tolower(cname[i]);87	}88 89	if (ver != 0)90		snprintf(f, sizeof(f), "nvidia/%s/%s-%d.bin", cname, fwname, ver);91	else92		snprintf(f, sizeof(f), "nvidia/%s/%s.bin", cname, fwname);93 94	if (!firmware_request_nowarn(fw, f, device->dev)) {95		nvkm_debug(subdev, "firmware \"%s\" loaded - %zu byte(s)\n",96			   f, (*fw)->size);97		return 0;98	}99 100	nvkm_debug(subdev, "firmware \"%s\" unavailable\n", f);101	return -ENOENT;102}103 104/*105 * nvkm_firmware_put - release firmware loaded with nvkm_firmware_get106 */107void108nvkm_firmware_put(const struct firmware *fw)109{110	release_firmware(fw);111}112 113#define nvkm_firmware_mem(p) container_of((p), struct nvkm_firmware, mem.memory)114 115static struct scatterlist *116nvkm_firmware_mem_sgl(struct nvkm_memory *memory)117{118	struct nvkm_firmware *fw = nvkm_firmware_mem(memory);119 120	switch (fw->func->type) {121	case NVKM_FIRMWARE_IMG_DMA: return &fw->mem.sgl;122	case NVKM_FIRMWARE_IMG_SGT: return  fw->mem.sgt.sgl;123	default:124		WARN_ON(1);125		break;126	}127 128	return NULL;129}130 131static int132nvkm_firmware_mem_map(struct nvkm_memory *memory, u64 offset, struct nvkm_vmm *vmm,133		      struct nvkm_vma *vma, void *argv, u32 argc)134{135	struct nvkm_firmware *fw = nvkm_firmware_mem(memory);136	struct nvkm_vmm_map map = {137		.memory = &fw->mem.memory,138		.offset = offset,139		.sgl = nvkm_firmware_mem_sgl(memory),140	};141 142	if (!map.sgl)143		return -ENOSYS;144 145	return nvkm_vmm_map(vmm, vma, argv, argc, &map);146}147 148static u64149nvkm_firmware_mem_size(struct nvkm_memory *memory)150{151	struct scatterlist *sgl = nvkm_firmware_mem_sgl(memory);152 153	return sgl ? sg_dma_len(sgl) : 0;154}155 156static u64157nvkm_firmware_mem_addr(struct nvkm_memory *memory)158{159	BUG_ON(nvkm_firmware_mem(memory)->func->type != NVKM_FIRMWARE_IMG_DMA);160	return nvkm_firmware_mem(memory)->phys;161}162 163static u8164nvkm_firmware_mem_page(struct nvkm_memory *memory)165{166	return PAGE_SHIFT;167}168 169static enum nvkm_memory_target170nvkm_firmware_mem_target(struct nvkm_memory *memory)171{172	if (nvkm_firmware_mem(memory)->device->func->tegra)173		return NVKM_MEM_TARGET_NCOH;174 175	return NVKM_MEM_TARGET_HOST;176}177 178static void *179nvkm_firmware_mem_dtor(struct nvkm_memory *memory)180{181	return NULL;182}183 184static const struct nvkm_memory_func185nvkm_firmware_mem = {186	.dtor = nvkm_firmware_mem_dtor,187	.target = nvkm_firmware_mem_target,188	.page = nvkm_firmware_mem_page,189	.addr = nvkm_firmware_mem_addr,190	.size = nvkm_firmware_mem_size,191	.map = nvkm_firmware_mem_map,192};193 194void195nvkm_firmware_dtor(struct nvkm_firmware *fw)196{197	struct nvkm_memory *memory = &fw->mem.memory;198 199	if (!fw->img)200		return;201 202	switch (fw->func->type) {203	case NVKM_FIRMWARE_IMG_RAM:204		kfree(fw->img);205		break;206	case NVKM_FIRMWARE_IMG_DMA:207		nvkm_memory_unref(&memory);208		dma_free_noncoherent(fw->device->dev, sg_dma_len(&fw->mem.sgl),209				     fw->img, fw->phys, DMA_TO_DEVICE);210		break;211	case NVKM_FIRMWARE_IMG_SGT:212		nvkm_memory_unref(&memory);213		dma_unmap_sgtable(fw->device->dev, &fw->mem.sgt, DMA_TO_DEVICE, 0);214		sg_free_table(&fw->mem.sgt);215		vfree(fw->img);216		break;217	default:218		WARN_ON(1);219		break;220	}221 222	fw->img = NULL;223}224 225int226nvkm_firmware_ctor(const struct nvkm_firmware_func *func, const char *name,227		   struct nvkm_device *device, const void *src, int len, struct nvkm_firmware *fw)228{229	fw->func = func;230	fw->name = name;231	fw->device = device;232	fw->len = len;233 234	switch (fw->func->type) {235	case NVKM_FIRMWARE_IMG_RAM:236		fw->img = kmemdup(src, fw->len, GFP_KERNEL);237		break;238	case NVKM_FIRMWARE_IMG_DMA: {239		dma_addr_t addr;240		len = ALIGN(fw->len, PAGE_SIZE);241 242		fw->img = dma_alloc_noncoherent(fw->device->dev,243						len, &addr,244						DMA_TO_DEVICE,245						GFP_KERNEL);246		if (fw->img) {247			memcpy(fw->img, src, fw->len);248			fw->phys = addr;249		}250 251		sg_init_one(&fw->mem.sgl, fw->img, len);252		sg_dma_address(&fw->mem.sgl) = fw->phys;253		sg_dma_len(&fw->mem.sgl) = len;254	}255		break;256	case NVKM_FIRMWARE_IMG_SGT:257		len = ALIGN(fw->len, PAGE_SIZE);258 259		fw->img = vmalloc(len);260		if (fw->img) {261			int pages = len >> PAGE_SHIFT;262			int ret = 0;263 264			memcpy(fw->img, src, fw->len);265 266			ret = sg_alloc_table(&fw->mem.sgt, pages, GFP_KERNEL);267			if (ret == 0) {268				struct scatterlist *sgl;269				u8 *data = fw->img;270				int i;271 272				for_each_sgtable_sg(&fw->mem.sgt, sgl, i) {273					struct page *page = vmalloc_to_page(data);274 275					if (!page) {276						ret = -EFAULT;277						break;278					}279 280					sg_set_page(sgl, page, PAGE_SIZE, 0);281					data += PAGE_SIZE;282				}283 284				if (ret == 0) {285					ret = dma_map_sgtable(fw->device->dev, &fw->mem.sgt,286							      DMA_TO_DEVICE, 0);287				}288 289				if (ret)290					sg_free_table(&fw->mem.sgt);291			}292 293			if (ret) {294				vfree(fw->img);295				fw->img = NULL;296			}297		}298		break;299	default:300		WARN_ON(1);301		return -EINVAL;302	}303 304	if (!fw->img)305		return -ENOMEM;306 307	nvkm_memory_ctor(&nvkm_firmware_mem, &fw->mem.memory);308	return 0;309}310